diff --git a/.buildkite/pipelines/pull_request/renovate.yml b/.buildkite/pipelines/pull_request/renovate.yml new file mode 100644 index 000000000000..3b441cfe5375 --- /dev/null +++ b/.buildkite/pipelines/pull_request/renovate.yml @@ -0,0 +1,20 @@ +steps: + - command: .buildkite/scripts/lifecycle/pre_build.sh + label: Pre-Build + timeout_in_minutes: 10 + agents: + machineType: n2-standard-2 + + - wait + + - command: .buildkite/scripts/steps/renovate.sh + label: 'Renovate validation' + agents: + machineType: n2-highcpu-8 + preemptible: true + key: renovate_validation + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index aba3da0cb890..b1c877bb3db0 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -39,6 +39,14 @@ const getPipeline = (filename: string, removeSteps = true) => { return; } + const onlyRunQuickChecks = await areChangesSkippable([/^renovate\.json$/], REQUIRED_PATHS); + if (onlyRunQuickChecks) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/renovate.yml', false)); + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/post_build.yml')); + console.log('Isolated changes to renovate.json. Skipping main PR pipeline.'); + return; + } + pipeline.push(getAgentImageConfig({ returnYaml: true })); pipeline.push(getPipeline('.buildkite/pipelines/pull_request/base.yml', false)); @@ -242,7 +250,7 @@ const getPipeline = (filename: string, removeSteps = true) => { /^packages\/kbn-securitysolution-.*/, /^x-pack\/plugins\/alerting/, /^x-pack\/plugins\/data_views\/common/, - /^x-pack\/plugins\/lists/, + /^x-pack\/solutions\/security\/plugins\/lists/, /^x-pack\/plugins\/rule_registry\/common/, /^x-pack\/solutions\/security\/plugins\/security_solution/, /^x-pack\/solutions\/security\/plugins\/security_solution_ess/, @@ -298,10 +306,10 @@ const getPipeline = (filename: string, removeSteps = true) => { /^packages\/kbn-search-types/, /^packages\/kbn-securitysolution-.*/, /^src\/platform\/packages\/shared\/kbn-securitysolution-ecs/, - /^packages\/kbn-securitysolution-io-ts-alerting-types/, - /^packages\/kbn-securitysolution-io-ts-list-types/, - /^packages\/kbn-securitysolution-list-hooks/, - /^packages\/kbn-securitysolution-t-grid/, + /^x-pack\/solutions\/security\/packages\/kbn-securitysolution-io-ts-alerting-types/, + /^x-pack\/solutions\/security\/packages\/kbn-securitysolution-io-ts-list-types/, + /^x-pack\/solutions\/security\/packages\/kbn-securitysolution-list-hooks/, + /^x-pack\/solutions\/security\/packages\/kbn-securitysolution-t-grid/, /^packages\/kbn-ui-theme/, /^packages\/kbn-utility-types/, /^packages\/react/, @@ -327,7 +335,7 @@ const getPipeline = (filename: string, removeSteps = true) => { /^x-pack\/plugins\/cases/, /^x-pack\/plugins\/data_views\/common/, /^x-pack\/solutions\/security\/plugins\/elastic_assistant/, - /^x-pack\/plugins\/lists/, + /^x-pack\/solutions\/security\/plugins\/lists/, /^x-pack\/plugins\/rule_registry\/common/, /^x-pack\/solutions\/security\/plugins\/security_solution/, /^x-pack\/solutions\/security\/plugins\/security_solution_ess/, diff --git a/.buildkite/scripts/steps/code_generation/security_solution_codegen.sh b/.buildkite/scripts/steps/code_generation/security_solution_codegen.sh index db7131d12785..13bd0aaf7189 100755 --- a/.buildkite/scripts/steps/code_generation/security_solution_codegen.sh +++ b/.buildkite/scripts/steps/code_generation/security_solution_codegen.sh @@ -10,13 +10,13 @@ echo -e "\n[Security Solution OpenAPI Code Generation] OpenAPI Common Package\n" (cd packages/kbn-openapi-common && yarn openapi:generate) echo -e "\n[Security Solution OpenAPI Code Generation] Lists Common Package\n" -(cd packages/kbn-securitysolution-lists-common && yarn openapi:generate) +(cd x-pack/solutions/security/packages/kbn-securitysolution-lists-common && yarn openapi:generate) echo -e "\n[Security Solution OpenAPI Code Generation] Exceptions Common Package\n" -(cd packages/kbn-securitysolution-exceptions-common && yarn openapi:generate) +(cd x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common && yarn openapi:generate) echo -e "\n[Security Solution OpenAPI Code Generation] Endpoint Exceptions Common Package\n" -(cd packages/kbn-securitysolution-endpoint-exceptions-common && yarn openapi:generate) +(cd x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common && yarn openapi:generate) echo -e "\n[Security Solution OpenAPI Code Generation] Security Solution Plugin\n" (cd x-pack/solutions/security/plugins/security_solution && yarn openapi:generate) diff --git a/.buildkite/scripts/steps/openapi_bundling/security_solution_openapi_bundling.sh b/.buildkite/scripts/steps/openapi_bundling/security_solution_openapi_bundling.sh index 18d2c963b3c2..4dd23435e351 100755 --- a/.buildkite/scripts/steps/openapi_bundling/security_solution_openapi_bundling.sh +++ b/.buildkite/scripts/steps/openapi_bundling/security_solution_openapi_bundling.sh @@ -16,13 +16,13 @@ echo -e "\n[Security Solution OpenAPI Bundling] Entity Analytics API\n" (cd x-pack/solutions/security/plugins/security_solution && yarn openapi:bundle:entity-analytics) echo -e "\n[Security Solution OpenAPI Bundling] Lists API\n" -(cd packages/kbn-securitysolution-lists-common && yarn openapi:bundle) +(cd x-pack/solutions/security/packages/kbn-securitysolution-lists-common && yarn openapi:bundle) echo -e "\n[Security Solution OpenAPI Bundling] Exceptions API\n" -(cd packages/kbn-securitysolution-exceptions-common && yarn openapi:bundle) +(cd x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common && yarn openapi:bundle) echo -e "\n[Security Solution OpenAPI Bundling] Endpoint Exceptions API\n" -(cd packages/kbn-securitysolution-endpoint-exceptions-common && yarn openapi:bundle) +(cd x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common && yarn openapi:bundle) echo -e "\n[Security Solution OpenAPI Bundling] Endpoint Management API\n" (cd x-pack/solutions/security/plugins/security_solution && yarn openapi:bundle:endpoint-management) diff --git a/.buildkite/scripts/steps/renovate.sh b/.buildkite/scripts/steps/renovate.sh new file mode 100755 index 000000000000..cc4583e3da21 --- /dev/null +++ b/.buildkite/scripts/steps/renovate.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail + +echo '--- Renovate: validation' +.buildkite/scripts/steps/checks/renovate.sh diff --git a/.eslintrc.js b/.eslintrc.js index a04a745fcf0e..93e3dabf3b86 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1343,8 +1343,8 @@ module.exports = { { // front end and common typescript and javascript files only files: [ - 'x-pack/plugins/lists/public/**/*.{js,mjs,ts,tsx}', - 'x-pack/plugins/lists/common/**/*.{js,mjs,ts,tsx}', + 'x-pack/solutions/security/plugins/lists/public/**/*.{js,mjs,ts,tsx}', + 'x-pack/solutions/security/plugins/lists/common/**/*.{js,mjs,ts,tsx}', ], rules: { 'import/no-nodejs-modules': 'error', @@ -1360,14 +1360,20 @@ module.exports = { }, { // typescript for /public and /common - files: ['x-pack/plugins/lists/public/*.{ts,tsx}', 'x-pack/plugins/lists/common/*.{ts,tsx}'], + files: [ + 'x-pack/solutions/security/plugins/lists/public/*.{ts,tsx}', + 'x-pack/solutions/security/plugins/lists/common/*.{ts,tsx}', + ], rules: { '@typescript-eslint/no-for-in-array': 'error', }, }, { // typescript for /public and /common - files: ['x-pack/plugins/lists/public/*.{ts,tsx}', 'x-pack/plugins/lists/common/*.{ts,tsx}'], + files: [ + 'x-pack/solutions/security/plugins/lists/public/*.{ts,tsx}', + 'x-pack/solutions/security/plugins/lists/common/*.{ts,tsx}', + ], plugins: ['react'], env: { jest: true, @@ -1425,7 +1431,7 @@ module.exports = { }, }, { - files: ['x-pack/plugins/lists/public/**/!(*.test).{js,mjs,ts,tsx}'], + files: ['x-pack/solutions/security/plugins/lists/public/**/!(*.test).{js,mjs,ts,tsx}'], plugins: ['react-perf'], rules: { 'react-perf/jsx-no-new-object-as-prop': 'error', @@ -1436,7 +1442,7 @@ module.exports = { }, { // typescript and javascript for front and back - files: ['x-pack/plugins/lists/**/*.{js,mjs,ts,tsx}'], + files: ['x-pack/solutions/security/plugins/lists/**/*.{js,mjs,ts,tsx}'], plugins: ['eslint-plugin-node'], env: { jest: true, @@ -1997,9 +2003,6 @@ module.exports = { }, { files: [ - // logsShared depends on o11y/private plugins, but platform plugins depend on it - 'x-pack/plugins/observability_solution/logs_shared/**', - // TODO @kibana/operations 'scripts/create_observability_rules.js', // is importing "@kbn/observability-alerting-test-data" (observability/private) 'src/cli_setup/**', // is importing "@kbn/interactive-setup-plugin" (platform/private) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d9def6481fc9..05ba8d0ac18a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -454,24 +454,6 @@ packages/kbn-search-index-documents @elastic/search-kibana packages/kbn-search-response-warnings @elastic/kibana-data-discovery packages/kbn-search-types @elastic/kibana-data-discovery packages/kbn-security-hardening @elastic/kibana-security -packages/kbn-securitysolution-autocomplete @elastic/security-detection-engine -packages/kbn-securitysolution-endpoint-exceptions-common @elastic/security-detection-engine -packages/kbn-securitysolution-es-utils @elastic/security-detection-engine -packages/kbn-securitysolution-exception-list-components @elastic/security-detection-engine -packages/kbn-securitysolution-exceptions-common @elastic/security-detection-engine -packages/kbn-securitysolution-hook-utils @elastic/security-detection-engine -packages/kbn-securitysolution-io-ts-alerting-types @elastic/security-detection-engine -packages/kbn-securitysolution-io-ts-list-types @elastic/security-detection-engine -packages/kbn-securitysolution-io-ts-types @elastic/security-detection-engine -packages/kbn-securitysolution-io-ts-utils @elastic/security-detection-engine -packages/kbn-securitysolution-list-api @elastic/security-detection-engine -packages/kbn-securitysolution-list-constants @elastic/security-detection-engine -packages/kbn-securitysolution-list-hooks @elastic/security-detection-engine -packages/kbn-securitysolution-list-utils @elastic/security-detection-engine -packages/kbn-securitysolution-lists-common @elastic/security-detection-engine -packages/kbn-securitysolution-rules @elastic/security-detection-engine -packages/kbn-securitysolution-t-grid @elastic/security-detection-engine -packages/kbn-securitysolution-utils @elastic/security-detection-engine packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-set-map @elastic/kibana-operations packages/kbn-shared-svg @elastic/obs-ux-infra_services-team @@ -615,6 +597,10 @@ src/platform/packages/shared/kbn-management/settings/types @elastic/kibana-manag src/platform/packages/shared/kbn-management/settings/utilities @elastic/kibana-management src/platform/packages/shared/kbn-osquery-io-ts-types @elastic/security-asset-management src/platform/packages/shared/kbn-securitysolution-ecs @elastic/security-threat-hunting-explore +src/platform/packages/shared/kbn-securitysolution-es-utils @elastic/security-detection-engine +src/platform/packages/shared/kbn-securitysolution-io-ts-types @elastic/security-detection-engine +src/platform/packages/shared/kbn-securitysolution-io-ts-utils @elastic/security-detection-engine +src/platform/packages/shared/kbn-securitysolution-rules @elastic/security-detection-engine src/platform/packages/shared/kbn-server-route-repository @elastic/obs-knowledge-team src/platform/packages/shared/kbn-server-route-repository-client @elastic/obs-knowledge-team src/platform/packages/shared/kbn-server-route-repository-utils @elastic/obs-knowledge-team @@ -878,6 +864,7 @@ x-pack/platform/plugins/shared/license_management @elastic/kibana-management x-pack/platform/plugins/shared/ml @elastic/ml-ui x-pack/platform/plugins/shared/observability_solution/observability_ai_assistant @elastic/obs-ai-assistant x-pack/platform/plugins/shared/osquery @elastic/security-defend-workflows +x-pack/platform/plugins/shared/screenshotting @elastic/kibana-reporting-services x-pack/platform/plugins/shared/searchprofiler @elastic/kibana-management x-pack/plugins/actions @elastic/response-ops x-pack/plugins/alerting @elastic/response-ops @@ -909,7 +896,6 @@ x-pack/plugins/graph @elastic/kibana-visualizations x-pack/plugins/index_management @elastic/kibana-management x-pack/plugins/lens @elastic/kibana-visualizations x-pack/plugins/licensing @elastic/kibana-core -x-pack/plugins/lists @elastic/security-detection-engine x-pack/plugins/logstash @elastic/logstash x-pack/plugins/maps @elastic/kibana-presentation x-pack/plugins/monitoring @elastic/stack-monitoring @@ -935,7 +921,6 @@ x-pack/plugins/observability_solution/profiling_data_access @elastic/obs-ux-infr x-pack/plugins/reporting @elastic/appex-sharedux x-pack/plugins/rule_registry @elastic/response-ops @elastic/obs-ux-management-team x-pack/plugins/saved_objects_tagging @elastic/appex-sharedux -x-pack/plugins/screenshotting @elastic/kibana-reporting-services x-pack/plugins/search_assistant @elastic/search-kibana x-pack/plugins/search_connectors @elastic/search-kibana x-pack/plugins/search_homepage @elastic/search-kibana @@ -986,6 +971,20 @@ x-pack/solutions/security/packages/features @elastic/security-threat-hunting-exp x-pack/solutions/security/packages/index-adapter @elastic/security-threat-hunting x-pack/solutions/security/packages/kbn-cloud-security-posture/graph @elastic/kibana-cloud-security-posture x-pack/solutions/security/packages/kbn-cloud-security-posture/public @elastic/kibana-cloud-security-posture +x-pack/solutions/security/packages/kbn-securitysolution-autocomplete @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-hook-utils @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-list-api @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-list-constants @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-list-hooks @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-list-utils @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-lists-common @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-t-grid @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-utils @elastic/security-detection-engine x-pack/solutions/security/packages/navigation @elastic/security-threat-hunting-explore x-pack/solutions/security/packages/side_nav @elastic/security-threat-hunting-explore x-pack/solutions/security/packages/storybook/config @elastic/security-threat-hunting-explore @@ -996,6 +995,7 @@ x-pack/solutions/security/plugins/cloud_security_posture @elastic/kibana-cloud-s x-pack/solutions/security/plugins/ecs_data_quality_dashboard @elastic/security-threat-hunting-explore x-pack/solutions/security/plugins/elastic_assistant @elastic/security-generative-ai x-pack/solutions/security/plugins/kubernetes_security @elastic/kibana-cloud-security-posture +x-pack/solutions/security/plugins/lists @elastic/security-detection-engine x-pack/solutions/security/plugins/security_solution @elastic/security-solution x-pack/solutions/security/plugins/security_solution_ess @elastic/security-solution x-pack/solutions/security/plugins/security_solution_serverless @elastic/security-solution @@ -1832,7 +1832,7 @@ packages/kbn-monaco/src/esql @elastic/kibana-esql #CC# /x-pack/plugins/global_search_providers/ @elastic/kibana-core # AppEx AI Infra -/x-pack/plugins/inference @elastic/appex-ai-infra @elastic/obs-ai-assistant @elastic/security-generative-ai +/x-pack/platform/plugins/shared/inference @elastic/appex-ai-infra @elastic/obs-ai-assistant @elastic/security-generative-ai /x-pack/test/functional_gen_ai/inference @elastic/appex-ai-infra # AppEx Platform Services Security @@ -3354,20 +3354,20 @@ x-pack/solutions/security/packages/kbn-cloud-security-posture/public @elastic/ki x-pack/solutions/security/packages/data-stream-adapter @elastic/security-threat-hunting x-pack/solutions/security/packages/expandable-flyout @elastic/security-threat-hunting-investigations x-pack/solutions/security/packages/index-adapter @elastic/security-threat-hunting -x-pack/solutions/security/packages/kbn-securitysolution-autocomplete @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-hook-utils @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-list-api @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-list-constants @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-list-hooks @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-list-utils @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-lists-common @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-t-grid @elastic/security-detection-engine -x-pack/solutions/security/packages/kbn-securitysolution-utils @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-list-api @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-list-constants @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-list-utils @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-lists-common @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-t-grid @elastic/security-detection-engine +x-pack/solutions/security/x-pack/solutions/security/packages/kbn-securitysolution-utils @elastic/security-detection-engine x-pack/solutions/security/packages/navigation @elastic/security-threat-hunting-explore x-pack/solutions/security/packages/side_nav @elastic/security-threat-hunting-explore x-pack/solutions/security/packages/upselling @elastic/security-threat-hunting-explore @@ -3377,7 +3377,7 @@ x-pack/solutions/security/plugins/cloud_security_posture @elastic/kibana-cloud-s x-pack/solutions/security/plugins/ecs_data_quality_dashboard @elastic/security-threat-hunting-explore x-pack/solutions/security/plugins/elastic_assistant @elastic/security-generative-ai x-pack/solutions/security/plugins/kubernetes_security @elastic/kibana-cloud-security-posture -x-pack/solutions/security/plugins/lists @elastic/security-detection-engine +x-pack/solutions/security/solutions/security/plugins/lists @elastic/security-detection-engine x-pack/solutions/security/solutions/security/plugins/security_solution @elastic/security-solution x-pack/solutions/security/solutions/security/plugins/security_solution_ess @elastic/security-solution x-pack/solutions/security/solutions/security/plugins/security_solution_serverless @elastic/security-solution diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index 8ad946732714..3077ac0f5167 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -78,8 +78,8 @@ paths-ignore: - x-pack/solutions/security/plugins/elastic_assistant/scripts - x-pack/plugins/event_log/scripts - x-pack/plugins/fleet/scripts - - x-pack/plugins/lists/scripts - - x-pack/plugins/lists/server/scripts + - x-pack/solutions/security/plugins/lists/scripts + - x-pack/solutions/security/plugins/lists/server/scripts - x-pack/plugins/observability_solution/*/scripts - x-pack/platform/plugins/shared/osquery/scripts - x-pack/plugins/rule_registry/scripts diff --git a/.i18nrc.json b/.i18nrc.json index 63cbc62d1365..0e167c2b08b5 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -6,7 +6,7 @@ "alertsUIShared": "packages/kbn-alerts-ui-shared/src", "alertingTypes": "packages/kbn-alerting-types", "apmOss": "src/plugins/apm_oss", - "autocomplete": "packages/kbn-securitysolution-autocomplete/src", + "autocomplete": "x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src", "avcBanner": "src/platform/packages/shared/kbn-avc-banner/src", "bfetch": "src/plugins/bfetch", "bfetchError": "packages/kbn-bfetch-error", @@ -25,12 +25,13 @@ "data": "src/plugins/data", "observabilityAlertDetails": "x-pack/solutions/observability/packages/alert_details", "dataViews": "src/plugins/data_views", - "defaultNavigation": [ - "packages/default-nav", - "src/platform/packages/private/default-nav" - ], + "defaultNavigation": ["packages/default-nav", "src/platform/packages/private/default-nav"], "devTools": "src/platform/plugins/shared/dev_tools", - "discover": ["src/plugins/discover", "packages/kbn-discover-utils", "packages/kbn-discover-contextual-components"], + "discover": [ + "src/plugins/discover", + "packages/kbn-discover-utils", + "packages/kbn-discover-contextual-components" + ], "savedSearch": "src/plugins/saved_search", "embeddableApi": "src/plugins/embeddable", "presentationPanel": "src/plugins/presentation_panel", @@ -87,8 +88,8 @@ "kibana-react": "src/plugins/kibana_react", "kibanaOverview": "src/plugins/kibana_overview", "lensFormulaDocs": "packages/kbn-lens-formula-docs", - "lists": "packages/kbn-securitysolution-list-utils/src", - "exceptionList-components": "packages/kbn-securitysolution-exception-list-components/src", + "lists": "x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src", + "exceptionList-components": "x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src", "management": [ "src/platform/plugins/shared/management", "src/platform/packages/private/kbn-management", @@ -120,11 +121,15 @@ "searchResponseWarnings": "packages/kbn-search-response-warnings", "searchTypes": "packages/kbn-search-types", "securitySolutionPackages": [ - "x-pack/solutions/security/packages" - ], - "sharedPlatformPackages": [ - "x-pack/platform/packages/shared/kbn-cloud-security-posture" + "x-pack/solutions/security/packages/data_table", + "x-pack/solutions/security/packages/ecs_data_quality_dashboard", + "x-pack/solutions/security/packages/features", + "x-pack/solutions/security/packages/kbn-cloud-security-posture", + "x-pack/solutions/security/packages/navigation", + "x-pack/solutions/security/packages/side_nav", + "x-pack/solutions/security/packages/upselling" ], + "sharedPlatformPackages": ["x-pack/platform/packages/shared/kbn-cloud-security-posture"], "serverlessPackages": "packages/serverless", "sse": ["src/platform/packages/shared/kbn-sse-utils"], "coloring": "packages/kbn-coloring/src", @@ -140,7 +145,7 @@ "uiActionsExamples": "examples/ui_action_examples", "usageCollection": "src/plugins/usage_collection", "userProfileComponents": "packages/kbn-user-profile-components", - "utils": "packages/kbn-securitysolution-utils/src", + "utils": "x-pack/solutions/security/packages/kbn-securitysolution-utils/src", "visDefaultEditor": "src/plugins/vis_default_editor", "visTypeGauge": "src/plugins/vis_types/gauge", "visTypeHeatmap": "src/plugins/vis_types/heatmap", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index f18bafe5221b..2a44a4d4f793 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -693,7 +693,7 @@ the infrastructure monitoring use-case within Kibana. |The licensing plugin retrieves license data from Elasticsearch at regular configurable intervals. -|{kib-repo}blob/{branch}/x-pack/plugins/lists/README.md[lists] +|{kib-repo}blob/{branch}/x-pack/solutions/security/plugins/lists/README.md[lists] |README.md for developers working on the backend lists on how to get started using the CURL scripts in the scripts folder. @@ -815,7 +815,7 @@ Elastic. |Add tagging capability to saved objects -|{kib-repo}blob/{branch}/x-pack/plugins/screenshotting/README.md[screenshotting] +|{kib-repo}blob/{branch}/x-pack/platform/plugins/shared/screenshotting/README.md[screenshotting] |This plugin provides functionality to take screenshots of the Kibana pages. It uses Chromium and Puppeteer underneath to run the browser in headless mode. diff --git a/oas_docs/scripts/merge_ess_oas.js b/oas_docs/scripts/merge_ess_oas.js index 3d96ad5b5933..df0b6e5a4ac0 100644 --- a/oas_docs/scripts/merge_ess_oas.js +++ b/oas_docs/scripts/merge_ess_oas.js @@ -27,9 +27,9 @@ const { REPO_ROOT } = require('@kbn/repo-info'); // Security solution `${REPO_ROOT}/x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/*.schema.yaml`, - `${REPO_ROOT}/packages/kbn-securitysolution-lists-common/docs/openapi/ess/*.schema.yaml`, - `${REPO_ROOT}/packages/kbn-securitysolution-exceptions-common/docs/openapi/ess/*.schema.yaml`, - `${REPO_ROOT}/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/ess/*.schema.yaml`, + `${REPO_ROOT}/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/docs/openapi/ess/*.schema.yaml`, + `${REPO_ROOT}/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/docs/openapi/ess/*.schema.yaml`, + `${REPO_ROOT}/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/ess/*.schema.yaml`, `${REPO_ROOT}/x-pack/platform/packages/shared/kbn-elastic-assistant-common/docs/openapi/ess/*.schema.yaml`, `${REPO_ROOT}/x-pack/platform/plugins/shared/osquery/docs/openapi/ess/*.schema.yaml`, ], diff --git a/oas_docs/scripts/merge_serverless_oas.js b/oas_docs/scripts/merge_serverless_oas.js index 2f92195502db..343487d8fc4f 100644 --- a/oas_docs/scripts/merge_serverless_oas.js +++ b/oas_docs/scripts/merge_serverless_oas.js @@ -25,9 +25,9 @@ const { REPO_ROOT } = require('@kbn/repo-info'); // Security solution `${REPO_ROOT}/x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/*.schema.yaml`, - `${REPO_ROOT}/packages/kbn-securitysolution-lists-common/docs/openapi/serverless/*.schema.yaml`, - `${REPO_ROOT}/packages/kbn-securitysolution-exceptions-common/docs/openapi/serverless/*.schema.yaml`, - `${REPO_ROOT}/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/serverless/*.schema.yaml`, + `${REPO_ROOT}/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/docs/openapi/serverless/*.schema.yaml`, + `${REPO_ROOT}/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/docs/openapi/serverless/*.schema.yaml`, + `${REPO_ROOT}/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/serverless/*.schema.yaml`, `${REPO_ROOT}/x-pack/platform/packages/shared/kbn-elastic-assistant-common/docs/openapi/serverless/*.schema.yaml`, `${REPO_ROOT}/x-pack/platform/plugins/shared/osquery/docs/openapi/serverless/*.schema.yaml`, ], diff --git a/package.json b/package.json index ee007b076af4..7ac4a95fe563 100644 --- a/package.json +++ b/package.json @@ -619,7 +619,7 @@ "@kbn/license-management-plugin": "link:x-pack/platform/plugins/shared/license_management", "@kbn/licensing-plugin": "link:x-pack/plugins/licensing", "@kbn/links-plugin": "link:src/plugins/links", - "@kbn/lists-plugin": "link:x-pack/plugins/lists", + "@kbn/lists-plugin": "link:x-pack/solutions/security/plugins/lists", "@kbn/llm-tasks-plugin": "link:x-pack/platform/plugins/shared/ai_infra/llm_tasks", "@kbn/locator-examples-plugin": "link:examples/locator_examples", "@kbn/locator-explorer-plugin": "link:examples/locator_explorer", @@ -796,7 +796,7 @@ "@kbn/screenshot-mode-example-plugin": "link:examples/screenshot_mode_example", "@kbn/screenshot-mode-plugin": "link:src/plugins/screenshot_mode", "@kbn/screenshotting-example-plugin": "link:x-pack/examples/screenshotting_example", - "@kbn/screenshotting-plugin": "link:x-pack/plugins/screenshotting", + "@kbn/screenshotting-plugin": "link:x-pack/platform/plugins/shared/screenshotting", "@kbn/screenshotting-server": "link:packages/kbn-screenshotting-server", "@kbn/search-api-keys-components": "link:packages/kbn-search-api-keys-components", "@kbn/search-api-keys-server": "link:packages/kbn-search-api-keys-server", @@ -839,26 +839,26 @@ "@kbn/security-solution-upselling": "link:x-pack/solutions/security/packages/upselling", "@kbn/security-test-endpoints-plugin": "link:x-pack/test/security_functional/plugins/test_endpoints", "@kbn/security-ui-components": "link:x-pack/packages/security/ui_components", - "@kbn/securitysolution-autocomplete": "link:packages/kbn-securitysolution-autocomplete", + "@kbn/securitysolution-autocomplete": "link:x-pack/solutions/security/packages/kbn-securitysolution-autocomplete", "@kbn/securitysolution-data-table": "link:x-pack/solutions/security/packages/data_table", "@kbn/securitysolution-ecs": "link:src/platform/packages/shared/kbn-securitysolution-ecs", - "@kbn/securitysolution-endpoint-exceptions-common": "link:packages/kbn-securitysolution-endpoint-exceptions-common", - "@kbn/securitysolution-es-utils": "link:packages/kbn-securitysolution-es-utils", - "@kbn/securitysolution-exception-list-components": "link:packages/kbn-securitysolution-exception-list-components", - "@kbn/securitysolution-exceptions-common": "link:packages/kbn-securitysolution-exceptions-common", - "@kbn/securitysolution-hook-utils": "link:packages/kbn-securitysolution-hook-utils", - "@kbn/securitysolution-io-ts-alerting-types": "link:packages/kbn-securitysolution-io-ts-alerting-types", - "@kbn/securitysolution-io-ts-list-types": "link:packages/kbn-securitysolution-io-ts-list-types", - "@kbn/securitysolution-io-ts-types": "link:packages/kbn-securitysolution-io-ts-types", - "@kbn/securitysolution-io-ts-utils": "link:packages/kbn-securitysolution-io-ts-utils", - "@kbn/securitysolution-list-api": "link:packages/kbn-securitysolution-list-api", - "@kbn/securitysolution-list-constants": "link:packages/kbn-securitysolution-list-constants", - "@kbn/securitysolution-list-hooks": "link:packages/kbn-securitysolution-list-hooks", - "@kbn/securitysolution-list-utils": "link:packages/kbn-securitysolution-list-utils", - "@kbn/securitysolution-lists-common": "link:packages/kbn-securitysolution-lists-common", - "@kbn/securitysolution-rules": "link:packages/kbn-securitysolution-rules", - "@kbn/securitysolution-t-grid": "link:packages/kbn-securitysolution-t-grid", - "@kbn/securitysolution-utils": "link:packages/kbn-securitysolution-utils", + "@kbn/securitysolution-endpoint-exceptions-common": "link:x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common", + "@kbn/securitysolution-es-utils": "link:src/platform/packages/shared/kbn-securitysolution-es-utils", + "@kbn/securitysolution-exception-list-components": "link:x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components", + "@kbn/securitysolution-exceptions-common": "link:x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common", + "@kbn/securitysolution-hook-utils": "link:x-pack/solutions/security/packages/kbn-securitysolution-hook-utils", + "@kbn/securitysolution-io-ts-alerting-types": "link:x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types", + "@kbn/securitysolution-io-ts-list-types": "link:x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types", + "@kbn/securitysolution-io-ts-types": "link:src/platform/packages/shared/kbn-securitysolution-io-ts-types", + "@kbn/securitysolution-io-ts-utils": "link:src/platform/packages/shared/kbn-securitysolution-io-ts-utils", + "@kbn/securitysolution-list-api": "link:x-pack/solutions/security/packages/kbn-securitysolution-list-api", + "@kbn/securitysolution-list-constants": "link:x-pack/solutions/security/packages/kbn-securitysolution-list-constants", + "@kbn/securitysolution-list-hooks": "link:x-pack/solutions/security/packages/kbn-securitysolution-list-hooks", + "@kbn/securitysolution-list-utils": "link:x-pack/solutions/security/packages/kbn-securitysolution-list-utils", + "@kbn/securitysolution-lists-common": "link:x-pack/solutions/security/packages/kbn-securitysolution-lists-common", + "@kbn/securitysolution-rules": "link:src/platform/packages/shared/kbn-securitysolution-rules", + "@kbn/securitysolution-t-grid": "link:x-pack/solutions/security/packages/kbn-securitysolution-t-grid", + "@kbn/securitysolution-utils": "link:x-pack/solutions/security/packages/kbn-securitysolution-utils", "@kbn/server-http-tools": "link:packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:src/platform/packages/shared/kbn-server-route-repository", "@kbn/server-route-repository-client": "link:src/platform/packages/shared/kbn-server-route-repository-client", diff --git a/packages/core/application/core-application-common/index.ts b/packages/core/application/core-application-common/index.ts index 633085dc95c1..25d375dc7c98 100644 --- a/packages/core/application/core-application-common/index.ts +++ b/packages/core/application/core-application-common/index.ts @@ -10,3 +10,4 @@ export type { AppCategory } from './src/app_category'; export { APP_WRAPPER_CLASS } from './src/app_wrapper_class'; export { DEFAULT_APP_CATEGORIES } from './src/default_app_categories'; +export { GlobalAppStyle } from './src/global_app_style'; diff --git a/packages/core/application/core-application-common/src/global_app_style.tsx b/packages/core/application/core-application-common/src/global_app_style.tsx new file mode 100644 index 000000000000..595602385da8 --- /dev/null +++ b/packages/core/application/core-application-common/src/global_app_style.tsx @@ -0,0 +1,164 @@ +/* + * 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 { css, Global } from '@emotion/react'; +import { useEuiTheme, type UseEuiTheme } from '@elastic/eui'; + +export const renderingOverrides = (euiTheme: UseEuiTheme['euiTheme']) => css` + #kibana-body { + // DO NOT ADD ANY OVERFLOW BEHAVIORS HERE + // It will break the sticky navigation + min-height: 100%; + display: flex; + flex-direction: column; + } + + // Affixes a div to restrict the position of charts tooltip to the visible viewport minus the header + #app-fixed-viewport { + pointer-events: none; + visibility: hidden; + position: fixed; + top: var(--kbnAppHeadersOffset, var(--euiFixedHeadersOffset, 0)); + right: 0; + bottom: 0; + left: 0; + } + + .kbnAppWrapper { + // DO NOT ADD ANY OTHER STYLES TO THIS SELECTOR + // This a very nested dependency happening in "all" apps + display: flex; + flex-flow: column nowrap; + flex-grow: 1; + z-index: 0; // This effectively puts every high z-index inside the scope of this wrapper to it doesn't interfere with the header and/or overlay mask + position: relative; // This is temporary for apps that relied on this being present on \`.application\` + } + + .kbnBody { + padding-top: var(--euiFixedHeadersOffset, 0); + } + + // Conditionally override :root CSS fixed header variable. Updating \`--euiFixedHeadersOffset\` + //on the body will cause all child EUI components to automatically update their offsets + .kbnBody--hasHeaderBanner { + --euiFixedHeadersOffset: var(--kbnHeaderOffsetWithBanner); + + // Offset fixed EuiHeaders by the top banner + .euiHeader[data-fixed-header] { + margin-top: var(--kbnHeaderBannerHeight); + } + + // Prevent banners from covering full screen data grids + .euiDataGrid--fullScreen { + height: calc(100vh - var(--kbnHeaderBannerHeight)); + top: var(--kbnHeaderBannerHeight); + } + } + + // Set a body CSS variable for the app container to use - calculates the total + // height of all fixed headers + the sticky action menu toolbar + .kbnBody--hasProjectActionMenu { + --kbnAppHeadersOffset: calc( + var(--kbnHeaderOffset) + var(--kbnProjectHeaderAppActionMenuHeight) + ); + + &.kbnBody--hasHeaderBanner { + --kbnAppHeadersOffset: calc( + var(--kbnHeaderOffsetWithBanner) + var(--kbnProjectHeaderAppActionMenuHeight) + ); + } + } + + .kbnBody--chromeHidden { + // stylelint-disable-next-line length-zero-no-unit + --euiFixedHeadersOffset: 0px; + + &.kbnBody--hasHeaderBanner { + --euiFixedHeadersOffset: var(--kbnHeaderBannerHeight); + } + + &.kbnBody--hasProjectActionMenu { + --kbnAppHeadersOffset: var(--euiFixedHeadersOffset, 0); + } + } +`; + +export const bannerStyles = (euiTheme: UseEuiTheme['euiTheme']) => css` + .header__topBanner { + position: fixed; + top: 0; + left: 0; + height: var(--kbnHeaderBannerHeight); + width: 100%; + z-index: ${euiTheme.levels.header}; + } + + .header__topBannerContainer { + height: 100%; + width: 100%; + } +`; + +export const chromeStyles = (euiTheme: UseEuiTheme['euiTheme']) => css` + .euiDataGrid__restrictBody { + .headerGlobalNav, + .kbnQueryBar { + display: none; + } + } + + .euiDataGrid__restrictBody.euiBody--headerIsFixed { + .euiFlyout { + top: 0; + height: 100%; + } + } + + .chrHeaderHelpMenu__version { + text-transform: none; + } + + .chrHeaderBadge__wrapper { + align-self: center; + margin-right: ${euiTheme.size.base}; + } + + .header__toggleNavButtonSection { + .euiBody--collapsibleNavIsDocked & { + display: none; + } + } + + .header__breadcrumbsWithExtensionContainer { + overflow: hidden; // enables text-ellipsis in the last breadcrumb + .euiHeaderBreadcrumbs { + // stop breadcrumbs from growing. + // this makes the extension appear right next to the last breadcrumb + flex-grow: 0; + margin-right: 0; + + overflow: hidden; // enables text-ellipsis in the last breadcrumb + } + } + .header__breadcrumbsAppendExtension { + flex-grow: 1; + } +`; + +export const GlobalAppStyle = () => { + const { euiTheme } = useEuiTheme(); + return ( + + ); +}; diff --git a/packages/core/http/core-http-server-internal/src/http_config.test.ts b/packages/core/http/core-http-server-internal/src/http_config.test.ts index 4ba13489552b..fa2fbe7ad9f3 100644 --- a/packages/core/http/core-http-server-internal/src/http_config.test.ts +++ b/packages/core/http/core-http-server-internal/src/http_config.test.ts @@ -517,9 +517,9 @@ describe('versioned', () => { ).toThrow(/failed validation/); }); - it('defaults version resolution "none" when in dev', () => { + it('defaults version resolution "oldest" when in dev', () => { expect(config.schema.validate({}, { dev: true })).toMatchObject({ - versioned: { versionResolution: 'none' }, + versioned: { versionResolution: 'oldest' }, }); }); }); diff --git a/packages/core/http/core-http-server-internal/src/http_config.ts b/packages/core/http/core-http-server-internal/src/http_config.ts index 11c1afc41033..4ba9bcb9e88b 100644 --- a/packages/core/http/core-http-server-internal/src/http_config.ts +++ b/packages/core/http/core-http-server-internal/src/http_config.ts @@ -213,7 +213,7 @@ const configSchema = schema.object( * Which handler resolution algo to use for public routes: "newest" or "oldest". * * @note Internal routes always require a version to be specified. - * @note in development we have an additional option "none" which is also the default in dev. + * @note in development we have an additional option "none". * This prevents any fallbacks and requires that a version specified. * Useful for ensuring that a given client always specifies a version. */ @@ -221,7 +221,7 @@ const configSchema = schema.object( schema.contextRef('dev'), true, schema.oneOf([schema.literal('newest'), schema.literal('oldest'), schema.literal('none')], { - defaultValue: 'none', + defaultValue: 'oldest', }), schema.oneOf([schema.literal('newest'), schema.literal('oldest')], { defaultValue: 'oldest', diff --git a/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.tsx b/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.tsx index 1995d6c013cf..9d5982bd40d3 100644 --- a/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.tsx +++ b/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.tsx @@ -20,6 +20,7 @@ import type { ThemeServiceStart } from '@kbn/core-theme-browser'; import type { UserProfileService } from '@kbn/core-user-profile-browser'; import { KibanaRootContextProvider } from '@kbn/react-kibana-context-root'; import { APP_FIXED_VIEWPORT_ID } from '@kbn/core-rendering-browser'; +import { GlobalAppStyle } from '@kbn/core-application-common'; import { AppWrapper } from './app_containers'; interface StartServices { @@ -62,6 +63,9 @@ export class RenderingService { ReactDOM.render( <> + {/* Global Styles that apply across the entire app */} + + {/* Fixed headers */} {chromeHeader} diff --git a/packages/kbn-babel-preset/styled_components_files.js b/packages/kbn-babel-preset/styled_components_files.js index 2a74c5b60f18..60dbb2b1053d 100644 --- a/packages/kbn-babel-preset/styled_components_files.js +++ b/packages/kbn-babel-preset/styled_components_files.js @@ -16,12 +16,13 @@ module.exports = { /packages[\/\\]kbn-ui-shared-deps-(npm|src)[\/\\]/, /src[\/\\]plugins[\/\\](kibana_react)[\/\\]/, /x-pack[\/\\]solutions[\/\\]observability[\/\\]plugins[\/\\](exploratory_view|investigate|investigate_app|observability|observability_ai_assistant_app|observability_ai_assistant_management|observability_solution|serverless_observability|streams|streams_app|synthetics|uptime|ux)[\/\\]/, - /x-pack[\/\\]plugins[\/\\](observability_solution\/apm|beats_management|fleet|lists|observability_solution\/observability|observability_solution\/observability_shared|observability_solution\/exploratory_view|security_solution|timelines|observability_solution\/synthetics|observability_solution\/ux|observability_solution\/uptime)[\/\\]/, + /x-pack[\/\\]plugins[\/\\](observability_solution\/apm|beats_management|fleet|observability_solution\/observability|observability_solution\/observability_shared|observability_solution\/exploratory_view|security_solution|timelines|observability_solution\/synthetics|observability_solution\/ux|observability_solution\/uptime)[\/\\]/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\](observability_solution\/apm|beats_management|fleet|observability_solution\/infra|lists|observability_solution\/observability|observability_solution\/observability_shared|observability_solution\/exploratory_view|security_solution|timelines|observability_solution\/synthetics|observability_solution\/ux|observability_solution\/uptime)[\/\\]/, /x-pack[\/\\]test[\/\\]plugin_functional[\/\\]plugins[\/\\]resolver_test[\/\\]/, /x-pack[\/\\]packages[\/\\]elastic_assistant[\/\\]/, /x-pack[\/\\]solutions[\/\\]security[\/\\]packages[\/\\]ecs_data_quality_dashboard[\/\\]/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]/, /x-pack[\/\\]platform[\/\\]packages[\/\\]shared[\/\\]kbn-elastic-assistant[\/\\]/, + /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]lists/, ], }; diff --git a/packages/kbn-cli-dev-mode/src/watcher.ts b/packages/kbn-cli-dev-mode/src/watcher.ts index 6dc11371d958..193458495aa2 100644 --- a/packages/kbn-cli-dev-mode/src/watcher.ts +++ b/packages/kbn-cli-dev-mode/src/watcher.ts @@ -19,7 +19,7 @@ import { Log } from './log'; const packageMatcher = makeMatcher([ '**/*', '!**/.*', - '!x-pack/plugins/screenshotting/chromium/**', + '!x-pack/platform/plugins/shared/screenshotting/chromium/**', '!x-pack/plugins/canvas/shareable_runtime/**', ]); diff --git a/packages/kbn-relocate/constants.ts b/packages/kbn-relocate/constants.ts index 5b5f4099b782..8c3bf347de4c 100644 --- a/packages/kbn-relocate/constants.ts +++ b/packages/kbn-relocate/constants.ts @@ -46,8 +46,10 @@ export const EXTENSIONS = [ 'mdz', 'asciidoc', 'sh', + 'snap', 'ts', 'jsonc', + 'xml', 'yaml', 'yml', ]; diff --git a/packages/kbn-relocate/relocate.ts b/packages/kbn-relocate/relocate.ts index fe2537ddeb04..16b2c17f4b06 100644 --- a/packages/kbn-relocate/relocate.ts +++ b/packages/kbn-relocate/relocate.ts @@ -15,7 +15,7 @@ import { orderBy } from 'lodash'; import type { ToolingLog } from '@kbn/tooling-log'; import { getPackages } from '@kbn/repo-packages'; import { REPO_ROOT } from '@kbn/repo-info'; -import type { Package } from './types'; +import type { Package, PullRequest } from './types'; import { DESCRIPTION, EXCLUDED_MODULES, KIBANA_FOLDER, NEW_BRANCH } from './constants'; import { belongsTo, @@ -26,7 +26,15 @@ import { } from './utils/relocate'; import { safeExec } from './utils/exec'; import { relocatePlan, relocateSummary } from './utils/logging'; -import { checkoutBranch, checkoutResetPr, findGithubLogin, findRemoteName } from './utils/git'; +import { + checkoutBranch, + checkoutResetPr, + cherryPickManualCommits, + findGithubLogin, + findPr, + findRemoteName, + getManualCommits, +} from './utils/git'; const moveModule = async (module: Package, log: ToolingLog) => { const destination = calculateModuleTargetFolder(module); @@ -128,6 +136,9 @@ export const findAndMoveModule = async (moduleId: string, log: ToolingLog) => { }; export const findAndRelocateModules = async (params: RelocateModulesParams, log: ToolingLog) => { + const { prNumber, baseBranch, ...findParams } = params; + let pr: PullRequest | undefined; + const upstream = await findRemoteName('elastic/kibana'); if (!upstream) { log.error( @@ -142,8 +153,6 @@ export const findAndRelocateModules = async (params: RelocateModulesParams, log: return; } - const { prNumber, baseBranch, ...findParams } = params; - const toMove = findModules(findParams, log); if (!toMove.length) { log.info( @@ -153,40 +162,60 @@ export const findAndRelocateModules = async (params: RelocateModulesParams, log: } relocatePlan(toMove, log); - const res1 = await inquirer.prompt({ + + const resConfirmPlan = await inquirer.prompt({ type: 'confirm', name: 'confirmPlan', message: `The script will RESET CHANGES in this repository, relocate the modules above and update references. Proceed?`, }); - if (!res1.confirmPlan) { + if (!resConfirmPlan.confirmPlan) { log.info('Aborting'); return; } + if (prNumber) { + pr = await findPr(prNumber); + + if (getManualCommits(pr.commits).length > 0) { + const resOverride = await inquirer.prompt({ + type: 'confirm', + name: 'overrideManualCommits', + message: 'Detected manual commits in the PR, do you want to override them?', + }); + if (!resOverride.overrideManualCommits) { + return; + } + } + } + // start with a clean repo await safeExec(`git restore --staged .`); await safeExec(`git restore .`); await safeExec(`git clean -f -d`); await safeExec(`git checkout ${baseBranch} && git pull ${upstream} ${baseBranch}`); - if (prNumber) { + if (pr) { // checkout existing PR, reset all commits, rebase from baseBranch try { - if (!(await checkoutResetPr(baseBranch, prNumber))) { - log.info('Aborting'); - return; - } + await checkoutResetPr(pr, baseBranch); } catch (error) { log.error(`Error checking out / resetting PR #${prNumber}:`); log.error(error); return; } } else { - // checkout [new] branch + // checkout new branch await checkoutBranch(NEW_BRANCH); } + // push changes in the branch + await inquirer.prompt({ + type: 'confirm', + name: 'readyRelocate', + message: `Ready to relocate! You can commit changes previous to the relocation at this point. Confirm to proceed with the relocation`, + }); + // relocate modules await safeExec(`yarn kbn bootstrap`); const movedCount = await relocateModules(toMove, log); @@ -197,10 +226,15 @@ export const findAndRelocateModules = async (params: RelocateModulesParams, log: ); return; } + relocateSummary(log); + if (pr) { + await cherryPickManualCommits(pr, log); + } + // push changes in the branch - const res2 = await inquirer.prompt({ + const resPushBranch = await inquirer.prompt({ type: 'confirm', name: 'pushBranch', message: `Relocation finished! You can commit extra changes at this point. Confirm to proceed pushing the current branch`, @@ -210,7 +244,7 @@ export const findAndRelocateModules = async (params: RelocateModulesParams, log: ? `git push --force-with-lease` : `git push --set-upstream ${origin} ${NEW_BRANCH}`; - if (!res2.pushBranch) { + if (!resPushBranch.pushBranch) { log.info(`Remember to push changes with "${pushCmd}"`); return; } @@ -221,6 +255,8 @@ export const findAndRelocateModules = async (params: RelocateModulesParams, log: log.info(`Access the PR at: https://github.com/elastic/kibana/pull/${prNumber}`); } else { log.info('TIP: Run the following command to quickly create a PR:'); - log.info(`$ gh pr create -d -t "" -F ${DESCRIPTION} -R elastic/kibana`); + log.info( + `$ gh pr create -d -B "${baseBranch}" -t "<title>" -F ${DESCRIPTION} -R elastic/kibana` + ); } }; diff --git a/packages/kbn-relocate/types.ts b/packages/kbn-relocate/types.ts index 391cef336d63..2f030bb68ae7 100644 --- a/packages/kbn-relocate/types.ts +++ b/packages/kbn-relocate/types.ts @@ -14,6 +14,7 @@ export interface CommitAuthor { } export interface Commit { + oid: string; messageHeadline: string; authors: CommitAuthor[]; } diff --git a/packages/kbn-relocate/utils/git.ts b/packages/kbn-relocate/utils/git.ts index f2e529bee6d0..0085e07fdd6b 100644 --- a/packages/kbn-relocate/utils/git.ts +++ b/packages/kbn-relocate/utils/git.ts @@ -8,17 +8,24 @@ */ import inquirer from 'inquirer'; +import type { ToolingLog } from '@kbn/tooling-log'; import type { Commit, PullRequest } from '../types'; import { safeExec } from './exec'; export const findRemoteName = async (repo: string) => { - const res = await safeExec('git remote -v'); - const remotes = res.stdout.split('\n').map((line) => line.split(/\t| /).filter(Boolean)); - return remotes.find(([_, url]) => url.includes(`github.com/${repo}`))?.[0]; + const res = await safeExec('git remote -v', true, false); + const remotes = res.stdout + .trim() + .split('\n') + .map((line) => line.split(/\t| /).filter(Boolean)) + .filter((chunks) => chunks.length >= 2); + return remotes.find( + ([, url]) => url.includes(`github.com/${repo}`) || url.includes(`github.com:${repo}`) + )?.[0]; }; export const findGithubLogin = async () => { - const res = await safeExec('gh auth status'); + const res = await safeExec('gh auth status', true, false); // e.g. ✓ Logged in to github.com account gsoldevila (/Users/gsoldevila/.config/gh/hosts.yml) const loginLine = res.stdout .split('\n') @@ -34,17 +41,16 @@ export const findPr = async (number: string): Promise<PullRequest> => { return { ...JSON.parse(res.stdout), number }; }; -export function hasManualCommits(commits: Commit[]) { - const manualCommits = commits.filter( - (commit) => - !commit.messageHeadline.startsWith('Relocating module ') && - !commit.messageHeadline.startsWith('Moving modules owned by ') && - commit.authors.some( - (author) => author.login !== 'kibanamachine' && author.login !== 'elasticmachine' - ) +export const isManualCommit = (commit: Commit) => + !commit.messageHeadline.startsWith('Relocating module ') && + !commit.messageHeadline.startsWith('Moving modules owned by ') && + !commit.messageHeadline.startsWith('Merge branch ') && + commit.authors.some( + (author) => author.login !== 'kibanamachine' && author.login !== 'elasticmachine' ); - return manualCommits.length > 0; +export function getManualCommits(commits: Commit[]) { + return commits.filter(isManualCommit); } export async function getLastCommitMessage() { @@ -87,33 +93,14 @@ async function deleteBranches(...branchNames: string[]) { ); } -export const checkoutResetPr = async (baseBranch: string, prNumber: string): Promise<boolean> => { - const pr = await findPr(prNumber); - - if (hasManualCommits(pr.commits)) { - const res = await inquirer.prompt({ - type: 'confirm', - name: 'overrideManualCommits', - message: 'Detected manual commits in the PR, do you want to override them?', - }); - if (!res.overrideManualCommits) { - return false; - } - } - - // previous cleanup on current branch - await safeExec(`git restore --staged .`); - await safeExec(`git restore .`); - await safeExec(`git clean -f -d`); - +export const checkoutResetPr = async (pr: PullRequest, baseBranch: string) => { // delete existing branch await deleteBranches(pr.headRefName); // checkout the PR branch - await safeExec(`gh pr checkout ${prNumber}`); + await safeExec(`gh pr checkout ${pr.number}`); await resetAllCommits(pr.commits.length); await safeExec(`git rebase ${baseBranch}`); - return true; }; export const checkoutBranch = async (branch: string) => { @@ -124,3 +111,71 @@ export const checkoutBranch = async (branch: string) => { await safeExec(`git checkout -b ${branch}`); } }; + +export const cherryPickManualCommits = async (pr: PullRequest, log: ToolingLog) => { + const manualCommits = getManualCommits(pr.commits); + if (manualCommits.length) { + log.info(`Found manual commits on https://github.com/elastic/kibana/pull/${pr.number}/commits`); + + for (let i = 0; i < manualCommits.length; ++i) { + const { oid, messageHeadline, authors } = manualCommits[i]; + const url = `https://github.com/elastic/kibana/pull/${pr.number}/commits/${oid}`; + + const res = await inquirer.prompt({ + type: 'list', + choices: [ + { name: 'Yes, attempt to cherry-pick', value: 'yes' }, + { name: 'No, I will add it manually (press when finished)', value: 'no' }, + ], + name: 'cherryPick', + message: `Do you want to cherry pick '${messageHeadline}' (${authors[0].login})?`, + }); + + if (res.cherryPick === 'yes') { + try { + await safeExec(`git cherry-pick ${oid}`); + log.info(`Commit '${messageHeadline}' (${authors[0].login}) cherry-picked successfully!`); + } catch (error) { + log.info(`Error trying to cherry-pick: ${url}`); + log.error(error.message); + const res2 = await inquirer.prompt({ + type: 'list', + choices: [ + { name: 'Abort this cherry-pick', value: 'abort' }, + { name: 'Conflicts solved (git cherry-pick --continue)', value: 'continue' }, + { name: 'I solved the conflicts and commited', value: 'done' }, + ], + name: 'cherryPickFailed', + message: `Automatic cherry-pick failed, manual intervention required`, + }); + + if (res2.cherryPickFailed === 'abort') { + try { + await safeExec(`git cherry-pick --abort`); + log.warning( + 'Cherry-pick aborted, please review changes in that commit and apply them manually if needed!' + ); + } catch (error2) { + log.error( + 'Cherry-pick --abort failed, please cleanup your working tree before continuing!' + ); + } + } else if (res2.cherryPickFailed === 'continue') { + try { + await safeExec(`git cherry-pick --continue`); + log.info( + `Commit '${messageHeadline}' (${authors[0].login}) cherry-picked successfully!` + ); + } catch (error2) { + await inquirer.prompt({ + type: 'confirm', + name: 'cherryPickContinueFailed', + message: `Cherry pick --continue failed, please address conflicts AND COMMIT manually. Hit confirm when ready`, + }); + } + } + } + } + } + } +}; diff --git a/packages/kbn-relocate/utils/logging.ts b/packages/kbn-relocate/utils/logging.ts index 742610dfe1de..4aec07a1d9bf 100644 --- a/packages/kbn-relocate/utils/logging.ts +++ b/packages/kbn-relocate/utils/logging.ts @@ -10,6 +10,7 @@ import type { ToolingLog } from '@kbn/tooling-log'; import { appendFileSync, writeFileSync } from 'fs'; import dedent from 'dedent'; +import Table from 'cli-table3'; import type { Package } from '../types'; import { calculateModuleTargetFolder } from './relocate'; import { @@ -21,6 +22,20 @@ import { UPDATED_RELATIVE_PATHS, } from '../constants'; +export const createModuleTable = (entries: string[][]) => { + const table = new Table({ + head: ['Id', 'Target folder'], + colAligns: ['left', 'left'], + style: { + 'padding-left': 2, + 'padding-right': 2, + }, + }); + + table.push(...entries); + return table; +}; + export const relocatePlan = (modules: Package[], log: ToolingLog) => { const plugins = modules.filter((module) => module.manifest.type === 'plugin'); const packages = modules.filter((module) => module.manifest.type !== 'plugin'); @@ -37,11 +52,8 @@ export const relocatePlan = (modules: Package[], log: ToolingLog) => { \n\n`; appendFileSync(DESCRIPTION, pluginList); - log.info( - `${plugins.length} plugin(s) are going to be relocated:\n${plugins - .map((plg) => `${plg.id} => ${target(plg)}`) - .join('\n')}` - ); + const plgTable = createModuleTable(plugins.map((plg) => [plg.id, target(plg)])); + log.info(`${plugins.length} plugin(s) are going to be relocated:\n${plgTable.toString()}`); } if (packages.length) { @@ -53,11 +65,8 @@ export const relocatePlan = (modules: Package[], log: ToolingLog) => { \n\n`; appendFileSync(DESCRIPTION, packageList); - log.info( - `${packages.length} packages(s) are going to be relocated:\n${packages - .map((plg) => `${plg.id} => ${target(plg)}`) - .join('\n')}` - ); + const pkgTable = createModuleTable(packages.map((pkg) => [pkg.id, target(pkg)])); + log.info(`${packages.length} packages(s) are going to be relocated:\n${pkgTable.toString()}`); } }; diff --git a/packages/kbn-search-api-panels/components/cloud_details.tsx b/packages/kbn-search-api-panels/components/cloud_details.tsx index df2cc6bb6837..0c406b4c3185 100644 --- a/packages/kbn-search-api-panels/components/cloud_details.tsx +++ b/packages/kbn-search-api-panels/components/cloud_details.tsx @@ -97,7 +97,7 @@ export const CloudDetailsPanel = ({ </EuiFlexItem> <EuiFlexItem grow={false}> <span> - <EuiBadge color="success"> + <EuiBadge color="accent"> <FormattedMessage id="searchApiPanels.cloudIdDetails.elasticsearchEndpoint.recommendedBadge" defaultMessage="Recommended" diff --git a/packages/kbn-securitysolution-autocomplete/index.ts b/packages/kbn-securitysolution-autocomplete/index.ts deleted file mode 100644 index e47113719176..000000000000 --- a/packages/kbn-securitysolution-autocomplete/index.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". - */ - -export * from './src/check_empty_value'; -export * from './src/es_field_selector'; -export * from './src/field_value_exists'; -export * from './src/field_value_lists'; -export * from './src/field_value_match'; -export * from './src/field_value_match_any'; -export * from './src/field_value_wildcard'; -export * from './src/filter_field_to_list'; -export * from './src/get_generic_combo_box_props'; -export * from './src/get_operators'; -export * from './src/hooks'; -export * from './src/operator'; -export * from './src/param_is_valid'; -export * from './src/param_contains_space'; - -export { default as autoCompletei18n } from './src/translations'; diff --git a/packages/kbn-securitysolution-autocomplete/jest.config.js b/packages/kbn-securitysolution-autocomplete/jest.config.js deleted file mode 100644 index b7d1e59f7786..000000000000 --- a/packages/kbn-securitysolution-autocomplete/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-autocomplete'], -}; diff --git a/packages/kbn-securitysolution-autocomplete/package.json b/packages/kbn-securitysolution-autocomplete/package.json deleted file mode 100644 index f841706e57a7..000000000000 --- a/packages/kbn-securitysolution-autocomplete/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@kbn/securitysolution-autocomplete", - "version": "1.0.0", - "description": "Security Solution auto complete", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "private": true, - "sideEffects": false -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-autocomplete/src/autocomplete/index.mock.ts b/packages/kbn-securitysolution-autocomplete/src/autocomplete/index.mock.ts deleted file mode 100644 index 71bfb7f4a67c..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/autocomplete/index.mock.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", 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". - */ - -// Copied from "src/plugins/data/public/mocks.ts" but without any type information -// TODO: Remove this in favor of the data/public/mocks if/when they become available, https://github.com/elastic/kibana/issues/100715 -export const autocompleteStartMock = { - getQuerySuggestions: jest.fn(), - getValueSuggestions: jest.fn(), - hasQuerySuggestions: jest.fn(), -}; diff --git a/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.test.ts b/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.test.ts deleted file mode 100644 index d98feca80b75..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { checkEmptyValue } from '.'; -import { getField } from '../fields/index.mock'; -import * as i18n from '../translations'; - -describe('check_empty_value', () => { - test('returns no errors if no field has been selected', () => { - const isValid = checkEmptyValue('', undefined, true, false); - - expect(isValid).toBeUndefined(); - }); - - test('returns error string if user has touched a required input and left empty', () => { - const isValid = checkEmptyValue(undefined, getField('@timestamp'), true, true); - - expect(isValid).toEqual(i18n.FIELD_REQUIRED_ERR); - }); - - test('returns no errors if required input is empty but user has not yet touched it', () => { - const isValid = checkEmptyValue(undefined, getField('@timestamp'), true, false); - - expect(isValid).toBeUndefined(); - }); - - test('returns no errors if user has touched an input that is not required and left empty', () => { - const isValid = checkEmptyValue(undefined, getField('@timestamp'), false, true); - - expect(isValid).toBeUndefined(); - }); - - test('returns no errors if user has touched an input that is not required and left empty string', () => { - const isValid = checkEmptyValue('', getField('@timestamp'), false, true); - - expect(isValid).toBeUndefined(); - }); - - test('returns null if input value is not empty string or undefined', () => { - const isValid = checkEmptyValue('hellooo', getField('@timestamp'), false, true); - - expect(isValid).toBeNull(); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.ts b/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.ts deleted file mode 100644 index 218b8fee94d1..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { DataViewFieldBase } from '@kbn/es-query'; -import * as i18n from '../translations'; - -/** - * Determines if empty value is ok - */ -export const checkEmptyValue = ( - param: string | undefined, - field: DataViewFieldBase | undefined, - isRequired: boolean, - touched: boolean -): string | undefined | null => { - if (isRequired && touched && (param == null || param.trim() === '')) { - return i18n.FIELD_REQUIRED_ERR; - } - - if ( - field == null || - (isRequired && !touched) || - (!isRequired && (param == null || param === '')) - ) { - return undefined; - } - - return null; -}; diff --git a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/disabled_types_with_tooltip_text.test.ts b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/disabled_types_with_tooltip_text.test.ts deleted file mode 100644 index 980819d1cedf..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/disabled_types_with_tooltip_text.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { disabledTypesWithTooltipText } from '../disabled_types_with_tooltip_text'; - -jest.mock('../../translations', () => ({ - BINARY_TYPE_NOT_SUPPORTED: 'Binary fields are currently unsupported', -})); -describe('disabledTypesWithTooltipText', () => { - it('should return Binary fields are currently unsupported for binary type', () => { - const type = 'binary'; - expect(disabledTypesWithTooltipText[type]).toEqual('Binary fields are currently unsupported'); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/index.test.tsx deleted file mode 100644 index 1b33b4b29464..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/index.test.tsx +++ /dev/null @@ -1,144 +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 { fireEvent, render, waitFor, within } from '@testing-library/react'; -import '@testing-library/jest-dom'; - -import { EsFieldSelector } from '..'; -import { fields, getField } from '../../fields/index.mock'; - -describe('FieldComponent', () => { - it('should render the component enabled and displays the selected field correctly', () => { - const wrapper = render( - <EsFieldSelector - isClearable={false} - isDisabled={false} - isLoading={false} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - expect(wrapper.container).toMatchSnapshot(); - const comboBox = wrapper.getByTestId('fieldAutocompleteComboBox'); - const input = within(comboBox).getByRole('combobox'); - expect(input).toHaveAttribute('value', 'machine.os.raw'); - }); - it('should render the component disabled if isDisabled is true', () => { - const wrapper = render( - <EsFieldSelector - isClearable={false} - isDisabled={true} - isLoading={false} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - expect(wrapper.container).toMatchSnapshot(); - expect(wrapper.getByTestId('fieldAutocompleteComboBox').querySelector('input')).toBeDisabled(); - }); - it('should render the loading spinner if isLoading is true when clicked', () => { - const wrapper = render( - <EsFieldSelector - isClearable={false} - isDisabled={true} - isLoading={true} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - const fieldAutocompleteComboBox = wrapper.getByTestId('fieldAutocompleteComboBox'); - expect(wrapper.container).toMatchSnapshot(); - fireEvent.click(fieldAutocompleteComboBox); - expect(wrapper.getByRole('progressbar')).toBeInTheDocument(); - }); - it('should allow user to clear values if isClearable is true', () => { - const wrapper = render( - <EsFieldSelector - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={true} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - expect(wrapper.container).toMatchSnapshot(); - expect(wrapper.getByTestId('comboBoxClearButton')).toBeInTheDocument(); - }); - it('should change the selected value', async () => { - const wrapper = render( - <EsFieldSelector - isClearable={false} - isDisabled={true} - isLoading={false} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - const fieldAutocompleteComboBox = wrapper.getByTestId('comboBoxSearchInput'); - fireEvent.change(fieldAutocompleteComboBox, { target: { value: '_source' } }); - expect(fieldAutocompleteComboBox).toHaveValue('_source'); - }); - - it('it allows custom user input if "acceptsCustomOptions" is "true"', async () => { - const mockOnChange = jest.fn(); - const wrapper = render( - <EsFieldSelector - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - placeholder="Placeholder text" - selectedField={undefined} - acceptsCustomOptions - /> - ); - - const fieldAutocompleteComboBox = wrapper.getByTestId('comboBoxSearchInput'); - fireEvent.change(fieldAutocompleteComboBox, { target: { value: 'custom' } }); - await waitFor(() => - expect(wrapper.getByTestId('fieldAutocompleteComboBox')).toHaveTextContent('custom') - ); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/disabled_types_with_tooltip_text.ts b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/disabled_types_with_tooltip_text.ts deleted file mode 100644 index 422a909bb991..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/disabled_types_with_tooltip_text.ts +++ /dev/null @@ -1,17 +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". - */ - -interface DisabledTypesTextType { - [typeName: string]: string; -} -import * as i18n from '../translations'; - -export const disabledTypesWithTooltipText: DisabledTypesTextType = { - binary: i18n.BINARY_TYPE_NOT_SUPPORTED, -}; diff --git a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/index.tsx b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/index.tsx deleted file mode 100644 index 31efaa23b62d..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/index.tsx +++ /dev/null @@ -1,109 +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 { EuiComboBox } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { FieldBaseProps } from './types'; -import { useEsField } from './use_es_field'; - -const AS_PLAIN_TEXT = { asPlainText: true }; - -interface EsFieldSelectorProps extends FieldBaseProps { - isClearable?: boolean; - isDisabled?: boolean; - isLoading?: boolean; - placeholder: string; - acceptsCustomOptions?: boolean; - showMappingConflicts?: boolean; - 'aria-label'?: string; -} - -export function EsFieldSelector({ - fieldInputWidth, - fieldTypeFilter = [], - indexPattern, - isClearable = false, - isDisabled = false, - isLoading = false, - isRequired = false, - onChange, - placeholder, - selectedField, - acceptsCustomOptions = false, - showMappingConflicts = false, - 'aria-label': ariaLabel, -}: EsFieldSelectorProps): JSX.Element { - const { - isInvalid, - comboOptions, - selectedComboOptions, - fieldWidth, - renderFields, - handleTouch, - handleValuesChange, - handleCreateCustomOption, - } = useEsField({ - indexPattern, - fieldTypeFilter, - isRequired, - selectedField, - fieldInputWidth, - showMappingConflicts, - onChange, - }); - - if (acceptsCustomOptions) { - return ( - <EuiComboBox - placeholder={placeholder} - options={comboOptions} - selectedOptions={selectedComboOptions} - onChange={handleValuesChange} - isLoading={isLoading} - isDisabled={isDisabled} - isClearable={isClearable} - isInvalid={isInvalid} - onFocus={handleTouch} - singleSelection={AS_PLAIN_TEXT} - data-test-subj="fieldAutocompleteComboBox" - style={fieldWidth} - onCreateOption={handleCreateCustomOption} - customOptionText={i18n.translate('autocomplete.customOptionText', { - defaultMessage: 'Add {searchValuePlaceholder} as a custom field', - values: { searchValuePlaceholder: '{searchValue}' }, - })} - fullWidth - renderOption={renderFields} - aria-label={ariaLabel} - /> - ); - } - - return ( - <EuiComboBox - placeholder={placeholder} - options={comboOptions} - selectedOptions={selectedComboOptions} - onChange={handleValuesChange} - isLoading={isLoading} - isDisabled={isDisabled} - isClearable={isClearable} - isInvalid={isInvalid} - onFocus={handleTouch} - singleSelection={AS_PLAIN_TEXT} - data-test-subj="fieldAutocompleteComboBox" - style={fieldWidth} - fullWidth - renderOption={renderFields} - aria-label={ariaLabel} - /> - ); -} diff --git a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/types.ts b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/types.ts deleted file mode 100644 index b0f1ab56e807..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/types.ts +++ /dev/null @@ -1,36 +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 { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; -import { FieldConflictsInfo } from '@kbn/securitysolution-list-utils'; -import { GetGenericComboBoxPropsReturn } from '../get_generic_combo_box_props'; - -export interface FieldBaseProps { - indexPattern: DataViewBase | undefined; - fieldTypeFilter?: string[]; - isRequired?: boolean; - selectedField?: DataViewFieldBase | undefined; - fieldInputWidth?: number; - showMappingConflicts?: boolean; - onChange: (a: DataViewFieldBase[]) => void; -} - -export interface ComboBoxFields { - availableFields: DataViewField[]; - selectedFields: DataViewField[]; -} - -export interface GetFieldComboBoxPropsReturn extends GetGenericComboBoxPropsReturn { - disabledLabelTooltipTexts: { [label: string]: string }; - mappingConflictsTooltipInfo: { [label: string]: FieldConflictsInfo[] }; -} - -export interface DataViewField extends DataViewFieldBase { - esTypes?: string[]; -} diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.test.tsx deleted file mode 100644 index 801f8e2864d5..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.test.tsx +++ /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 React from 'react'; -import { mount } from 'enzyme'; - -import { AutocompleteFieldExistsComponent } from '.'; - -describe('AutocompleteFieldExistsComponent', () => { - test('it renders field disabled', () => { - const wrapper = mount(<AutocompleteFieldExistsComponent placeholder="Placeholder text" />); - - expect( - wrapper - .find(`[data-test-subj="valuesAutocompleteComboBox existsComboxBox"] input`) - .prop('disabled') - ).toBeTruthy(); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.tsx deleted file mode 100644 index 592886905aae..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.tsx +++ /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 React from 'react'; -import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; - -const NO_OPTIONS_FOR_EXIST: EuiComboBoxOptionOption[] = []; - -interface AutocompleteFieldExistsProps { - placeholder: string; - rowLabel?: string; - 'aria-label'?: string; -} - -export const AutocompleteFieldExistsComponent: React.FC<AutocompleteFieldExistsProps> = ({ - placeholder, - rowLabel, - 'aria-label': ariaLabel, -}): JSX.Element => ( - <EuiFormRow label={rowLabel} fullWidth> - <EuiComboBox - placeholder={placeholder} - options={NO_OPTIONS_FOR_EXIST} - selectedOptions={NO_OPTIONS_FOR_EXIST} - onChange={undefined} - isDisabled - data-test-subj="valuesAutocompleteComboBox existsComboxBox" - aria-label={ariaLabel} - fullWidth - /> - </EuiFormRow> -); - -AutocompleteFieldExistsComponent.displayName = 'AutocompleteFieldExists'; diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.test.tsx deleted file mode 100644 index b2d8dc0a70be..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.test.tsx +++ /dev/null @@ -1,270 +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 { mount } from 'enzyme'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { waitFor } from '@testing-library/react'; -import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; - -import { getField } from '../fields/index.mock'; -import { AutocompleteFieldListsComponent } from '.'; -import { - getListResponseMock, - getFoundListsBySizeSchemaMock, - DATE_NOW, - IMMUTABLE, - VERSION, -} from '../list_schema/index.mock'; - -// TODO: Once these mocks are available, use them instead of hand mocking, https://github.com/elastic/kibana/issues/100715 -// const mockKibanaHttpService = coreMock.createStart().http; -// import { coreMock } from '../../../../../../../src/core/public/mocks'; -const mockKibanaHttpService = jest.fn(); -const mockShowValueListModal = jest.fn(); -const MockedShowValueListModal = (props: unknown) => { - mockShowValueListModal(props); - return <></>; -}; -const mockStart = jest.fn(); -const mockKeywordList: ListSchema = { - ...getListResponseMock(), - id: 'keyword_list', - name: 'keyword list', - type: 'keyword', -}; -const mockResult = { ...getFoundListsBySizeSchemaMock() }; -mockResult.smallLists = [...mockResult.smallLists, mockKeywordList]; -mockResult.largeLists = []; -jest.mock('@kbn/securitysolution-list-hooks', () => { - const originalModule = jest.requireActual('@kbn/securitysolution-list-hooks'); - - return { - ...originalModule, - useFindListsBySize: () => ({ - error: undefined, - loading: false, - result: mockResult, - start: mockStart.mockReturnValue(mockResult), - }), - }; -}); - -describe('AutocompleteFieldListsComponent', () => { - test('it renders disabled if "isDisabled" is true', async () => { - const wrapper = mount( - <AutocompleteFieldListsComponent - httpService={mockKibanaHttpService} - isClearable={true} - isDisabled - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="some-list-id" - showValueListModal={MockedShowValueListModal} - /> - ); - - expect( - wrapper - .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] input`) - .prop('disabled') - ).toBeTruthy(); - }); - - test('it renders loading if "isLoading" is true', async () => { - const wrapper = mount( - <AutocompleteFieldListsComponent - httpService={mockKibanaHttpService} - isClearable={false} - isDisabled={false} - isLoading - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('@tags')} - selectedValue="" - showValueListModal={MockedShowValueListModal} - /> - ); - - wrapper - .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] button`) - .at(0) - .simulate('click'); - expect( - wrapper - .find( - `EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteComboBox listsComboxBox-optionsList"]` - ) - .prop('isLoading') - ).toBeTruthy(); - }); - - test('it allows user to clear values if "isClearable" is true', async () => { - const wrapper = mount( - <AutocompleteFieldListsComponent - httpService={mockKibanaHttpService} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="" - showValueListModal={MockedShowValueListModal} - /> - ); - expect( - wrapper - .find('EuiComboBox[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]') - .prop('options') - ).toEqual([{ label: 'some name', disabled: false }]); - }); - - test('it correctly displays lists that match the selected "keyword" field esType', () => { - const wrapper = mount( - <AutocompleteFieldListsComponent - httpService={mockKibanaHttpService} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('@tags')} - selectedValue="" - showValueListModal={MockedShowValueListModal} - /> - ); - - wrapper.find('[data-test-subj="comboBoxToggleListButton"] button').simulate('click'); - - expect( - wrapper - .find('EuiComboBox[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]') - .prop('options') - ).toEqual([{ label: 'keyword list', disabled: false }]); - }); - - test('it correctly displays lists that match the selected "ip" field esType', () => { - const wrapper = mount( - <AutocompleteFieldListsComponent - httpService={mockKibanaHttpService} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="" - showValueListModal={MockedShowValueListModal} - /> - ); - - wrapper.find('[data-test-subj="comboBoxToggleListButton"] button').simulate('click'); - - expect( - wrapper - .find('EuiComboBox[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]') - .prop('options') - ).toEqual([{ label: 'some name', disabled: false }]); - }); - - test('it correctly displays selected list', async () => { - const wrapper = mount( - <AutocompleteFieldListsComponent - httpService={mockKibanaHttpService} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="some-list-id" - showValueListModal={MockedShowValueListModal} - /> - ); - - expect( - wrapper - .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] input`) - .at(0) - .props().value - ).toEqual('some name'); - }); - - test('it invokes "onChange" when option selected', async () => { - const mockOnChange = jest.fn(); - const wrapper = mount( - <AutocompleteFieldListsComponent - httpService={mockKibanaHttpService} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="" - showValueListModal={MockedShowValueListModal} - /> - ); - - ( - wrapper.find(EuiComboBox).props() as unknown as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - } - ).onChange([{ label: 'some name', disabled: false }]); - - await waitFor(() => { - expect(mockOnChange).toHaveBeenCalledWith({ - '@timestamp': DATE_NOW, - _version: undefined, - created_at: DATE_NOW, - created_by: 'some user', - description: 'some description', - deserializer: undefined, - id: 'some-list-id', - immutable: IMMUTABLE, - meta: {}, - name: 'some name', - serializer: undefined, - tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', - type: 'ip', - updated_at: DATE_NOW, - updated_by: 'some user', - version: VERSION, - }); - }); - }); - - test('it render the value list modal', async () => { - mockShowValueListModal.mockReset(); - mount( - <AutocompleteFieldListsComponent - httpService={mockKibanaHttpService} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="some-list-id" - showValueListModal={MockedShowValueListModal} - /> - ); - - expect(mockShowValueListModal).toHaveBeenCalledWith( - expect.objectContaining({ - children: 'Show value list', - listId: 'some-list-id', - shouldShowContentIfModalNotAvailable: false, - }) - ); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.tsx deleted file mode 100644 index c332dde356f2..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.tsx +++ /dev/null @@ -1,183 +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, { ElementType, useCallback, useEffect, useMemo, useState } from 'react'; -import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow, EuiLink, EuiText } from '@elastic/eui'; -import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { useFindListsBySize } from '@kbn/securitysolution-list-hooks'; -import { DataViewFieldBase } from '@kbn/es-query'; -import { getDocLinks } from '@kbn/doc-links'; - -import { filterFieldToList } from '../filter_field_to_list'; -import { getGenericComboBoxProps } from '../get_generic_combo_box_props'; - -// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 -// import { HttpStart } from '@kbn/core/public'; -type HttpStart = any; - -import * as i18n from '../translations'; - -const SINGLE_SELECTION = { asPlainText: true }; - -interface AutocompleteFieldListsProps { - httpService: HttpStart; - isClearable: boolean; - isDisabled: boolean; - isLoading: boolean; - onChange: (arg: ListSchema) => void; - placeholder: string; - rowLabel?: string; - selectedField: DataViewFieldBase | undefined; - selectedValue: string | undefined; - allowLargeValueLists?: boolean; - 'aria-label'?: string; - showValueListModal: ElementType; -} - -export interface AutocompleteListsData { - smallLists: ListSchema[]; - largeLists: ListSchema[]; -} - -export const AutocompleteFieldListsComponent: React.FC<AutocompleteFieldListsProps> = ({ - httpService, - isClearable = false, - isDisabled = false, - isLoading = false, - onChange, - placeholder, - rowLabel, - selectedField, - selectedValue, - allowLargeValueLists = false, - 'aria-label': ariaLabel, - showValueListModal, -}): JSX.Element => { - const [error, setError] = useState<string | undefined>(undefined); - const [listData, setListData] = useState<AutocompleteListsData>({ - smallLists: [], - largeLists: [], - }); - const { loading, result, start } = useFindListsBySize(); - const getLabel = useCallback(({ name }: ListSchema) => name, []); - - const optionsMemo = useMemo( - () => filterFieldToList(listData, selectedField), - [listData, selectedField] - ); - const selectedOptionsMemo = useMemo(() => { - if (selectedValue != null) { - const combinedLists = [...listData.smallLists, ...listData.largeLists]; - const list = combinedLists.filter(({ id }) => id === selectedValue); - return list ?? []; - } else { - return []; - } - }, [selectedValue, listData]); - const { comboOptions, labels, selectedComboOptions } = useMemo( - () => - getGenericComboBoxProps<ListSchema>({ - getLabel, - options: [...optionsMemo.smallLists, ...optionsMemo.largeLists], - selectedOptions: selectedOptionsMemo, - disabledOptions: allowLargeValueLists ? undefined : optionsMemo.largeLists, // Disable large lists if the rule type doesn't allow it - }), - [optionsMemo, selectedOptionsMemo, getLabel, allowLargeValueLists] - ); - - const handleValuesChange = useCallback( - (newOptions: EuiComboBoxOptionOption[]) => { - const combinedLists = [...optionsMemo.smallLists, ...optionsMemo.largeLists]; - const [newValue] = newOptions.map(({ label }) => combinedLists[labels.indexOf(label)]); - onChange(newValue ?? ''); - }, - [labels, optionsMemo, onChange] - ); - - const setIsTouchedValue = useCallback((): void => { - setError(selectedValue == null ? i18n.FIELD_REQUIRED_ERR : undefined); - }, [selectedValue]); - - useEffect(() => { - if (result != null) { - setListData(result); - } - }, [result]); - - useEffect(() => { - if (selectedField != null && httpService != null) { - start({ - http: httpService, - pageIndex: 1, - pageSize: 500, - }); - } - }, [selectedField, start, httpService]); - - const isLoadingState = useMemo((): boolean => isLoading || loading, [isLoading, loading]); - const ShowValueListModal = showValueListModal; - - const helpText = useMemo(() => { - return ( - <> - {selectedValue && ( - <ShowValueListModal shouldShowContentIfModalNotAvailable={false} listId={selectedValue}> - {i18n.SHOW_VALUE_LIST_MODAL} - </ShowValueListModal> - )} - {!allowLargeValueLists && ( - <EuiText size="xs"> - {i18n.LISTS_TOOLTIP_INFO}{' '} - <EuiLink - external - target="_blank" - href={ - getDocLinks({ - kibanaBranch: 'main', - buildFlavor: 'traditional', - }).securitySolution.exceptions.value_lists - } - > - {i18n.SEE_DOCUMENTATION} - </EuiLink> - </EuiText> - )} - </> - ); - }, [allowLargeValueLists, selectedValue, ShowValueListModal]); - return ( - <EuiFormRow - label={rowLabel} - error={error} - isInvalid={error != null} - helpText={helpText} - fullWidth - > - <EuiComboBox - async - data-test-subj="valuesAutocompleteComboBox listsComboxBox" - fullWidth - isClearable={isClearable} - isDisabled={isDisabled} - isInvalid={error != null} - isLoading={isLoadingState} - onBlur={setIsTouchedValue} - onChange={handleValuesChange} - options={comboOptions} - placeholder={placeholder} - selectedOptions={selectedComboOptions} - singleSelection={SINGLE_SELECTION} - sortMatchesBy="startsWith" - aria-label={ariaLabel} - /> - </EuiFormRow> - ); -}; - -AutocompleteFieldListsComponent.displayName = 'AutocompleteFieldList'; diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx deleted file mode 100644 index e83d79b180e9..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx +++ /dev/null @@ -1,631 +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 { ReactWrapper, mount } from 'enzyme'; -import { - EuiComboBox, - EuiComboBoxOptionOption, - EuiFormHelpText, - EuiSuperSelect, -} from '@elastic/eui'; -import { act, waitFor } from '@testing-library/react'; -import { AutocompleteFieldMatchComponent } from '.'; -import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; -import { fields, getField } from '../fields/index.mock'; -import { autocompleteStartMock } from '../autocomplete/index.mock'; - -jest.mock('../hooks/use_field_value_autocomplete'); -jest.mock('../translations', () => ({ - FIELD_SPACE_WARNING: 'Warning: there is a space', -})); -describe('AutocompleteFieldMatchComponent', () => { - let wrapper: ReactWrapper; - - const getValueSuggestionsMock = jest - .fn() - .mockResolvedValue([false, true, ['value 3', 'value 4'], jest.fn()]); - - const findEuiComboBox = () => - wrapper.find(EuiComboBox).props() as unknown as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - onSearchChange: (a: string) => void; - onCreateOption: (a: string) => void; - }; - - beforeEach(() => { - (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ - false, - true, - ['value 1', 'value 2'], - getValueSuggestionsMock, - ]); - }); - - afterEach(() => { - jest.clearAllMocks(); - wrapper.unmount(); - }); - - test('it renders row label if one passed in', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('ip')} - selectedValue="127.0.0.1" - /> - ); - - expect( - wrapper.find('[data-test-subj="valuesAutocompleteMatchLabel"] label').at(0).text() - ).toEqual('Row Label'); - }); - - test('it renders disabled if "isDisabled" is true', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="127.0.0.1" - /> - ); - - expect( - wrapper.find('[data-test-subj="valuesAutocompleteMatch"] input').prop('disabled') - ).toBeTruthy(); - }); - - test('it renders loading if "isLoading" is true', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="127.0.0.1" - /> - ); - wrapper.find('[data-test-subj="valuesAutocompleteMatch"] button').at(0).simulate('click'); - expect( - wrapper - .find('EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteMatch-optionsList"]') - .prop('isLoading') - ).toBeTruthy(); - }); - - test('it allows user to clear values if "isClearable" is true', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={true} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="127.0.0.1" - /> - ); - - expect(wrapper.find(`[data-test-subj="comboBoxClearButton"]`)).toBeTruthy(); - }); - - test('it correctly displays selected value', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="127.0.0.1" - /> - ); - - expect( - wrapper.find('[data-test-subj="valuesAutocompleteMatch"] input').at(0).props().value - ).toEqual('127.0.0.1'); - }); - - test('it invokes "onChange" when new value created', () => { - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ip')} - selectedValue="" - /> - ); - - findEuiComboBox().onCreateOption('127.0.0.1'); - - expect(mockOnChange).toHaveBeenCalledWith('127.0.0.1'); - }); - - test('it invokes "onChange" when new value selected', () => { - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - selectedValue="" - /> - ); - - findEuiComboBox().onChange([{ label: 'value 1' }]); - - expect(mockOnChange).toHaveBeenCalledWith('value 1'); - }); - - test('it invokes "onChange" with empty value (i.e. clears selection) when new value searched', () => { - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - selectedValue="value 1" - /> - ); - - act(() => { - findEuiComboBox().onSearchChange('value 12'); - }); - - expect(mockOnChange).toHaveBeenCalledWith(''); - }); - - test('should show the warning helper text if the new value contains spaces when change', async () => { - (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ - false, - true, - [' value 1 ', 'value 2'], - getValueSuggestionsMock, - ]); - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - rowLabel="Test" - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - selectedValue="" - /> - ); - - await waitFor(() => findEuiComboBox().onChange([{ label: ' value 1 ' }])); - wrapper.update(); - expect(mockOnChange).toHaveBeenCalledWith(' value 1 '); - - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeTruthy(); - }); - - test('it refreshes autocomplete with search query when new value searched', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - selectedValue="" - /> - ); - act(() => { - findEuiComboBox().onSearchChange('value 1'); - }); - - expect(useFieldValueAutocomplete).toHaveBeenCalledWith({ - autocompleteService: autocompleteStartMock, - fieldValue: '', - indexPattern: { - fields, - id: '1234', - title: 'logstash-*', - }, - operatorType: 'match', - query: 'value 1', - selectedField: getField('machine.os.raw'), - }); - }); - - test('it refreshes autocomplete with search query when input field is cleared', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - selectedValue="windows" - /> - ); - - act(() => { - findEuiComboBox().onSearchChange('value 1'); - }); - act(() => { - findEuiComboBox().onSearchChange(''); - }); - - // 1st call is initial render, 2nd call sets the search query: - expect(useFieldValueAutocomplete).toHaveBeenNthCalledWith(2, { - autocompleteService: autocompleteStartMock, - fieldValue: 'windows', - indexPattern: { fields, id: '1234', title: 'logstash-*' }, - operatorType: 'match', - query: 'value 1', - selectedField: getField('machine.os.raw'), - }); - // last call is the refresh when input field is cleared - expect(useFieldValueAutocomplete).toHaveBeenLastCalledWith({ - autocompleteService: autocompleteStartMock, - fieldValue: 'windows', - indexPattern: { fields, id: '1234', title: 'logstash-*' }, - operatorType: 'match', - query: '', - selectedField: getField('machine.os.raw'), - }); - }); - - test('should show the warning helper text if the new value contains spaces when searching a new query', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - selectedValue="" - /> - ); - act(() => { - findEuiComboBox().onSearchChange(' value 1'); - }); - - wrapper.update(); - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeTruthy(); - expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); - }); - - test('should show the warning helper text if selectedValue contains spaces when editing', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - selectedValue=" leading and trailing space " - /> - ); - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeTruthy(); - expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); - }); - - test('should not show the warning helper text if selectedValue is falsy', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - selectedValue="" - /> - ); - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeFalsy(); - }); - - describe('boolean type', () => { - const valueSuggestionsMock = jest.fn().mockResolvedValue([false, false, [], jest.fn()]); - - beforeEach(() => { - (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ - false, - false, - [], - valueSuggestionsMock, - ]); - }); - - test('it displays only two options - "true" or "false"', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ssl')} - selectedValue="" - /> - ); - expect( - wrapper.find('[data-test-subj="valuesAutocompleteMatchBoolean"]').exists() - ).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="valuesAutocompleteMatchBoolean"]').at(0).prop('options') - ).toEqual([ - { - inputDisplay: 'true', - value: 'true', - }, - { - inputDisplay: 'false', - value: 'false', - }, - ]); - }); - - test('it invokes "onChange" with "true" when selected', () => { - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ssl')} - selectedValue="" - /> - ); - - ( - wrapper.find(EuiSuperSelect).props() as unknown as { - onChange: (a: string) => void; - } - ).onChange('true'); - - expect(mockOnChange).toHaveBeenCalledWith('true'); - }); - - test('it invokes "onChange" with "false" when selected', () => { - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('ssl')} - selectedValue="" - /> - ); - - ( - wrapper.find(EuiSuperSelect).props() as unknown as { - onChange: (a: string) => void; - } - ).onChange('false'); - - expect(mockOnChange).toHaveBeenCalledWith('false'); - }); - }); - - describe('number type', () => { - const valueSuggestionsMock = jest.fn().mockResolvedValue([false, false, [], jest.fn()]); - - beforeEach(() => { - (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ - false, - false, - [], - valueSuggestionsMock, - ]); - }); - - test('it number input when field type is number', () => { - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('bytes')} - selectedValue="" - /> - ); - - expect( - wrapper.find('[data-test-subj="valueAutocompleteFieldMatchNumber"]').exists() - ).toBeTruthy(); - }); - - test('it invokes "onChange" with numeric value when inputted', () => { - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldMatchComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('bytes')} - selectedValue="" - /> - ); - wrapper - .find('[data-test-subj="valueAutocompleteFieldMatchNumber"] input') - .at(0) - .simulate('change', { target: { value: '8' } }); - - expect(mockOnChange).toHaveBeenCalledWith('8'); - }); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx deleted file mode 100644 index 5b627451db19..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx +++ /dev/null @@ -1,377 +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, { useCallback, useMemo, useState, useEffect } from 'react'; -import { - EuiSuperSelect, - EuiFormRow, - EuiFieldNumber, - EuiComboBoxOptionOption, - EuiComboBox, -} from '@elastic/eui'; -import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; - -import { uniq } from 'lodash'; - -import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; - -// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 -// import { AutocompleteStart } from '../../../../../../../src/plugins/unified_search/public'; -type AutocompleteStart = any; - -import * as i18n from '../translations'; -import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; -import { - getGenericComboBoxProps, - GetGenericComboBoxPropsReturn, -} from '../get_generic_combo_box_props'; -import { paramIsValid } from '../param_is_valid'; -import { paramContainsSpace } from '../param_contains_space'; - -const BOOLEAN_OPTIONS = [ - { inputDisplay: 'true', value: 'true' }, - { inputDisplay: 'false', value: 'false' }, -]; - -const SINGLE_SELECTION = { asPlainText: true }; - -type Warning = string | React.ReactNode; - -interface AutocompleteFieldMatchProps { - placeholder: string; - selectedField: DataViewFieldBase | undefined; - selectedValue: string | undefined; - indexPattern: DataViewBase | undefined; - isLoading?: boolean; - isDisabled?: boolean; - isClearable?: boolean; - isRequired?: boolean; - fieldInputWidth?: number; - rowLabel?: string; - autocompleteService: AutocompleteStart; - onChange: (arg: string) => void; - onError?: (arg: boolean) => void; - onWarning?: (arg: boolean) => void; - warning?: Warning; - 'aria-label'?: string; -} - -export const AutocompleteFieldMatchComponent: React.FC<AutocompleteFieldMatchProps> = ({ - placeholder, - rowLabel, - selectedField, - selectedValue, - indexPattern, - isLoading = false, - isDisabled = false, - isClearable = false, - isRequired = false, - fieldInputWidth, - autocompleteService, - onChange, - onError, - onWarning, - warning, - 'aria-label': ariaLabel, -}): JSX.Element => { - const [searchQuery, setSearchQuery] = useState(''); - const [touched, setIsTouched] = useState(false); - const [error, setError] = useState<string | undefined>(undefined); - const [showSpacesWarning, setShowSpacesWarning] = useState<boolean>(false); - const [isLoadingSuggestions, isSuggestingValues, suggestions] = useFieldValueAutocomplete({ - autocompleteService, - fieldValue: selectedValue, - indexPattern, - operatorType: OperatorTypeEnum.MATCH, - query: searchQuery, - selectedField, - }); - const getLabel = useCallback((option: string): string => option, []); - - const optionsMemo = useMemo((): string[] => { - const valueAsStr = String(selectedValue); - return selectedValue != null && selectedValue.trim() !== '' - ? uniq([valueAsStr, ...suggestions]) - : suggestions; - }, [suggestions, selectedValue]); - - const selectedOptionsMemo = useMemo((): string[] => { - const valueAsStr = String(selectedValue); - return selectedValue ? [valueAsStr] : []; - }, [selectedValue]); - - const handleSpacesWarning = useCallback( - (param: string | undefined) => { - if (!param) return setShowSpacesWarning(false); - setShowSpacesWarning(!!paramContainsSpace(param)); - }, - [setShowSpacesWarning] - ); - - const handleError = useCallback( - (err: string | undefined): void => { - setError((existingErr): string | undefined => { - const oldErr = existingErr != null; - const newErr = err != null; - if (oldErr !== newErr && onError != null) { - onError(newErr); - } - - return err; - }); - }, - [setError, onError] - ); - - const handleWarning = useCallback( - (warn: Warning | undefined): void => { - if (onWarning) { - onWarning(warn !== undefined); - } - }, - [onWarning] - ); - - const { comboOptions, labels, selectedComboOptions } = useMemo( - (): GetGenericComboBoxPropsReturn => - getGenericComboBoxProps<string>({ - getLabel, - options: optionsMemo, - selectedOptions: selectedOptionsMemo, - }), - [optionsMemo, selectedOptionsMemo, getLabel] - ); - - const handleValuesChange = useCallback( - (newOptions: EuiComboBoxOptionOption[]): void => { - const [newValue] = newOptions.map(({ label }) => optionsMemo[labels.indexOf(label)]); - - handleSpacesWarning(newValue); - handleError(undefined); - handleWarning(undefined); - onChange(newValue ?? ''); - }, - [handleError, handleWarning, handleSpacesWarning, labels, onChange, optionsMemo] - ); - - const handleSearchChange = useCallback( - (searchVal: string): void => { - if (searchVal !== '' && selectedField != null) { - const err = paramIsValid(searchVal, selectedField, isRequired, touched); - handleError(err); - handleWarning(warning); - - if (!err) handleSpacesWarning(searchVal); - } - - if (searchVal) { - // Clear selected option when user types to allow user to modify value without {backspace} - onChange(''); - } - - // Update search query unconditionally to show correct suggestions even when input is cleared - setSearchQuery(searchVal); - }, - [ - selectedField, - onChange, - isRequired, - touched, - handleError, - handleWarning, - warning, - handleSpacesWarning, - ] - ); - - const handleCreateOption = useCallback( - (option: string): boolean | undefined => { - const err = paramIsValid(option, selectedField, isRequired, touched); - handleError(err); - handleWarning(warning); - - if (err != null) { - // Explicitly reject the user's input - setShowSpacesWarning(false); - return false; - } - - handleSpacesWarning(option); - onChange(option); - return undefined; - }, - [ - isRequired, - onChange, - selectedField, - touched, - handleError, - handleSpacesWarning, - handleWarning, - warning, - ] - ); - - const handleNonComboBoxInputChange = useCallback( - (event: React.ChangeEvent<HTMLInputElement>): void => { - const newValue = event.target.value; - onChange(newValue); - }, - [onChange] - ); - - const handleBooleanInputChange = useCallback( - (newOption: string): void => { - onChange(newOption); - }, - [onChange] - ); - - const setIsTouchedValue = useCallback((): void => { - setIsTouched(true); - - const err = paramIsValid(selectedValue, selectedField, isRequired, true); - handleError(err); - handleWarning(warning); - }, [setIsTouched, handleError, selectedValue, selectedField, isRequired, warning, handleWarning]); - - const inputPlaceholder = useMemo((): string => { - if (isLoading || isLoadingSuggestions) { - return i18n.LOADING; - } else if (selectedField == null) { - return i18n.SELECT_FIELD_FIRST; - } else { - return placeholder; - } - }, [isLoading, selectedField, isLoadingSuggestions, placeholder]); - - const isLoadingState = useMemo( - (): boolean => isLoading || isLoadingSuggestions, - [isLoading, isLoadingSuggestions] - ); - - useEffect((): void => { - setError(undefined); - if (onError != null) onError(false); - - handleSpacesWarning(selectedValue); - // Looks like selectedField return new object every time when we for example add "and" entry - // that's why we need to check for name and type here - // Probably we should use some kind of memoization on parent components for entries - }, [selectedField?.name, selectedField?.type, selectedValue, handleSpacesWarning, onError]); - - const defaultInput = useMemo((): JSX.Element => { - return ( - <EuiFormRow - label={rowLabel} - error={error} - isInvalid={selectedField != null && error != null} - data-test-subj="valuesAutocompleteMatchLabel" - fullWidth - helpText={warning || (showSpacesWarning && i18n.FIELD_SPACE_WARNING)} - > - <EuiComboBox - placeholder={inputPlaceholder} - isDisabled={isDisabled || !selectedField} - isLoading={isLoadingState} - isClearable={isClearable} - options={comboOptions} - selectedOptions={selectedComboOptions} - onChange={handleValuesChange} - singleSelection={SINGLE_SELECTION} - onSearchChange={handleSearchChange} - onCreateOption={handleCreateOption} - isInvalid={selectedField != null && error != null} - onBlur={setIsTouchedValue} - sortMatchesBy="startsWith" - data-test-subj="valuesAutocompleteMatch" - style={fieldInputWidth ? { width: `${fieldInputWidth}px` } : {}} - aria-label={ariaLabel} - fullWidth - async - /> - </EuiFormRow> - ); - }, [ - rowLabel, - error, - selectedField, - showSpacesWarning, - inputPlaceholder, - isDisabled, - isLoadingState, - isClearable, - comboOptions, - selectedComboOptions, - handleValuesChange, - handleSearchChange, - handleCreateOption, - setIsTouchedValue, - warning, - fieldInputWidth, - ariaLabel, - ]); - - if (!isSuggestingValues && selectedField != null) { - switch (selectedField.type) { - case 'number': - return ( - <EuiFormRow - label={rowLabel} - error={error} - isInvalid={selectedField != null && error != null} - data-test-subj="valuesAutocompleteMatchLabel" - fullWidth - > - <EuiFieldNumber - placeholder={inputPlaceholder} - onBlur={setIsTouchedValue} - value={ - typeof selectedValue === 'string' && selectedValue.trim().length > 0 - ? parseFloat(selectedValue) - : selectedValue ?? '' - } - onChange={handleNonComboBoxInputChange} - data-test-subj="valueAutocompleteFieldMatchNumber" - style={fieldInputWidth ? { width: `${fieldInputWidth}px` } : {}} - aria-label={ariaLabel} - fullWidth - /> - </EuiFormRow> - ); - case 'boolean': - return ( - <EuiFormRow - label={rowLabel} - error={error} - isInvalid={selectedField != null && error != null} - data-test-subj="valuesAutocompleteMatchLabel" - fullWidth - > - <EuiSuperSelect - isLoading={isLoadingState} - options={BOOLEAN_OPTIONS} - valueOfSelected={selectedValue ?? 'true'} - onChange={handleBooleanInputChange} - data-test-subj="valuesAutocompleteMatchBoolean" - style={fieldInputWidth ? { width: `${fieldInputWidth}px` } : {}} - aria-label={ariaLabel} - fullWidth - /> - </EuiFormRow> - ); - default: - return defaultInput; - } - } else { - return defaultInput; - } -}; - -AutocompleteFieldMatchComponent.displayName = 'AutocompleteFieldMatch'; diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.test.tsx deleted file mode 100644 index 0b73a57814aa..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.test.tsx +++ /dev/null @@ -1,400 +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 { ReactWrapper, mount } from 'enzyme'; -import { EuiComboBox, EuiComboBoxOptionOption, EuiFormHelpText } from '@elastic/eui'; -import { act, waitFor } from '@testing-library/react'; - -import { AutocompleteFieldMatchAnyComponent } from '.'; -import { getField, fields } from '../fields/index.mock'; -import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; -import { autocompleteStartMock } from '../autocomplete/index.mock'; - -jest.mock('../hooks/use_field_value_autocomplete', () => { - const actual = jest.requireActual('../hooks/use_field_value_autocomplete'); - return { - ...actual, - useFieldValueAutocomplete: jest.fn(), - }; -}); -jest.mock('../translations', () => ({ - FIELD_SPACE_WARNING: 'Warning: there is a space', -})); - -describe('AutocompleteFieldMatchAnyComponent', () => { - let wrapper: ReactWrapper; - const getValueSuggestionsMock = jest - .fn() - .mockResolvedValue([false, true, ['value 3', 'value 4'], jest.fn()]); - - beforeEach(() => { - (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ - false, - true, - ['value 1', 'value 2'], - getValueSuggestionsMock, - ]); - }); - - afterEach(() => { - jest.clearAllMocks(); - wrapper.unmount(); - }); - - test('it renders disabled if "isDisabled" is true', () => { - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={true} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('ip')} - selectedValue={['127.0.0.1']} - /> - ); - - expect( - wrapper.find(`[data-test-subj="valuesAutocompleteMatchAny"] input`).prop('disabled') - ).toBeTruthy(); - }); - - test('it renders loading if "isLoading" is true', () => { - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={true} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('ip')} - selectedValue={[]} - /> - ); - wrapper.find(`[data-test-subj="valuesAutocompleteMatchAny"] button`).at(0).simulate('click'); - expect( - wrapper - .find(`EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteMatchAny-optionsList"]`) - .prop('isLoading') - ).toBeTruthy(); - }); - - test('it allows user to clear values if "isClearable" is true', () => { - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={true} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('ip')} - selectedValue={['127.0.0.1']} - /> - ); - - expect(wrapper.find(`[data-test-subj="comboBoxClearButton"]`)).toBeTruthy(); - }); - - test('it correctly displays selected value', () => { - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('ip')} - selectedValue={['127.0.0.1']} - /> - ); - - expect( - wrapper.find(`[data-test-subj="valuesAutocompleteMatchAny"] EuiComboBoxPill`).at(0).text() - ).toEqual('127.0.0.1'); - }); - - test('it invokes "onChange" when new value created', async () => { - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('ip')} - selectedValue={[]} - /> - ); - - ( - wrapper.find(EuiComboBox).props() as unknown as { - onCreateOption: (a: string) => void; - } - ).onCreateOption('127.0.0.1'); - - expect(mockOnChange).toHaveBeenCalledWith(['127.0.0.1']); - }); - - test('it invokes "onChange" when new value selected', async () => { - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isLoading={false} - isClearable={false} - isDisabled={false} - onChange={mockOnChange} - onError={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('machine.os.raw')} - selectedValue={[]} - /> - ); - - ( - wrapper.find(EuiComboBox).props() as unknown as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - } - ).onChange([{ label: 'value 1' }]); - - expect(mockOnChange).toHaveBeenCalledWith(['value 1']); - }); - - test('it refreshes autocomplete with search query when new value searched', () => { - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('machine.os.raw')} - selectedValue={[]} - /> - ); - act(() => { - ( - wrapper.find(EuiComboBox).props() as unknown as { - onSearchChange: (a: string) => void; - } - ).onSearchChange('value 1'); - }); - expect(useFieldValueAutocomplete).toHaveBeenCalledWith({ - autocompleteService: autocompleteStartMock, - fieldValue: [], - indexPattern: { - fields, - id: '1234', - title: 'logstash-*', - }, - operatorType: 'match_any', - query: 'value 1', - selectedField: getField('machine.os.raw'), - }); - }); - test('should show the warning helper text if the new value contains spaces when change', async () => { - (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ - false, - true, - [' value 1 ', 'value 2'], - getValueSuggestionsMock, - ]); - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('machine.os.raw')} - selectedValue={[]} - /> - ); - - await waitFor(() => - ( - wrapper.find(EuiComboBox).props() as unknown as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - } - ).onChange([{ label: ' value 1 ' }]) - ); - wrapper.update(); - expect(mockOnChange).toHaveBeenCalledWith([' value 1 ']); - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeTruthy(); - }); - test('should show the warning helper text if the new value contains spaces when searching a new query', () => { - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('machine.os.raw')} - selectedValue={[]} - /> - ); - act(() => { - ( - wrapper.find(EuiComboBox).props() as unknown as { - onSearchChange: (a: string) => void; - } - ).onSearchChange(' value 1'); - }); - - wrapper.update(); - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeTruthy(); - expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); - }); - test('should show the warning helper text if selectedValue contains spaces when editing', () => { - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('machine.os.raw')} - selectedValue={['value with trailing space ', 'value 1']} - /> - ); - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeTruthy(); - expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); - }); - test('should not show the warning helper text if selectedValue is falsy', () => { - wrapper = mount( - <AutocompleteFieldMatchAnyComponent - autocompleteService={{ - ...autocompleteStartMock, - }} - indexPattern={{ - fields, - id: '1234', - title: 'logstash-*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('machine.os.raw')} - selectedValue={[]} - /> - ); - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeFalsy(); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.tsx deleted file mode 100644 index 761712797c5e..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.tsx +++ /dev/null @@ -1,253 +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, { useCallback, useEffect, useMemo, useState } from 'react'; -import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; -import { uniq } from 'lodash'; -import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; - -// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 -// import { AutocompleteStart } from '../../../../../../../src/plugins/unified_search/public'; -type AutocompleteStart = any; - -import * as i18n from '../translations'; -import { - getGenericComboBoxProps, - GetGenericComboBoxPropsReturn, -} from '../get_generic_combo_box_props'; -import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; -import { paramIsValid } from '../param_is_valid'; -import { paramContainsSpace } from '../param_contains_space'; - -interface AutocompleteFieldMatchAnyProps { - placeholder: string; - selectedField: DataViewFieldBase | undefined; - selectedValue: string[]; - indexPattern: DataViewBase | undefined; - isLoading: boolean; - isDisabled: boolean; - isClearable: boolean; - isRequired?: boolean; - rowLabel?: string; - autocompleteService: AutocompleteStart; - onChange: (arg: string[]) => void; - onError?: (arg: boolean) => void; - 'aria-label'?: string; -} - -export const AutocompleteFieldMatchAnyComponent: React.FC<AutocompleteFieldMatchAnyProps> = ({ - placeholder, - rowLabel, - selectedField, - selectedValue, - indexPattern, - isLoading, - isDisabled = false, - isClearable = false, - isRequired = false, - onChange, - onError, - autocompleteService, - 'aria-label': ariaLabel, -}): JSX.Element => { - const [searchQuery, setSearchQuery] = useState(''); - const [touched, setIsTouched] = useState(false); - const [error, setError] = useState<string | undefined>(undefined); - const [showSpacesWarning, setShowSpacesWarning] = useState<boolean>(false); - const [isLoadingSuggestions, isSuggestingValues, suggestions] = useFieldValueAutocomplete({ - autocompleteService, - fieldValue: selectedValue, - indexPattern, - operatorType: OperatorTypeEnum.MATCH_ANY, - query: searchQuery, - selectedField, - }); - const getLabel = useCallback((option: string): string => option, []); - const optionsMemo = useMemo( - (): string[] => (selectedValue ? uniq([...selectedValue, ...suggestions]) : suggestions), - [suggestions, selectedValue] - ); - const { comboOptions, labels, selectedComboOptions } = useMemo( - (): GetGenericComboBoxPropsReturn => - getGenericComboBoxProps<string>({ - getLabel, - options: optionsMemo, - selectedOptions: selectedValue, - }), - [optionsMemo, selectedValue, getLabel] - ); - const handleSpacesWarning = useCallback( - (params: string[]) => - setShowSpacesWarning(!!params.find((param: string) => paramContainsSpace(param))), - [setShowSpacesWarning] - ); - const handleError = useCallback( - (err: string | undefined): void => { - setError((existingErr): string | undefined => { - const oldErr = existingErr != null; - const newErr = err != null; - if (oldErr !== newErr && onError != null) { - onError(newErr); - } - - return err; - }); - }, - [setError, onError] - ); - - const handleValuesChange = useCallback( - (newOptions: EuiComboBoxOptionOption[]): void => { - const newValues: string[] = newOptions.map(({ label }) => optionsMemo[labels.indexOf(label)]); - handleError(undefined); - handleSpacesWarning(newValues); - onChange(newValues); - }, - [handleError, handleSpacesWarning, labels, onChange, optionsMemo] - ); - - const handleSearchChange = useCallback( - (searchVal: string) => { - if (searchVal === '') { - handleError(undefined); - } - - if (searchVal !== '' && selectedField != null) { - const err = paramIsValid(searchVal, selectedField, isRequired, touched); - handleError(err); - - if (!err) handleSpacesWarning([searchVal]); - - setSearchQuery(searchVal); - } - }, - [handleError, handleSpacesWarning, isRequired, selectedField, touched] - ); - - const handleCreateOption = useCallback( - (option: string): boolean => { - const err = paramIsValid(option, selectedField, isRequired, touched); - handleError(err); - - if (err != null) { - // Explicitly reject the user's input - setShowSpacesWarning(false); - return false; - } - - onChange([...(selectedValue || []), option]); - handleSpacesWarning([option]); - return true; - }, - [handleError, handleSpacesWarning, isRequired, onChange, selectedField, selectedValue, touched] - ); - - const setIsTouchedValue = useCallback((): void => { - handleError(selectedComboOptions.length === 0 ? i18n.FIELD_REQUIRED_ERR : undefined); - setIsTouched(true); - }, [setIsTouched, handleError, selectedComboOptions]); - - const inputPlaceholder = useMemo( - (): string => (isLoading || isLoadingSuggestions ? i18n.LOADING : placeholder), - [isLoading, isLoadingSuggestions, placeholder] - ); - - const isLoadingState = useMemo( - (): boolean => isLoading || isLoadingSuggestions, - [isLoading, isLoadingSuggestions] - ); - useEffect((): void => { - handleSpacesWarning(selectedValue); - }, [selectedField, selectedValue, handleSpacesWarning]); - - const defaultInput = useMemo((): JSX.Element => { - return ( - <EuiFormRow - label={rowLabel} - error={error} - isInvalid={selectedField != null && error != null} - helpText={showSpacesWarning && i18n.FIELD_SPACE_WARNING} - fullWidth - > - <EuiComboBox - placeholder={inputPlaceholder} - isLoading={isLoadingState} - isClearable={isClearable} - isDisabled={isDisabled} - options={comboOptions} - selectedOptions={selectedComboOptions} - onChange={handleValuesChange} - onSearchChange={handleSearchChange} - onCreateOption={handleCreateOption} - isInvalid={selectedField != null && error != null} - isCaseSensitive - onBlur={setIsTouchedValue} - data-test-subj="valuesAutocompleteMatchAny" - aria-label={ariaLabel} - fullWidth - async - /> - </EuiFormRow> - ); - }, [ - rowLabel, - error, - selectedField, - showSpacesWarning, - inputPlaceholder, - isLoadingState, - isClearable, - isDisabled, - comboOptions, - selectedComboOptions, - handleValuesChange, - handleSearchChange, - handleCreateOption, - setIsTouchedValue, - ariaLabel, - ]); - - if (!isSuggestingValues && selectedField != null) { - switch (selectedField.type) { - case 'number': - return ( - <EuiFormRow - label={rowLabel} - error={error} - isInvalid={selectedField != null && error != null} - fullWidth - > - <EuiComboBox - noSuggestions - placeholder={inputPlaceholder} - isLoading={isLoadingState} - isClearable={isClearable} - isDisabled={isDisabled} - selectedOptions={selectedComboOptions} - onChange={handleValuesChange} - onSearchChange={handleSearchChange} - onCreateOption={handleCreateOption} - isInvalid={selectedField != null && error != null} - onFocus={setIsTouchedValue} - data-test-subj="valuesAutocompleteMatchAnyNumber" - aria-label={ariaLabel} - fullWidth - /> - </EuiFormRow> - ); - default: - return defaultInput; - } - } - - return defaultInput; -}; - -AutocompleteFieldMatchAnyComponent.displayName = 'AutocompleteFieldMatchAny'; diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx deleted file mode 100644 index 2840e428a69e..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx +++ /dev/null @@ -1,516 +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 { ReactWrapper, mount } from 'enzyme'; -import { EuiComboBox, EuiComboBoxOptionOption, EuiFormHelpText } from '@elastic/eui'; -import { act, waitFor } from '@testing-library/react'; -import { AutocompleteFieldWildcardComponent } from '.'; -import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; -import { fields, getField } from '../fields/index.mock'; -import { autocompleteStartMock } from '../autocomplete/index.mock'; -import { WILDCARD_WARNING, FILEPATH_WARNING } from '@kbn/securitysolution-utils'; - -jest.mock('../hooks/use_field_value_autocomplete'); -jest.mock('../translations', () => ({ - FIELD_SPACE_WARNING: 'Warning: there is a space', -})); -describe('AutocompleteFieldWildcardComponent', () => { - let wrapper: ReactWrapper; - - const getValueSuggestionsMock = jest - .fn() - .mockResolvedValue([false, true, ['value 3', 'value 4'], jest.fn()]); - - beforeEach(() => { - (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ - false, - true, - ['value 1', 'value 2'], - getValueSuggestionsMock, - ]); - }); - - afterEach(() => { - jest.clearAllMocks(); - wrapper.unmount(); - }); - - test('it renders row label if one passed in', () => { - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - rowLabel={'Row Label'} - selectedField={getField('file.path.text')} - selectedValue="/opt/bin/app.dmg" - /> - ); - - expect( - wrapper.find('[data-test-subj="valuesAutocompleteWildcardLabel"] label').at(0).text() - ).toEqual('Row Label'); - }); - - test('it renders disabled if "isDisabled" is true', () => { - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="/opt/*/app.dmg" - /> - ); - - expect( - wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] input').prop('disabled') - ).toBeTruthy(); - }); - - test('it renders loading if "isLoading" is true', () => { - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading - onChange={jest.fn()} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="/opt/*/app.dmg" - /> - ); - wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] button').at(0).simulate('click'); - expect( - wrapper - .find('EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteWildcard-optionsList"]') - .prop('isLoading') - ).toBeTruthy(); - }); - - test('it allows user to clear values if "isClearable" is true', () => { - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={true} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="/opt/*/app.dmg" - /> - ); - - expect(wrapper.find(`[data-test-subj="comboBoxClearButton"]`)).toBeTruthy(); - }); - - test('it correctly displays selected value', () => { - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="/opt/*/app.dmg" - /> - ); - - expect( - wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] input').at(0).props().value - ).toEqual('/opt/*/app.dmg'); - }); - - test('it invokes "onChange" when new value created', async () => { - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="" - /> - ); - - ( - wrapper.find(EuiComboBox).props() as unknown as { - onCreateOption: (a: string) => void; - } - ).onCreateOption('/opt/*/app.dmg'); - - expect(mockOnChange).toHaveBeenCalledWith('/opt/*/app.dmg'); - }); - - test('it invokes "onChange" when new value selected', async () => { - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="" - /> - ); - - ( - wrapper.find(EuiComboBox).props() as unknown as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - } - ).onChange([{ label: 'value 1' }]); - - expect(mockOnChange).toHaveBeenCalledWith('value 1'); - }); - - test('it refreshes autocomplete with search query when new value searched', () => { - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="" - /> - ); - act(() => { - ( - wrapper.find(EuiComboBox).props() as unknown as { - onSearchChange: (a: string) => void; - } - ).onSearchChange('A:\\Some Folder\\inc*.exe'); - }); - - expect(useFieldValueAutocomplete).toHaveBeenCalledWith({ - autocompleteService: autocompleteStartMock, - fieldValue: '', - indexPattern: { - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }, - operatorType: 'wildcard', - query: 'A:\\Some Folder\\inc*.exe', - selectedField: getField('file.path.text'), - }); - }); - - test('it does not invoke "onWarning" when no warning exists', () => { - const mockOnWarning = jest.fn(); - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={mockOnWarning} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="invalid path" - /> - ); - - act(() => { - ( - wrapper.find(EuiComboBox).props() as unknown as { - onBlur: () => void; - } - ).onBlur(); - }); - - expect(mockOnWarning).not.toHaveBeenCalledWith(true); - }); - - test('it invokes "onWarning" when warning exists', () => { - const mockOnWarning = jest.fn(); - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={mockOnWarning} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="invalid path" - warning={FILEPATH_WARNING} - /> - ); - - act(() => { - ( - wrapper.find(EuiComboBox).props() as unknown as { - onBlur: () => void; - } - ).onBlur(); - }); - - expect(mockOnWarning).toHaveBeenCalledWith(true); - expect( - wrapper - .find('[data-test-subj="valuesAutocompleteWildcardLabel"] div.euiFormHelpText') - .at(0) - .text() - ).toEqual(FILEPATH_WARNING); - }); - - test('it invokes "onWarning" when warning exists and is wildcard warning', () => { - const mockOnWarning = jest.fn(); - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={mockOnWarning} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="invalid path" - warning={WILDCARD_WARNING} - /> - ); - - act(() => { - ( - wrapper.find(EuiComboBox).props() as unknown as { - onBlur: () => void; - } - ).onBlur(); - }); - - expect(mockOnWarning).toHaveBeenCalledWith(true); - const helpText = wrapper - .find('[data-test-subj="valuesAutocompleteWildcardLabel"] div.euiFormHelpText') - .at(0); - expect(helpText.text()).toEqual(WILDCARD_WARNING); - expect(helpText.find('.euiToolTipAnchor')).toBeTruthy(); - }); - test('should show the warning helper text if the new value contains spaces when change', async () => { - (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ - false, - true, - [' value 1 ', 'value 2'], - getValueSuggestionsMock, - ]); - const mockOnChange = jest.fn(); - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="invalid path" - warning={WILDCARD_WARNING} - /> - ); - - await waitFor(() => - ( - wrapper.find(EuiComboBox).props() as unknown as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - } - ).onChange([{ label: ' value 1 ' }]) - ); - wrapper.update(); - expect(mockOnChange).toHaveBeenCalledWith(' value 1 '); - - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeTruthy(); - }); - test('should show the warning helper text if the new value contains spaces when searching a new query', () => { - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="invalid path" - warning={''} - /> - ); - act(() => { - ( - wrapper.find(EuiComboBox).props() as unknown as { - onSearchChange: (a: string) => void; - } - ).onSearchChange(' value 1'); - }); - - wrapper.update(); - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeTruthy(); - expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); - }); - test('should show the warning helper text if selectedValue contains spaces when editing', () => { - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue=" leading space" - warning={''} - /> - ); - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeTruthy(); - expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); - }); - test('should not show the warning helper text if selectedValue is falsy', () => { - wrapper = mount( - <AutocompleteFieldWildcardComponent - autocompleteService={autocompleteStartMock} - indexPattern={{ - fields, - id: '1234', - title: 'logs-endpoint.events.*', - }} - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - onError={jest.fn()} - onWarning={jest.fn()} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - selectedValue="" - warning={''} - /> - ); - const euiFormHelptext = wrapper.find(EuiFormHelpText); - expect(euiFormHelptext.length).toBeFalsy(); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.tsx deleted file mode 100644 index 5a55b3399a6a..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.tsx +++ /dev/null @@ -1,286 +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, { useCallback, useMemo, useState, useEffect, memo } from 'react'; -import { EuiFormRow, EuiComboBoxOptionOption, EuiComboBox } from '@elastic/eui'; -import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; - -import { uniq } from 'lodash'; - -import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; - -// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 -// import { AutocompleteStart } from '../../../../../../../src/plugins/unified_search/public'; -type AutocompleteStart = any; - -import * as i18n from '../translations'; -import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; -import { - getGenericComboBoxProps, - GetGenericComboBoxPropsReturn, -} from '../get_generic_combo_box_props'; -import { paramIsValid } from '../param_is_valid'; -import { paramContainsSpace } from '../param_contains_space'; - -const SINGLE_SELECTION = { asPlainText: true }; - -type Warning = string | React.ReactNode; -interface AutocompleteFieldWildcardProps { - placeholder: string; - selectedField: DataViewFieldBase | undefined; - selectedValue: string | undefined; - indexPattern: DataViewBase | undefined; - isLoading: boolean; - isDisabled?: boolean; - isClearable?: boolean; - isRequired?: boolean; - fieldInputWidth?: number; - rowLabel?: string; - autocompleteService: AutocompleteStart; - onChange: (arg: string) => void; - onError: (arg: boolean) => void; - onWarning: (arg: boolean) => void; - warning?: Warning; - 'aria-label'?: string; -} - -export const AutocompleteFieldWildcardComponent: React.FC<AutocompleteFieldWildcardProps> = memo( - ({ - autocompleteService, - placeholder, - rowLabel, - selectedField, - selectedValue, - indexPattern, - isLoading, - isDisabled = false, - isClearable = false, - isRequired = false, - fieldInputWidth, - onChange, - onError, - onWarning, - warning, - 'aria-label': ariaLabel, - }): JSX.Element => { - const [searchQuery, setSearchQuery] = useState(''); - const [touched, setIsTouched] = useState(false); - const [error, setError] = useState<string | undefined>(undefined); - const [showSpacesWarning, setShowSpacesWarning] = useState<boolean>(false); - const [isLoadingSuggestions, , suggestions] = useFieldValueAutocomplete({ - autocompleteService, - fieldValue: selectedValue, - indexPattern, - operatorType: OperatorTypeEnum.WILDCARD, - query: searchQuery, - selectedField, - }); - const getLabel = useCallback((option: string): string => option, []); - const optionsMemo = useMemo((): string[] => { - const valueAsStr = String(selectedValue); - return selectedValue != null && selectedValue.trim() !== '' - ? uniq([valueAsStr, ...suggestions]) - : suggestions; - }, [suggestions, selectedValue]); - const selectedOptionsMemo = useMemo((): string[] => { - const valueAsStr = String(selectedValue); - return selectedValue ? [valueAsStr] : []; - }, [selectedValue]); - - const handleSpacesWarning = useCallback( - (param: string | undefined) => { - if (!param) return setShowSpacesWarning(false); - setShowSpacesWarning(!!paramContainsSpace(param)); - }, - [setShowSpacesWarning] - ); - const handleError = useCallback( - (err: string | undefined): void => { - setError((existingErr): string | undefined => { - const oldErr = existingErr != null; - const newErr = err != null; - if (oldErr !== newErr && onError != null) { - onError(newErr); - } - - return err; - }); - }, - [setError, onError] - ); - - const handleWarning = useCallback( - (warn: Warning | undefined): void => { - onWarning(warn !== undefined); - }, - [onWarning] - ); - - const { comboOptions, labels, selectedComboOptions } = useMemo( - (): GetGenericComboBoxPropsReturn => - getGenericComboBoxProps<string>({ - getLabel, - options: optionsMemo, - selectedOptions: selectedOptionsMemo, - }), - [optionsMemo, selectedOptionsMemo, getLabel] - ); - - const handleValuesChange = useCallback( - (newOptions: EuiComboBoxOptionOption[]): void => { - const [newValue] = newOptions.map(({ label }) => optionsMemo[labels.indexOf(label)]); - handleError(undefined); - handleSpacesWarning(newValue); - setShowSpacesWarning(false); - - onChange(newValue ?? ''); - }, - [handleError, handleSpacesWarning, labels, onChange, optionsMemo] - ); - - const handleSearchChange = useCallback( - (searchVal: string): void => { - if (searchVal.trim() !== '' && selectedField != null) { - const err = paramIsValid(searchVal, selectedField, isRequired, touched); - handleError(err); - handleWarning(warning); - if (!err) handleSpacesWarning(searchVal); - - setSearchQuery(searchVal); - } - }, - [handleError, handleSpacesWarning, isRequired, selectedField, touched, warning, handleWarning] - ); - - const handleCreateOption = useCallback( - (option: string): boolean | undefined => { - const err = paramIsValid(option, selectedField, isRequired, touched); - handleError(err); - handleWarning(warning); - - if (err != null) { - // Explicitly reject the user's input - setShowSpacesWarning(false); - return false; - } - - handleSpacesWarning(option); - onChange(option); - return undefined; - }, - [ - isRequired, - handleSpacesWarning, - onChange, - selectedField, - touched, - handleError, - handleWarning, - warning, - ] - ); - - const setIsTouchedValue = useCallback((): void => { - setIsTouched(true); - - const err = paramIsValid(selectedValue, selectedField, isRequired, true); - handleError(err); - handleWarning(warning); - }, [ - setIsTouched, - handleError, - selectedValue, - selectedField, - isRequired, - handleWarning, - warning, - ]); - - const inputPlaceholder = useMemo((): string => { - if (isLoading || isLoadingSuggestions) { - return i18n.LOADING; - } else if (selectedField == null) { - return i18n.SELECT_FIELD_FIRST; - } else { - return placeholder; - } - }, [isLoading, selectedField, isLoadingSuggestions, placeholder]); - - const isLoadingState = useMemo( - (): boolean => isLoading || isLoadingSuggestions, - [isLoading, isLoadingSuggestions] - ); - - useEffect((): void => { - setError(undefined); - if (onError != null) { - onError(false); - } - handleSpacesWarning(selectedValue); - - onWarning(false); - }, [selectedField, selectedValue, onError, onWarning, handleSpacesWarning]); - - const defaultInput = useMemo((): JSX.Element => { - return ( - <EuiFormRow - label={rowLabel} - error={error} - helpText={warning || (showSpacesWarning && i18n.FIELD_SPACE_WARNING)} - isInvalid={selectedField != null && error != null} - data-test-subj="valuesAutocompleteWildcardLabel" - fullWidth - > - <EuiComboBox - placeholder={inputPlaceholder} - isDisabled={isDisabled || !selectedField} - isLoading={isLoadingState} - isClearable={isClearable} - options={comboOptions} - selectedOptions={selectedComboOptions} - onChange={handleValuesChange} - singleSelection={SINGLE_SELECTION} - onSearchChange={handleSearchChange} - onCreateOption={handleCreateOption} - isInvalid={selectedField != null && error != null} - onBlur={setIsTouchedValue} - sortMatchesBy="startsWith" - data-test-subj="valuesAutocompleteWildcard" - style={fieldInputWidth ? { width: `${fieldInputWidth}px` } : {}} - fullWidth - async - aria-label={ariaLabel} - /> - </EuiFormRow> - ); - }, [ - rowLabel, - error, - warning, - showSpacesWarning, - selectedField, - inputPlaceholder, - isDisabled, - isLoadingState, - isClearable, - comboOptions, - selectedComboOptions, - handleValuesChange, - handleSearchChange, - handleCreateOption, - setIsTouchedValue, - fieldInputWidth, - ariaLabel, - ]); - - return defaultInput; - } -); - -AutocompleteFieldWildcardComponent.displayName = 'AutocompleteFieldWildcard'; diff --git a/packages/kbn-securitysolution-autocomplete/src/fields/index.mock.ts b/packages/kbn-securitysolution-autocomplete/src/fields/index.mock.ts deleted file mode 100644 index dcb719ccdb56..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/fields/index.mock.ts +++ /dev/null @@ -1,323 +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 { DataViewFieldBase } from '@kbn/es-query'; - -// Copied from "src/plugins/data/common/index_patterns/fields/fields.mocks.ts" but with the types changed to "DataViewFieldBase" since that type is compatible. -// TODO: This should move out once those mocks are directly useable or in their own package, https://github.com/elastic/kibana/issues/100715 - -export const fields: DataViewFieldBase[] = [ - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'ssl', - type: 'boolean', - esTypes: ['boolean'], - count: 20, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: '@timestamp', - type: 'date', - esTypes: ['date'], - count: 30, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'time', - type: 'date', - esTypes: ['date'], - count: 30, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: '@tags', - type: 'string', - esTypes: ['keyword'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'utc_time', - type: 'date', - esTypes: ['date'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'phpmemory', - type: 'number', - esTypes: ['integer'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'ip', - type: 'ip', - esTypes: ['ip'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'request_body', - type: 'attachment', - esTypes: ['attachment'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'point', - type: 'geo_point', - esTypes: ['geo_point'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'area', - type: 'geo_shape', - esTypes: ['geo_shape'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'hashed', - type: 'murmur3', - esTypes: ['murmur3'], - count: 0, - scripted: false, - searchable: true, - aggregatable: false, - readFromDocValues: false, - }, - { - name: 'geo.coordinates', - type: 'geo_point', - esTypes: ['geo_point'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'extension', - type: 'string', - esTypes: ['keyword'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'machine.os', - type: 'string', - esTypes: ['text'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: false, - }, - { - name: 'machine.os.raw', - type: 'string', - esTypes: ['keyword'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - subType: { multi: { parent: 'machine.os' } }, - }, - { - name: 'geo.src', - type: 'string', - esTypes: ['keyword'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: '_id', - type: 'string', - esTypes: ['_id'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: false, - }, - { - name: '_type', - type: 'string', - esTypes: ['_type'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: false, - }, - { - name: '_source', - type: '_source', - esTypes: ['_source'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: false, - }, - { - name: 'non-filterable', - type: 'string', - esTypes: ['text'], - count: 0, - scripted: false, - searchable: false, - aggregatable: true, - readFromDocValues: false, - }, - { - name: 'non-sortable', - type: 'string', - esTypes: ['text'], - count: 0, - scripted: false, - searchable: false, - aggregatable: false, - readFromDocValues: false, - }, - { - name: 'custom_user_field', - type: 'conflict', - esTypes: ['long', 'text'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'script string', - type: 'string', - count: 0, - scripted: true, - script: "'i am a string'", - lang: 'expression', - searchable: true, - aggregatable: true, - readFromDocValues: false, - }, - { - name: 'script number', - type: 'number', - count: 0, - scripted: true, - script: '1234', - lang: 'expression', - searchable: true, - aggregatable: true, - readFromDocValues: false, - }, - { - name: 'script date', - type: 'date', - count: 0, - scripted: true, - script: '1234', - lang: 'painless', - searchable: true, - aggregatable: true, - readFromDocValues: false, - }, - { - name: 'script murmur3', - type: 'murmur3', - count: 0, - scripted: true, - script: '1234', - lang: 'expression', - searchable: true, - aggregatable: true, - readFromDocValues: false, - }, - { - name: 'nestedField.child', - type: 'string', - esTypes: ['text'], - count: 0, - scripted: false, - searchable: true, - aggregatable: false, - readFromDocValues: false, - subType: { nested: { path: 'nestedField' } }, - }, - { - name: 'nestedField.nestedChild.doublyNestedChild', - type: 'string', - esTypes: ['text'], - count: 0, - scripted: false, - searchable: true, - aggregatable: false, - readFromDocValues: false, - subType: { nested: { path: 'nestedField.nestedChild' } }, - }, - { - name: 'file.path.text', - type: 'string', - esTypes: ['text'], - searchable: true, - aggregatable: false, - subType: { multi: { parent: 'file.path' } }, - }, -] as unknown as DataViewFieldBase[]; - -export const getField = (name: string) => fields.find((field) => field.name === name); diff --git a/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.test.ts b/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.test.ts deleted file mode 100644 index be0710bfd1ba..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.test.ts +++ /dev/null @@ -1,125 +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 { filterFieldToList } from '.'; - -import { getListResponseMock } from '../list_schema/index.mock'; -import { DataViewFieldBase } from '@kbn/es-query'; -import { AutocompleteListsData } from '../field_value_lists'; - -const emptyListData: AutocompleteListsData = { smallLists: [], largeLists: [] }; - -describe('#filterFieldToList', () => { - test('it returns empty list data object if given a undefined for field', () => { - const filter = filterFieldToList(emptyListData, undefined); - expect(filter).toEqual(emptyListData); - }); - - test('it returns empty list data object if filed does not contain esTypes', () => { - const field: DataViewFieldBase = { - name: 'some-name', - type: 'some-type', - }; - const filter = filterFieldToList(emptyListData, field); - expect(filter).toEqual(emptyListData); - }); - - test('it returns filtered lists of ip_range -> ip', () => { - const field: DataViewFieldBase & { esTypes: string[] } = { - esTypes: ['ip'], - name: 'some-name', - type: 'ip', - }; - const listData: AutocompleteListsData = { - smallLists: [{ ...getListResponseMock(), type: 'ip_range' }], - largeLists: [], - }; - const filter = filterFieldToList(listData, field); - const expected = listData; - expect(filter).toEqual(expected); - }); - - test('it returns filtered lists of ip -> ip', () => { - const field: DataViewFieldBase & { esTypes: string[] } = { - esTypes: ['ip'], - name: 'some-name', - type: 'ip', - }; - const listData: AutocompleteListsData = { - smallLists: [{ ...getListResponseMock(), type: 'ip' }], - largeLists: [], - }; - const filter = filterFieldToList(listData, field); - const expected = listData; - expect(filter).toEqual(expected); - }); - - test('it returns filtered lists of keyword -> keyword', () => { - const field: DataViewFieldBase & { esTypes: string[] } = { - esTypes: ['keyword'], - name: 'some-name', - type: 'keyword', - }; - const listData: AutocompleteListsData = { - smallLists: [{ ...getListResponseMock(), type: 'keyword' }], - largeLists: [], - }; - const filter = filterFieldToList(listData, field); - const expected = listData; - expect(filter).toEqual(expected); - }); - - test('it returns filtered lists of text -> text', () => { - const field: DataViewFieldBase & { esTypes: string[] } = { - esTypes: ['text'], - name: 'some-name', - type: 'text', - }; - const listData: AutocompleteListsData = { - smallLists: [{ ...getListResponseMock(), type: 'text' }], - largeLists: [], - }; - const filter = filterFieldToList(listData, field); - const expected = listData; - expect(filter).toEqual(expected); - }); - - test('it returns small and large filtered lists of ip_range -> ip', () => { - const field: DataViewFieldBase & { esTypes: string[] } = { - esTypes: ['ip'], - name: 'some-name', - type: 'ip', - }; - const listData: AutocompleteListsData = { - smallLists: [{ ...getListResponseMock(), type: 'ip_range' }], - largeLists: [{ ...getListResponseMock(), type: 'ip_range' }], - }; - const filter = filterFieldToList(listData, field); - const expected = listData; - expect(filter).toEqual(expected); - }); - - test('it returns 1 filtered lists of ip_range -> ip if the 2nd is not compatible type', () => { - const field: DataViewFieldBase & { esTypes: string[] } = { - esTypes: ['ip'], - name: 'some-name', - type: 'ip', - }; - const listData: AutocompleteListsData = { - smallLists: [{ ...getListResponseMock(), type: 'ip_range' }], - largeLists: [{ ...getListResponseMock(), type: 'text' }], - }; - const filter = filterFieldToList(listData, field); - const expected: AutocompleteListsData = { - smallLists: [{ ...getListResponseMock(), type: 'ip_range' }], - largeLists: [], - }; - expect(filter).toEqual(expected); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.ts b/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.ts deleted file mode 100644 index 86d429cc9ef8..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { DataViewFieldBase } from '@kbn/es-query'; -import { typeMatch } from '../type_match'; -import { AutocompleteListsData } from '../field_value_lists'; - -/** - * Given an array of lists and optionally a field this will return all - * the lists that match against the field based on the types from the field - * - * NOTE: That we support one additional property from "FieldSpec" located here: - * src/plugins/data/common/index_patterns/fields/types.ts - * This type property is esTypes. If it exists and is on there we will read off the esTypes. - * @param lists The lists to match against the field - * @param field The field to check against the list to see if they are compatible - */ -export const filterFieldToList = ( - lists: AutocompleteListsData, - field?: DataViewFieldBase & { esTypes?: string[] } -): AutocompleteListsData => { - if (field != null) { - const { esTypes = [] } = field; - return { - smallLists: lists.smallLists.filter(({ type }) => - esTypes.some((esType: string) => typeMatch(type, esType)) - ), - largeLists: lists.largeLists.filter(({ type }) => - esTypes.some((esType: string) => typeMatch(type, esType)) - ), - }; - } else { - return { smallLists: [], largeLists: [] }; - } -}; diff --git a/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.test.tsx deleted file mode 100644 index 82ca7f60de24..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.test.tsx +++ /dev/null @@ -1,126 +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 { getGenericComboBoxProps } from '.'; - -describe('get_generic_combo_box_props', () => { - test('it returns empty arrays if "options" is empty array', () => { - const result = getGenericComboBoxProps<string>({ - options: [], - selectedOptions: ['option1'], - getLabel: (t: string) => t, - }); - - expect(result).toEqual({ comboOptions: [], labels: [], selectedComboOptions: [] }); - }); - - test('it returns formatted props if "options" array is not empty', () => { - const result = getGenericComboBoxProps<string>({ - options: ['option1', 'option2', 'option3'], - selectedOptions: [], - getLabel: (t: string) => t, - }); - - expect(result).toEqual({ - comboOptions: [ - { - label: 'option1', - }, - { - label: 'option2', - }, - { - label: 'option3', - }, - ], - labels: ['option1', 'option2', 'option3'], - selectedComboOptions: [], - }); - }); - - test('it does not return "selectedOptions" items that do not appear in "options"', () => { - const result = getGenericComboBoxProps<string>({ - options: ['option1', 'option2', 'option3'], - selectedOptions: ['option4'], - getLabel: (t: string) => t, - }); - - expect(result).toEqual({ - comboOptions: [ - { - label: 'option1', - }, - { - label: 'option2', - }, - { - label: 'option3', - }, - ], - labels: ['option1', 'option2', 'option3'], - selectedComboOptions: [], - }); - }); - - test('it returns "selectedOptions" items that do appear in "options"', () => { - const result = getGenericComboBoxProps<string>({ - options: ['option1', 'option2', 'option3'], - selectedOptions: ['option2'], - getLabel: (t: string) => t, - }); - - expect(result).toEqual({ - comboOptions: [ - { - label: 'option1', - }, - { - label: 'option2', - }, - { - label: 'option3', - }, - ], - labels: ['option1', 'option2', 'option3'], - selectedComboOptions: [ - { - label: 'option2', - }, - ], - }); - }); - - test('it returns "disabledOptions" items that do appear in "options" as disabled', () => { - const result = getGenericComboBoxProps<string>({ - options: ['option1', 'option2', 'option3'], - selectedOptions: [], - disabledOptions: ['option2'], - getLabel: (t: string) => t, - }); - - expect(result).toEqual({ - comboOptions: [ - { - label: 'option1', - disabled: false, - }, - { - label: 'option2', - disabled: true, - }, - { - label: 'option3', - disabled: false, - }, - ], - labels: ['option1', 'option2', 'option3'], - selectedComboOptions: [], - }); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.ts b/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.ts deleted file mode 100644 index 2f2a6697c422..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.ts +++ /dev/null @@ -1,55 +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 { EuiComboBoxOptionOption } from '@elastic/eui'; - -export interface GetGenericComboBoxPropsReturn { - comboOptions: EuiComboBoxOptionOption[]; - labels: string[]; - selectedComboOptions: EuiComboBoxOptionOption[]; -} - -/** - * Determines the options, selected values and option labels for EUI combo box - * @param options options user can select from - * @param selectedOptions user selection if any - * @param getLabel helper function to know which property to use for labels - */ -export const getGenericComboBoxProps = <T>({ - getLabel, - options, - selectedOptions, - disabledOptions, -}: { - getLabel: (value: T) => string; - options: T[]; - selectedOptions: T[]; - disabledOptions?: T[]; -}): GetGenericComboBoxPropsReturn => { - const newLabels = options.map(getLabel); - const disabledLabels = disabledOptions?.map(getLabel); - const newComboOptions: EuiComboBoxOptionOption[] = newLabels.map((label) => ({ - label, - disabled: disabledLabels && disabledLabels.length !== 0 && disabledLabels.includes(label), - })); - const newSelectedComboOptions = selectedOptions - .map(getLabel) - .filter((option) => { - return newLabels.indexOf(option) !== -1; - }) - .map((option) => { - return newComboOptions[newLabels.indexOf(option)]; - }); - - return { - comboOptions: newComboOptions, - labels: newLabels, - selectedComboOptions: newSelectedComboOptions, - }; -}; diff --git a/packages/kbn-securitysolution-autocomplete/src/get_operators/index.test.ts b/packages/kbn-securitysolution-autocomplete/src/get_operators/index.test.ts deleted file mode 100644 index 711e7ea87a8e..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/get_operators/index.test.ts +++ /dev/null @@ -1,59 +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 { - doesNotExistOperator, - EVENT_FILTERS_OPERATORS, - ALL_OPERATORS, - existsOperator, - isNotOperator, - isOperator, -} from '@kbn/securitysolution-list-utils'; -import { getOperators } from '.'; -import { getField } from '../fields/index.mock'; - -describe('#getOperators', () => { - test('it returns "isOperator" if passed in field is "undefined"', () => { - const operator = getOperators(undefined); - - expect(operator).toEqual([isOperator]); - }); - - test('it returns expected operators when field type is "boolean"', () => { - const operator = getOperators(getField('ssl')); - - expect(operator).toEqual([isOperator, isNotOperator, existsOperator, doesNotExistOperator]); - }); - - test('it returns "isOperator" when field type is "nested"', () => { - const operator = getOperators({ - name: 'nestedField', - scripted: false, - subType: { nested: { path: 'nestedField' } }, - type: 'nested', - }); - - expect(operator).toEqual([isOperator]); - }); - - test('it includes a "matches" operator when field is "file.path.text"', () => { - const operator = getOperators({ - name: 'file.path.text', - type: 'simple', - }); - - expect(operator).toEqual(EVENT_FILTERS_OPERATORS); - }); - - test('it returns all operator types when field type is not null, boolean, or nested', () => { - const operator = getOperators(getField('machine.os.raw')); - - expect(operator).toEqual(ALL_OPERATORS); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/get_operators/index.ts b/packages/kbn-securitysolution-autocomplete/src/get_operators/index.ts deleted file mode 100644 index dbb36fd479df..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/get_operators/index.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 { DataViewFieldBase } from '@kbn/es-query'; - -import { - ALL_OPERATORS, - EVENT_FILTERS_OPERATORS, - OperatorOption, - doesNotExistOperator, - existsOperator, - isNotOperator, - isOperator, -} from '@kbn/securitysolution-list-utils'; - -/** - * Returns the appropriate operators given a field type - * - * @param field DataViewFieldBase selected field - * - */ -export const getOperators = (field: DataViewFieldBase | undefined): OperatorOption[] => { - if (field == null) { - return [isOperator]; - } else if (field.type === 'boolean') { - return [isOperator, isNotOperator, existsOperator, doesNotExistOperator]; - } else if (field.type === 'nested') { - return [isOperator]; - } else if (field.name === 'file.path.text') { - return EVENT_FILTERS_OPERATORS; - } else { - return ALL_OPERATORS; - } -}; diff --git a/packages/kbn-securitysolution-autocomplete/src/hooks/index.ts b/packages/kbn-securitysolution-autocomplete/src/hooks/index.ts deleted file mode 100644 index 5aa216af4c67..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/hooks/index.ts +++ /dev/null @@ -1,10 +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". - */ - -export * from './use_field_value_autocomplete'; diff --git a/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.test.ts b/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.test.ts deleted file mode 100644 index 836744f3ede2..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.test.ts +++ /dev/null @@ -1,300 +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 { waitFor, renderHook } from '@testing-library/react'; -import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; - -import { UseFieldValueAutocompleteReturn, useFieldValueAutocomplete } from '.'; -import { getField } from '../../fields/index.mock'; -import { autocompleteStartMock } from '../../autocomplete/index.mock'; -import { DataViewFieldBase } from '@kbn/es-query'; - -// Copied from "src/plugins/data/common/index_patterns/index_pattern.stub.ts" -// TODO: Remove this in favor of the above if/when it is ported, https://github.com/elastic/kibana/issues/100715 -export const stubIndexPatternWithFields = { - id: '1234', - title: 'logstash-*', - fields: [ - { - name: 'response', - type: 'number', - esTypes: ['integer'], - aggregatable: true, - filterable: true, - searchable: true, - }, - ], -}; - -describe('use_field_value_autocomplete', () => { - const onErrorMock = jest.fn(); - const getValueSuggestionsMock = jest.fn().mockResolvedValue(['value 1', 'value 2']); - - afterEach(() => { - onErrorMock.mockClear(); - getValueSuggestionsMock.mockClear(); - }); - - test('initializes hook', async () => { - const { result } = renderHook(() => - useFieldValueAutocomplete({ - autocompleteService: { - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }, - fieldValue: '', - indexPattern: undefined, - operatorType: OperatorTypeEnum.MATCH, - query: '', - selectedField: undefined, - }) - ); - await waitFor(() => expect(result.current).toEqual([false, true, [], result.current[3]])); - }); - - test('does not call autocomplete service if "operatorType" is "exists"', async () => { - const { result } = renderHook(() => - useFieldValueAutocomplete({ - autocompleteService: { - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }, - fieldValue: '', - indexPattern: stubIndexPatternWithFields, - operatorType: OperatorTypeEnum.EXISTS, - query: '', - selectedField: getField('machine.os'), - }) - ); - - await waitFor(() => { - const expectedResult: UseFieldValueAutocompleteReturn = [false, true, [], result.current[3]]; - - expect(result.current).toEqual(expectedResult); - expect(getValueSuggestionsMock).not.toHaveBeenCalled(); - }); - }); - - test('does not call autocomplete service if "selectedField" is undefined', async () => { - const { result } = renderHook(() => - useFieldValueAutocomplete({ - autocompleteService: { - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }, - fieldValue: '', - indexPattern: stubIndexPatternWithFields, - operatorType: OperatorTypeEnum.EXISTS, - query: '', - selectedField: undefined, - }) - ); - - await waitFor(() => { - const expectedResult: UseFieldValueAutocompleteReturn = [false, true, [], result.current[3]]; - - expect(result.current).toEqual(expectedResult); - expect(getValueSuggestionsMock).not.toHaveBeenCalled(); - }); - }); - - test('does not call autocomplete service if "indexPattern" is undefined', async () => { - const { result } = renderHook(() => - useFieldValueAutocomplete({ - autocompleteService: { - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }, - fieldValue: '', - indexPattern: undefined, - operatorType: OperatorTypeEnum.EXISTS, - query: '', - selectedField: getField('machine.os'), - }) - ); - - await waitFor(() => { - const expectedResult: UseFieldValueAutocompleteReturn = [false, true, [], result.current[3]]; - - expect(result.current).toEqual(expectedResult); - expect(getValueSuggestionsMock).not.toHaveBeenCalled(); - }); - }); - - test('it uses full path name for nested fields to fetch suggestions', async () => { - const suggestionsMock = jest.fn().mockResolvedValue([]); - - const selectedField: DataViewFieldBase | undefined = getField('nestedField.child'); - if (selectedField == null) { - throw new TypeError('selectedField for this test should always be defined'); - } - - const { signal } = new AbortController(); - renderHook(() => - useFieldValueAutocomplete({ - autocompleteService: { - ...autocompleteStartMock, - getValueSuggestions: suggestionsMock, - }, - fieldValue: '', - indexPattern: stubIndexPatternWithFields, - operatorType: OperatorTypeEnum.MATCH, - query: '', - selectedField: { ...selectedField, name: 'child' }, - }) - ); - - await waitFor(() => - expect(suggestionsMock).toHaveBeenCalledWith({ - field: { ...getField('nestedField.child'), name: 'nestedField.child' }, - indexPattern: { - fields: [ - { - aggregatable: true, - esTypes: ['integer'], - filterable: true, - name: 'response', - searchable: true, - type: 'number', - }, - ], - id: '1234', - title: 'logstash-*', - }, - query: '', - signal, - useTimeRange: false, - }) - ); - }); - - test('returns "isSuggestingValues" of false if field type is boolean', async () => { - const { result } = renderHook(() => - useFieldValueAutocomplete({ - autocompleteService: { - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }, - fieldValue: '', - indexPattern: stubIndexPatternWithFields, - operatorType: OperatorTypeEnum.MATCH, - query: '', - selectedField: getField('ssl'), - }) - ); - - await waitFor(() => { - const expectedResult: UseFieldValueAutocompleteReturn = [false, false, [], result.current[3]]; - - expect(result.current).toEqual(expectedResult); - expect(getValueSuggestionsMock).not.toHaveBeenCalled(); - }); - }); - - test('returns "isSuggestingValues" of false to note that autocomplete service is not in use if no autocomplete suggestions available', async () => { - const suggestionsMock = jest.fn().mockResolvedValue([]); - - const { result } = renderHook(() => - useFieldValueAutocomplete({ - autocompleteService: { - ...autocompleteStartMock, - getValueSuggestions: suggestionsMock, - }, - fieldValue: '', - indexPattern: stubIndexPatternWithFields, - operatorType: OperatorTypeEnum.MATCH, - query: '', - selectedField: getField('bytes'), - }) - ); - - await waitFor(() => { - const expectedResult: UseFieldValueAutocompleteReturn = [false, false, [], result.current[3]]; - - expect(suggestionsMock).toHaveBeenCalled(); - expect(result.current).toEqual(expectedResult); - }); - }); - - test('returns suggestions', async () => { - const { signal } = new AbortController(); - const { result } = renderHook(() => - useFieldValueAutocomplete({ - autocompleteService: { - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }, - fieldValue: '', - indexPattern: stubIndexPatternWithFields, - operatorType: OperatorTypeEnum.MATCH, - query: '', - selectedField: getField('@tags'), - }) - ); - - await waitFor(() => { - const expectedResult: UseFieldValueAutocompleteReturn = [ - false, - true, - ['value 1', 'value 2'], - result.current[3], - ]; - - expect(getValueSuggestionsMock).toHaveBeenCalledWith({ - field: getField('@tags'), - indexPattern: stubIndexPatternWithFields, - query: '', - signal, - useTimeRange: false, - }); - expect(result.current).toEqual(expectedResult); - }); - }); - - test('returns new suggestions on subsequent calls', async () => { - const { result } = renderHook(() => - useFieldValueAutocomplete({ - autocompleteService: { - ...autocompleteStartMock, - getValueSuggestions: getValueSuggestionsMock, - }, - fieldValue: '', - indexPattern: stubIndexPatternWithFields, - operatorType: OperatorTypeEnum.MATCH, - query: '', - selectedField: getField('@tags'), - }) - ); - - await waitFor(() => expect(result.current[3]).not.toBeNull()); - - // Added check for typescripts sake, if null, - // would not reach below logic as test would stop above - if (result.current[3] != null) { - result.current[3]({ - fieldSelected: getField('@tags'), - patterns: stubIndexPatternWithFields, - searchQuery: '', - value: 'hello', - }); - } - - await waitFor(() => { - const expectedResult: UseFieldValueAutocompleteReturn = [ - false, - true, - ['value 1', 'value 2'], - result.current[3], - ]; - - expect(getValueSuggestionsMock).toHaveBeenCalledTimes(2); - expect(result.current).toEqual(expectedResult); - }); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.ts b/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.ts deleted file mode 100644 index 9fff2abfd5be..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.ts +++ /dev/null @@ -1,123 +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 { useEffect, useRef, useState } from 'react'; -import { debounce } from 'lodash'; -import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import { DataViewBase, DataViewFieldBase, getDataViewFieldSubtypeNested } from '@kbn/es-query'; - -// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 -// import { AutocompleteStart } from '../../../../../../../../src/plugins/unified_search/public'; -type AutocompleteStart = any; - -interface FuncArgs { - fieldSelected: DataViewFieldBase | undefined; - patterns: DataViewBase | undefined; - searchQuery: string; - value: string | string[] | undefined; -} - -type Func = (args: FuncArgs) => void; - -export type UseFieldValueAutocompleteReturn = [boolean, boolean, string[], Func | null]; - -export interface UseFieldValueAutocompleteProps { - autocompleteService: AutocompleteStart; - fieldValue: string | string[] | undefined; - indexPattern: DataViewBase | undefined; - operatorType: OperatorTypeEnum; - query: string; - selectedField: DataViewFieldBase | undefined; -} -/** - * Hook for using the field value autocomplete service - */ -export const useFieldValueAutocomplete = ({ - selectedField, - operatorType, - fieldValue, - query, - indexPattern, - autocompleteService, -}: UseFieldValueAutocompleteProps): UseFieldValueAutocompleteReturn => { - const [isLoading, setIsLoading] = useState(false); - const [isSuggestingValues, setIsSuggestingValues] = useState(true); - const [suggestions, setSuggestions] = useState<string[]>([]); - const updateSuggestions = useRef<Func | null>(null); - - useEffect(() => { - let isSubscribed = true; - const abortCtrl = new AbortController(); - - const fetchSuggestions = debounce( - async ({ fieldSelected, patterns, searchQuery }: FuncArgs) => { - try { - if (isSubscribed) { - if (fieldSelected == null || patterns == null) { - return; - } - - if (fieldSelected.type === 'boolean') { - setIsSuggestingValues(false); - return; - } - - setIsLoading(true); - const subTypeNested = getDataViewFieldSubtypeNested(fieldSelected); - const field = subTypeNested - ? { - ...fieldSelected, - name: `${subTypeNested.nested.path}.${fieldSelected.name}`, - } - : fieldSelected; - - const newSuggestions = await autocompleteService.getValueSuggestions({ - field, - indexPattern: patterns, - query: searchQuery, - signal: abortCtrl.signal, - useTimeRange: false, - }); - - if (newSuggestions.length === 0) { - setIsSuggestingValues(false); - } - - setIsLoading(false); - setSuggestions([...newSuggestions]); - } - } catch (error) { - if (isSubscribed) { - setSuggestions([]); - setIsLoading(false); - } - } - }, - 500 - ); - - if (operatorType !== OperatorTypeEnum.EXISTS) { - fetchSuggestions({ - fieldSelected: selectedField, - patterns: indexPattern, - searchQuery: query, - value: fieldValue, - }); - } - - updateSuggestions.current = fetchSuggestions; - - return (): void => { - isSubscribed = false; - abortCtrl.abort(); - }; - }, [selectedField, operatorType, fieldValue, indexPattern, query, autocompleteService]); - - return [isLoading, isSuggestingValues, suggestions, updateSuggestions.current]; -}; diff --git a/packages/kbn-securitysolution-autocomplete/src/list_schema/index.mock.ts b/packages/kbn-securitysolution-autocomplete/src/list_schema/index.mock.ts deleted file mode 100644 index e2c2c9b0fe05..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/list_schema/index.mock.ts +++ /dev/null @@ -1,62 +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 { - FoundListSchema, - ListSchema, - FoundListsBySizeSchema, -} from '@kbn/securitysolution-io-ts-list-types'; - -// TODO: Once this mock is available within packages, use it instead, https://github.com/elastic/kibana/issues/100715 -// import { getFoundListSchemaMock } from '../../../../../lists/common/schemas/response/found_list_schema.mock'; -export const getFoundListSchemaMock = (): FoundListSchema => ({ - cursor: '123', - data: [getListResponseMock()], - page: 1, - per_page: 1, - total: 1, -}); - -export const getFoundListsBySizeSchemaMock = (): FoundListsBySizeSchema => ({ - smallLists: [getListResponseMock()], - largeLists: [getListResponseMock()], -}); - -// TODO: Once these mocks are available from packages use it instead, https://github.com/elastic/kibana/issues/100715 -export const DATE_NOW = '2020-04-20T15:25:31.830Z'; -export const USER = 'some user'; -export const IMMUTABLE = false; -export const VERSION = 1; -export const DESCRIPTION = 'some description'; -export const TIE_BREAKER = '6a76b69d-80df-4ab2-8c3e-85f466b06a0e'; -export const LIST_ID = 'some-list-id'; -export const META = {}; -export const TYPE = 'ip'; -export const NAME = 'some name'; - -// TODO: Once this mock is available within packages, use it instead, https://github.com/elastic/kibana/issues/100715 -// import { getListResponseMock } from '../../../../../lists/common/schemas/response/list_schema.mock'; -export const getListResponseMock = (): ListSchema => ({ - '@timestamp': DATE_NOW, - _version: undefined, - created_at: DATE_NOW, - created_by: USER, - description: DESCRIPTION, - deserializer: undefined, - id: LIST_ID, - immutable: IMMUTABLE, - meta: META, - name: NAME, - serializer: undefined, - tie_breaker_id: TIE_BREAKER, - type: TYPE, - updated_at: DATE_NOW, - updated_by: USER, - version: VERSION, -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx deleted file mode 100644 index 4d2112d8f31d..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx +++ /dev/null @@ -1,255 +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 { mount } from 'enzyme'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { isNotOperator, isOperator } from '@kbn/securitysolution-list-utils'; - -import { OperatorComponent } from '.'; -import { getField } from '../fields/index.mock'; - -describe('operator', () => { - test('it renders disabled if "isDisabled" is true', () => { - const wrapper = mount( - <OperatorComponent - isClearable={false} - isDisabled={true} - isLoading={false} - onChange={jest.fn()} - operator={isOperator} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"] input`).prop('disabled') - ).toBeTruthy(); - }); - - test('it renders loading if "isLoading" is true', () => { - const wrapper = mount( - <OperatorComponent - isClearable={false} - isDisabled={false} - isLoading={true} - onChange={jest.fn()} - operator={isOperator} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"] button`).at(0).simulate('click'); - expect( - wrapper - .find(`EuiComboBoxOptionsList[data-test-subj="operatorAutocompleteComboBox-optionsList"]`) - .prop('isLoading') - ).toBeTruthy(); - }); - - test('it allows user to clear values if "isClearable" is true', () => { - const wrapper = mount( - <OperatorComponent - isClearable={true} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - operator={isOperator} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - - expect(wrapper.find(`button[data-test-subj="comboBoxClearButton"]`).exists()).toBeTruthy(); - }); - - test('it displays "operatorOptions" if param is passed in with items', () => { - const wrapper = mount( - <OperatorComponent - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - operator={isOperator} - operatorOptions={[isNotOperator]} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') - ).toEqual([{ label: 'is not' }]); - }); - - test('it does not display "operatorOptions" if param is passed in with no items', () => { - const wrapper = mount( - <OperatorComponent - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - operator={isOperator} - operatorOptions={[]} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') - ).toEqual([ - { - label: 'is', - }, - { - label: 'is not', - }, - { - label: 'is one of', - }, - { - label: 'is not one of', - }, - { - label: 'exists', - }, - { - label: 'does not exist', - }, - { - label: 'is in list', - }, - { - label: 'is not in list', - }, - { - label: 'matches', - }, - { - label: 'does not match', - }, - ]); - }); - - test('it correctly displays selected operator', () => { - const wrapper = mount( - <OperatorComponent - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - operator={isOperator} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - - expect( - wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').at(0).props().value - ).toEqual('is'); - }); - - test('it only displays subset of operators if field type is nested', () => { - const wrapper = mount( - <OperatorComponent - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - operator={isOperator} - placeholder="Placeholder text" - selectedField={{ - name: 'nestedField', - scripted: false, - subType: { nested: { path: 'nestedField' } }, - type: 'nested', - }} - /> - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') - ).toEqual([{ label: 'is' }]); - }); - - test('it only displays subset of operators if field type is boolean', () => { - const wrapper = mount( - <OperatorComponent - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - operator={isOperator} - placeholder="Placeholder text" - selectedField={getField('ssl')} - /> - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') - ).toEqual([ - { label: 'is' }, - { label: 'is not' }, - { label: 'exists' }, - { label: 'does not exist' }, - ]); - }); - - test('it only displays subset of operators if field name is "file.path.text"', () => { - const wrapper = mount( - <OperatorComponent - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={jest.fn()} - operator={isOperator} - placeholder="Placeholder text" - selectedField={getField('file.path.text')} - /> - ); - - expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') - ).toEqual([ - { label: 'is' }, - { label: 'is not' }, - { label: 'is one of' }, - { label: 'is not one of' }, - { label: 'matches' }, - { label: 'does not match' }, - ]); - }); - - test('it invokes "onChange" when option selected', () => { - const mockOnChange = jest.fn(); - const wrapper = mount( - <OperatorComponent - isClearable={false} - isDisabled={false} - isLoading={false} - onChange={mockOnChange} - operator={isOperator} - placeholder="Placeholder text" - selectedField={getField('machine.os.raw')} - /> - ); - - ( - wrapper.find(EuiComboBox).props() as unknown as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - } - ).onChange([{ label: 'is not' }]); - - expect(mockOnChange).toHaveBeenCalledWith([ - { message: 'is not', operator: 'excluded', type: 'match', value: 'is_not' }, - ]); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/operator/index.tsx b/packages/kbn-securitysolution-autocomplete/src/operator/index.tsx deleted file mode 100644 index c3d91c16eae9..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/operator/index.tsx +++ /dev/null @@ -1,101 +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, { useCallback, useMemo } from 'react'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { OperatorOption } from '@kbn/securitysolution-list-utils'; -import { DataViewFieldBase } from '@kbn/es-query'; - -import { getOperators } from '../get_operators'; -import { - getGenericComboBoxProps, - GetGenericComboBoxPropsReturn, -} from '../get_generic_combo_box_props'; - -const AS_PLAIN_TEXT = { asPlainText: true }; - -interface OperatorState { - isClearable: boolean; - isDisabled: boolean; - isLoading: boolean; - onChange: (arg: OperatorOption[]) => void; - operator: OperatorOption; - operatorInputWidth?: number; - operatorOptions?: OperatorOption[]; - placeholder: string; - selectedField: DataViewFieldBase | undefined; - 'aria-label'?: string; -} - -export const OperatorComponent: React.FC<OperatorState> = ({ - isClearable = false, - isDisabled = false, - isLoading = false, - onChange, - operator, - operatorOptions, - operatorInputWidth = 150, - placeholder, - selectedField, - 'aria-label': ariaLabel, -}): JSX.Element => { - const getLabel = useCallback(({ message }: OperatorOption): string => message, []); - const optionsMemo = useMemo( - (): OperatorOption[] => - operatorOptions != null && operatorOptions.length > 0 - ? operatorOptions - : getOperators(selectedField), - [operatorOptions, selectedField] - ); - const selectedOptionsMemo = useMemo( - (): OperatorOption[] => (operator ? [operator] : []), - [operator] - ); - const { comboOptions, labels, selectedComboOptions } = useMemo( - (): GetGenericComboBoxPropsReturn => - getGenericComboBoxProps<OperatorOption>({ - getLabel, - options: optionsMemo, - selectedOptions: selectedOptionsMemo, - }), - [optionsMemo, selectedOptionsMemo, getLabel] - ); - - const handleValuesChange = useCallback( - (newOptions: EuiComboBoxOptionOption[]): void => { - const newValues: OperatorOption[] = newOptions.map( - ({ label }) => optionsMemo[labels.indexOf(label)] - ); - onChange(newValues); - }, - [labels, onChange, optionsMemo] - ); - - const inputWidth = useMemo(() => { - return { width: `${operatorInputWidth}px` }; - }, [operatorInputWidth]); - - return ( - <EuiComboBox - placeholder={placeholder} - options={comboOptions} - selectedOptions={selectedComboOptions} - onChange={handleValuesChange} - isLoading={isLoading} - isDisabled={isDisabled} - isClearable={isClearable} - singleSelection={AS_PLAIN_TEXT} - data-test-subj="operatorAutocompleteComboBox" - style={inputWidth} - aria-label={ariaLabel} - /> - ); -}; - -OperatorComponent.displayName = 'Operator'; diff --git a/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.test.ts b/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.test.ts deleted file mode 100644 index aef6fc4acffa..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.test.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", 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 { paramContainsSpace } from '.'; - -describe('param_contains_space', () => { - test('should return true if leading spaces were found', () => { - expect(paramContainsSpace(' test')).toBeTruthy(); - }); - test('should return true if trailing spaces were found', () => { - expect(paramContainsSpace('test ')).toBeTruthy(); - }); - test('should return true if both trailing and leading spaces were found', () => { - expect(paramContainsSpace(' test ')).toBeTruthy(); - }); - test('should return true if tabs was found', () => { - expect(paramContainsSpace('\ttest')).toBeTruthy(); - }); - test('should return false if no spaces were found', () => { - expect(paramContainsSpace('test test')).toBeFalsy(); - }); - test('should return false if param is falsy', () => { - expect(paramContainsSpace('')).toBeFalsy(); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.ts b/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.ts deleted file mode 100644 index 996167f20536..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.ts +++ /dev/null @@ -1,10 +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". - */ - -export const paramContainsSpace = (param: string) => param && param.trim().length !== param.length; diff --git a/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.test.ts b/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.test.ts deleted file mode 100644 index c530bca356cb..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.test.ts +++ /dev/null @@ -1,107 +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 { paramIsValid } from '.'; -import { getField } from '../fields/index.mock'; -import * as i18n from '../translations'; -import moment from 'moment'; - -describe('params_is_valid', () => { - beforeEach(() => { - // Disable momentJS deprecation warning and it looks like it is not typed either so - // we have to disable the type as well and cannot extend it easily. - ( - moment as unknown as { - suppressDeprecationWarnings: boolean; - } - ).suppressDeprecationWarnings = true; - }); - - afterEach(() => { - // Re-enable momentJS deprecation warning and it looks like it is not typed either so - // we have to disable the type as well and cannot extend it easily. - ( - moment as unknown as { - suppressDeprecationWarnings: boolean; - } - ).suppressDeprecationWarnings = false; - }); - - test('returns no errors if no field has been selected', () => { - const isValid = paramIsValid('', undefined, true, false); - - expect(isValid).toBeUndefined(); - }); - - test('returns error string if user has touched a required input and left empty', () => { - const isValid = paramIsValid(undefined, getField('@timestamp'), true, true); - - expect(isValid).toEqual(i18n.FIELD_REQUIRED_ERR); - }); - - test('returns no errors if required input is empty but user has not yet touched it', () => { - const isValid = paramIsValid(undefined, getField('@timestamp'), true, false); - - expect(isValid).toBeUndefined(); - }); - - test('returns no errors if user has touched an input that is not required and left empty', () => { - const isValid = paramIsValid(undefined, getField('@timestamp'), false, true); - - expect(isValid).toBeUndefined(); - }); - - test('returns no errors if user has touched an input that is not required and left empty string', () => { - const isValid = paramIsValid('', getField('@timestamp'), false, true); - - expect(isValid).toBeUndefined(); - }); - - test('returns no errors if field is of type date and value is valid', () => { - const isValid = paramIsValid('1994-11-05T08:15:30-05:00', getField('@timestamp'), false, true); - - expect(isValid).toBeUndefined(); - }); - - test('returns errors if filed is of type date and value is not valid', () => { - const isValid = paramIsValid('1593478826', getField('@timestamp'), false, true); - - expect(isValid).toEqual(i18n.DATE_ERR); - }); - - test('returns no errors if field is of type number and value is an integer', () => { - const isValid = paramIsValid('4', getField('bytes'), true, true); - - expect(isValid).toBeUndefined(); - }); - - test('returns no errors if field is of type number and value is a float', () => { - const isValid = paramIsValid('4.3', getField('bytes'), true, true); - - expect(isValid).toBeUndefined(); - }); - - test('returns no errors if field is of type number and value is a long', () => { - const isValid = paramIsValid('-9223372036854775808', getField('bytes'), true, true); - - expect(isValid).toBeUndefined(); - }); - - test('returns errors if field is of type number and value is "hello"', () => { - const isValid = paramIsValid('hello', getField('bytes'), true, true); - - expect(isValid).toEqual(i18n.NUMBER_ERR); - }); - - test('returns errors if field is of type number and value is "123abc"', () => { - const isValid = paramIsValid('123abc', getField('bytes'), true, true); - - expect(isValid).toEqual(i18n.NUMBER_ERR); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.ts b/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.ts deleted file mode 100644 index 5abde0b36c4c..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 dateMath from '@kbn/datemath'; -import { DataViewFieldBase } from '@kbn/es-query'; -import { checkEmptyValue } from '../check_empty_value'; - -import * as i18n from '../translations'; - -/** - * Very basic validation for values - * @param param the value being checked - * @param field the selected field - * @param isRequired whether or not an empty value is allowed - * @param touched has field been touched by user - * @returns undefined if valid, string with error message if invalid - */ -export const paramIsValid = ( - param: string | undefined, - field: DataViewFieldBase | undefined, - isRequired: boolean, - touched: boolean -): string | undefined => { - if (field == null) { - return undefined; - } - - const emptyValueError = checkEmptyValue(param, field, isRequired, touched); - if (emptyValueError !== null) { - return emptyValueError; - } - - switch (field.type) { - case 'date': - const moment = dateMath.parse(param ?? ''); - const isDate = Boolean(moment && moment.isValid()); - return isDate ? undefined : i18n.DATE_ERR; - case 'number': - const isNum = param != null && param.trim() !== '' && !isNaN(+param); - return isNum ? undefined : i18n.NUMBER_ERR; - default: - return undefined; - } -}; diff --git a/packages/kbn-securitysolution-autocomplete/src/translations/index.ts b/packages/kbn-securitysolution-autocomplete/src/translations/index.ts deleted file mode 100644 index 01671880d2dc..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/translations/index.ts +++ /dev/null @@ -1,81 +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 { i18n } from '@kbn/i18n'; - -export const LOADING = i18n.translate('autocomplete.loadingDescription', { - defaultMessage: 'Loading...', -}); - -export const SELECT_FIELD_FIRST = i18n.translate('autocomplete.selectField', { - defaultMessage: 'Please select a field first...', -}); - -export const FIELD_REQUIRED_ERR = i18n.translate('autocomplete.fieldRequiredError', { - defaultMessage: 'Value cannot be empty', -}); - -export const NUMBER_ERR = i18n.translate('autocomplete.invalidNumberError', { - defaultMessage: 'Not a valid number', -}); - -export const DATE_ERR = i18n.translate('autocomplete.invalidDateError', { - defaultMessage: 'Not a valid date', -}); - -export const BINARY_TYPE_NOT_SUPPORTED = i18n.translate('autocomplete.invalidBinaryType', { - defaultMessage: 'Binary fields are currently unsupported', -}); -export const FIELD_SPACE_WARNING = i18n.translate('autocomplete.fieldSpaceWarning', { - defaultMessage: "Warning: Spaces at the start or end of this value aren't being displayed.", -}); - -export const LISTS_TOOLTIP_INFO = i18n.translate('autocomplete.listsTooltipWarning', { - defaultMessage: "Lists that aren't able to be processed by this rule type will be disabled.", -}); - -export const SEE_DOCUMENTATION = i18n.translate('autocomplete.seeDocumentation', { - defaultMessage: 'See Documentation', -}); - -export const FIELD_CONFLICT_INDICES_WARNING_TITLE = i18n.translate( - 'autocomplete.conflictIndicesWarning.title', - { - defaultMessage: 'Mapping Conflict', - } -); - -export const FIELD_CONFLICT_INDICES_WARNING_DESCRIPTION = i18n.translate( - 'autocomplete.conflictIndicesWarning.description', - { - defaultMessage: - 'This field is defined as different types across the following indices or is unmapped. This can cause unexpected query results.', - } -); - -export const CONFLICT_MULTIPLE_INDEX_DESCRIPTION = (name: string, count: number): string => - i18n.translate('autocomplete.conflictIndicesWarning.index.description', { - defaultMessage: '{name} ({count} indices)', - values: { count, name }, - }); - -export const SHOW_VALUE_LIST_MODAL = i18n.translate('autocomplete.showValueListModal', { - defaultMessage: 'Show value list', -}); - -// eslint-disable-next-line import/no-default-export -export default { - LOADING, - SELECT_FIELD_FIRST, - FIELD_REQUIRED_ERR, - NUMBER_ERR, - DATE_ERR, - FIELD_SPACE_WARNING, - BINARY_TYPE_NOT_SUPPORTED, -}; diff --git a/packages/kbn-securitysolution-autocomplete/src/type_match/index.test.ts b/packages/kbn-securitysolution-autocomplete/src/type_match/index.test.ts deleted file mode 100644 index 15c7c3f580da..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/type_match/index.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { typeMatch } from '.'; - -describe('type_match', () => { - test('ip -> ip is true', () => { - expect(typeMatch('ip', 'ip')).toEqual(true); - }); - - test('keyword -> keyword is true', () => { - expect(typeMatch('keyword', 'keyword')).toEqual(true); - }); - - test('text -> text is true', () => { - expect(typeMatch('text', 'text')).toEqual(true); - }); - - test('ip_range -> ip is true', () => { - expect(typeMatch('ip_range', 'ip')).toEqual(true); - }); - - test('date_range -> date is true', () => { - expect(typeMatch('date_range', 'date')).toEqual(true); - }); - - test('double_range -> double is true', () => { - expect(typeMatch('double_range', 'double')).toEqual(true); - }); - - test('float_range -> float is true', () => { - expect(typeMatch('float_range', 'float')).toEqual(true); - }); - - test('integer_range -> integer is true', () => { - expect(typeMatch('integer_range', 'integer')).toEqual(true); - }); - - test('long_range -> long is true', () => { - expect(typeMatch('long_range', 'long')).toEqual(true); - }); - - test('ip -> date is false', () => { - expect(typeMatch('ip', 'date')).toEqual(false); - }); - - test('long -> float is false', () => { - expect(typeMatch('long', 'float')).toEqual(false); - }); - - test('integer -> long is false', () => { - expect(typeMatch('integer', 'long')).toEqual(false); - }); -}); diff --git a/packages/kbn-securitysolution-autocomplete/src/type_match/index.ts b/packages/kbn-securitysolution-autocomplete/src/type_match/index.ts deleted file mode 100644 index 4d5899a376a0..000000000000 --- a/packages/kbn-securitysolution-autocomplete/src/type_match/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { Type } from '@kbn/securitysolution-io-ts-list-types'; - -/** - * Given an input list type and a string based ES type this will match - * if they're exact or if they are compatible with a range - * @param type The type to match against the esType - * @param esType The ES type to match with - */ -export const typeMatch = (type: Type, esType: string): boolean => { - return ( - type === esType || - (type === 'ip_range' && esType === 'ip') || - (type === 'date_range' && esType === 'date') || - (type === 'double_range' && esType === 'double') || - (type === 'float_range' && esType === 'float') || - (type === 'integer_range' && esType === 'integer') || - (type === 'long_range' && esType === 'long') - ); -}; diff --git a/packages/kbn-securitysolution-autocomplete/tsconfig.json b/packages/kbn-securitysolution-autocomplete/tsconfig.json deleted file mode 100644 index f7df7da6f06a..000000000000 --- a/packages/kbn-securitysolution-autocomplete/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": ["jest", "node"] - }, - "include": [ - "**/*.ts", - "**/*.tsx", - ], - "kbn_references": [ - "@kbn/datemath", - "@kbn/es-query", - "@kbn/i18n", - "@kbn/securitysolution-io-ts-list-types", - "@kbn/securitysolution-list-hooks", - "@kbn/securitysolution-list-utils", - "@kbn/doc-links", - "@kbn/securitysolution-utils", - ], - "exclude": [ - "target/**/*", - ], -} diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.gen.ts b/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.gen.ts deleted file mode 100644 index 42a3b67106c0..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.gen.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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Create endpoint list API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { EndpointList } from '../model/endpoint_list_common.gen'; - -export type CreateEndpointListResponse = z.infer<typeof CreateEndpointListResponse>; -export const CreateEndpointListResponse = EndpointList; diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.schema.yaml b/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.schema.yaml deleted file mode 100644 index b2ca0f563617..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.schema.yaml +++ /dev/null @@ -1,45 +0,0 @@ -openapi: 3.0.0 -info: - title: Create endpoint list API endpoint - version: '2023-10-31' -paths: - /api/endpoint_list: - post: - x-labels: [serverless, ess] - x-codegen-enabled: true - operationId: CreateEndpointList - summary: Create an endpoint exception list - description: Create an endpoint exception list, which groups endpoint exception list items. If an endpoint exception list already exists, an empty response is returned. - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/endpoint_list_common.schema.yaml#/components/schemas/EndpointList' - 400: - description: Invalid input data - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Insufficient privileges - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 500: - description: Internal server error - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.schema.yaml b/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.schema.yaml deleted file mode 100644 index 69db50616918..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.schema.yaml +++ /dev/null @@ -1,64 +0,0 @@ -openapi: 3.0.0 -info: - title: Delete endpoint list item API endpoint - version: '2023-10-31' -paths: - /api/endpoint_list/items: - delete: - x-labels: [serverless, ess] - x-codegen-enabled: true - operationId: DeleteEndpointListItem - summary: Delete an endpoint exception list item - description: Delete an endpoint exception list item using the `id` or `item_id` field. - parameters: - - name: id - in: query - required: false - description: Either `id` or `item_id` must be specified - schema: - $ref: '../../../kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemId' - - name: item_id - in: query - required: false - description: Either `id` or `item_id` must be specified - schema: - $ref: '../../../kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemHumanId' - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/endpoint_list_common.schema.yaml#/components/schemas/EndpointListItem' - 400: - description: Invalid input data - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Insufficient privileges - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: Endpoint list item not found - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.schema.yaml b/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.schema.yaml deleted file mode 100644 index 6dc2fcaa7e87..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.schema.yaml +++ /dev/null @@ -1,113 +0,0 @@ -openapi: 3.0.0 -info: - title: Find endpoint list items API endpoint - version: '2023-10-31' -paths: - /api/endpoint_list/items/_find: - get: - x-labels: [serverless, ess] - x-codegen-enabled: true - operationId: FindEndpointListItems - summary: Get endpoint exception list items - description: Get a list of all endpoint exception list items. - parameters: - - name: filter - in: query - required: false - description: | - Filters the returned results according to the value of the specified field, - using the `<field name>:<field value>` syntax. - schema: - $ref: '#/components/schemas/FindEndpointListItemsFilter' - - name: page - in: query - required: false - description: The page number to return - schema: - type: integer - minimum: 0 - - name: per_page - in: query - required: false - description: The number of exception list items to return per page - schema: - type: integer - minimum: 0 - - name: sort_field - in: query - required: false - description: Determines which field is used to sort the results - schema: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - - name: sort_order - in: query - required: false - description: Determines the sort order, which can be `desc` or `asc` - schema: - type: string - enum: [desc, asc] - responses: - 200: - description: Successful response - content: - application/json: - schema: - type: object - properties: - data: - type: array - items: - $ref: '../model/endpoint_list_common.schema.yaml#/components/schemas/EndpointListItem' - page: - type: integer - minimum: 0 - per_page: - type: integer - minimum: 0 - total: - type: integer - minimum: 0 - pit: - type: string - required: - - data - - page - - per_page - - total - 400: - description: Invalid input data - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Insufficient privileges - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: Endpoint list not found - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - -components: - schemas: - FindEndpointListItemsFilter: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/index.ts b/packages/kbn-securitysolution-endpoint-exceptions-common/api/index.ts deleted file mode 100644 index c71b02c448db..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/index.ts +++ /dev/null @@ -1,15 +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". - */ - -export * from './create_endpoint_list/create_endpoint_list.gen'; -export * from './create_endpoint_list_item/create_endpoint_list_item.gen'; -export * from './read_endpoint_list_item/read_endpoint_list_item.gen'; -export * from './update_endpoint_list_item/update_endpoint_list_item.gen'; -export * from './delete_endpoint_list_item/delete_endpoint_list_item.gen'; -export * from './find_endpoint_list_item/find_endpoint_list_item.gen'; diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/model/endpoint_list_common.gen.ts b/packages/kbn-securitysolution-endpoint-exceptions-common/api/model/endpoint_list_common.gen.ts deleted file mode 100644 index 6c8093246b1a..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/model/endpoint_list_common.gen.ts +++ /dev/null @@ -1,30 +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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Common Exception List Attributes - * version: not applicable - */ - -import { z } from '@kbn/zod'; - -import { - ExceptionList, - ExceptionListItem, -} from '@kbn/securitysolution-exceptions-common/api/model/exception_list_common.gen'; - -export type EndpointList = z.infer<typeof EndpointList>; -export const EndpointList = z.union([ExceptionList, z.object({}).strict()]); - -export type EndpointListItem = z.infer<typeof EndpointListItem>; -export const EndpointListItem = ExceptionListItem; diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.schema.yaml b/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.schema.yaml deleted file mode 100644 index 8a4e0b291c76..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.schema.yaml +++ /dev/null @@ -1,66 +0,0 @@ -openapi: 3.0.0 -info: - title: Read endpoint list item API endpoint - version: '2023-10-31' -paths: - /api/endpoint_list/items: - get: - x-labels: [serverless, ess] - x-codegen-enabled: true - operationId: ReadEndpointListItem - summary: Get an endpoint exception list item - description: Get the details of an endpoint exception list item using the `id` or `item_id` field. - parameters: - - name: id - in: query - required: false - description: Either `id` or `item_id` must be specified - schema: - $ref: '../../../kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemId' - - name: item_id - in: query - required: false - description: Either `id` or `item_id` must be specified - schema: - $ref: '../../../kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemHumanId' - responses: - 200: - description: Successful response - content: - application/json: - schema: - type: array - items: - $ref: '../model/endpoint_list_common.schema.yaml#/components/schemas/EndpointListItem' - 400: - description: Invalid input data - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Insufficient privileges - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: Endpoint list item not found - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/package.json b/packages/kbn-securitysolution-endpoint-exceptions-common/package.json deleted file mode 100644 index 78470be1d4e4..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "description": "OpenAPI Endpoint Exceptions Common", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "name": "@kbn/securitysolution-endpoint-exceptions-common", - "private": true, - "version": "1.0.0", - "scripts": { - "openapi:generate": "node scripts/openapi_generate", - "openapi:bundle": "node scripts/openapi_bundle" - } -} diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_bundle.js b/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_bundle.js deleted file mode 100644 index e5a02965593c..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_bundle.js +++ /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", 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". - */ - -require('../../../src/setup_node_env'); -const { join, resolve } = require('path'); -const { bundle } = require('@kbn/openapi-bundler'); - -const ROOT = resolve(__dirname, '..'); - -(async () => { - await bundle({ - sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), - outputFilePath: join( - ROOT, - 'docs/openapi/serverless/security_solution_endpoint_exceptions_api_{version}.bundled.schema.yaml' - ), - options: { - includeLabels: ['serverless'], - prototypeDocument: { - info: { - title: 'Security Endpoint Exceptions API (Elastic Cloud Serverless)', - description: 'Endpoint Exceptions API allow you to manage Endpoint lists.', - }, - tags: [ - { - name: 'Security Endpoint Exceptions API', - 'x-displayName': 'Security endpoint exceptions', - description: - "Endpoint Exceptions API allows you to manage detection rule endpoint exceptions to prevent a rule from generating an alert from incoming events even when the rule's other criteria are met.", - }, - ], - }, - }, - }); - - await bundle({ - sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), - outputFilePath: join( - ROOT, - 'docs/openapi/ess/security_solution_endpoint_exceptions_api_{version}.bundled.schema.yaml' - ), - options: { - includeLabels: ['ess'], - prototypeDocument: { - info: { - title: 'Security Endpoint Exceptions API (Elastic Cloud and self-hosted)', - description: 'Endpoint Exceptions API allow you to manage Endpoint lists.', - }, - tags: [ - { - name: 'Security Endpoint Exceptions API', - 'x-displayName': 'Security endpoint exceptions', - description: - "Endpoint Exceptions API allows you to manage detection rule endpoint exceptions to prevent a rule from generating an alert from incoming events even when the rule's other criteria are met.", - }, - ], - }, - }, - }); -})(); diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_generate.js b/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_generate.js deleted file mode 100644 index bca85786a518..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_generate.js +++ /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", 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". - */ - -require('../../../src/setup_node_env'); -const { join, resolve } = require('path'); -const { generate } = require('@kbn/openapi-generator'); -const { REPO_ROOT } = require('@kbn/repo-info'); - -const ROOT = resolve(__dirname, '..'); - -(async () => { - await generate({ - title: 'OpenAPI Endpoint Exceptions API Schemas', - rootDir: ROOT, - sourceGlob: './api/**/*.schema.yaml', - templateName: 'zod_operation_schema', - }); - - await generate({ - title: 'Endpoint Exceptions API client for tests', - rootDir: ROOT, - sourceGlob: './api/**/*.schema.yaml', - templateName: 'api_client_supertest', - skipLinting: true, - bundle: { - outFile: join( - REPO_ROOT, - 'x-pack/test/api_integration/services/security_solution_endpoint_exceptions_api.gen.ts' - ), - }, - }); -})(); diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/tsconfig.json b/packages/kbn-securitysolution-endpoint-exceptions-common/tsconfig.json deleted file mode 100644 index 7e3c3fd7396d..000000000000 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "outDir": "target/types", - "types": ["jest", "node"] - }, - "exclude": ["target/**/*"], - "extends": "../../tsconfig.base.json", - "include": ["**/*.ts"], - "kbn_references": [ - "@kbn/securitysolution-exceptions-common", - "@kbn/openapi-common", - "@kbn/zod", - ] -} diff --git a/packages/kbn-securitysolution-es-utils/jest.config.js b/packages/kbn-securitysolution-es-utils/jest.config.js deleted file mode 100644 index 5d52b2d390b5..000000000000 --- a/packages/kbn-securitysolution-es-utils/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-es-utils'], -}; diff --git a/packages/kbn-securitysolution-es-utils/tsconfig.json b/packages/kbn-securitysolution-es-utils/tsconfig.json deleted file mode 100644 index e584d504cc9c..000000000000 --- a/packages/kbn-securitysolution-es-utils/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts" - ], - "exclude": [ - "target/**/*", - ], - "kbn_references": [ - "@kbn/zod-helpers", - "@kbn/zod", - ] -} diff --git a/packages/kbn-securitysolution-exception-list-components/index.ts b/packages/kbn-securitysolution-exception-list-components/index.ts deleted file mode 100644 index 0e11a4694384..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/index.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". - */ - -export * from './src/search_bar'; -export * from './src/empty_viewer_state'; -export * from './src/pagination/pagination'; -// export * from './src/exceptions_utility/exceptions_utility'; -export * from './src/exception_items'; -export * from './src/exception_item_card'; -export * from './src/value_with_space_warning'; -export * from './src/types'; -export * from './src/list_header'; -export * from './src/header_menu'; -export * from './src/generate_linked_rules_menu_item'; -export * from './src/wildcard_with_wrong_operator_callout'; -export * from './src/partial_code_signature_callout'; diff --git a/packages/kbn-securitysolution-exception-list-components/jest.config.js b/packages/kbn-securitysolution-exception-list-components/jest.config.js deleted file mode 100644 index e9d4ad8ad848..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-exception-list-components'], - coverageReporters: ['text', 'html'], - collectCoverageFrom: [ - '<rootDir>/packages/kbn-securitysolution-exception-list-components/**/*.{ts,tsx}', - '!<rootDir>/packages/kbn-securitysolution-exception-list-components/**/*.test', - '!<rootDir>/packages/kbn-securitysolution-exception-list-components/**/types/*', - '!<rootDir>/packages/kbn-securitysolution-exception-list-components/**/*.type', - '!<rootDir>/packages/kbn-securitysolution-exception-list-components/**/*.styles', - '!<rootDir>/packages/kbn-securitysolution-exception-list-components/**/mocks/*', - '!<rootDir>/packages/kbn-securitysolution-exception-list-components/**/*.config', - '!<rootDir>/packages/kbn-securitysolution-exception-list-components/**/translations', - '!<rootDir>/packages/kbn-securitysolution-exception-list-components/**/types/*', - ], - setupFilesAfterEnv: [ - '<rootDir>/packages/kbn-securitysolution-exception-list-components/setup_test.ts', - ], -}; diff --git a/packages/kbn-securitysolution-exception-list-components/package.json b/packages/kbn-securitysolution-exception-list-components/package.json deleted file mode 100644 index 0b99a01c071e..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "@kbn/securitysolution-exception-list-components", - "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/packages/kbn-securitysolution-exception-list-components/setup_test.ts b/packages/kbn-securitysolution-exception-list-components/setup_test.ts deleted file mode 100644 index 5ebc6d3dac1c..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/setup_test.ts +++ /dev/null @@ -1,11 +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". - */ - -// eslint-disable-next-line import/no-extraneous-dependencies -import '@testing-library/jest-dom'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/custom.d.ts b/packages/kbn-securitysolution-exception-list-components/src/custom.d.ts deleted file mode 100644 index aa99593b9e87..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/custom.d.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", 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". - */ - -declare module '*.svg' { - const content: string; - // eslint-disable-next-line import/no-default-export - export default content; -} diff --git a/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/index.tsx deleted file mode 100644 index e9996e814c34..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/index.tsx +++ /dev/null @@ -1,135 +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, { useMemo } from 'react'; -import type { FC } from 'react'; -import { css } from '@emotion/react'; -import { - EuiSkeletonText, - EuiImage, - EuiEmptyPrompt, - EuiButton, - useEuiTheme, - EuiPanel, -} from '@elastic/eui'; -import type { ExpressionColor } from '@elastic/eui/src/components/expression/expression'; -import type { EuiFacetGroupLayout } from '@elastic/eui/src/components/facet/facet_group'; -import { euiThemeVars } from '@kbn/ui-theme'; -import { ListTypeText, ViewerStatus } from '../types'; -import * as i18n from '../translations'; -import illustration from '../assets/images/illustration_product_no_results_magnifying_glass.svg'; - -interface EmptyViewerStateProps { - title?: string; - body?: string; - buttonText?: string; - listType?: ListTypeText; - isReadOnly: boolean; - viewerStatus: ViewerStatus; - onEmptyButtonStateClick?: () => void | null; -} - -const panelCss = css` - margin: ${euiThemeVars.euiSizeL} 0; - padding: ${euiThemeVars.euiSizeL} 0; -`; -const EmptyViewerStateComponent: FC<EmptyViewerStateProps> = ({ - title, - body, - buttonText, - listType, - isReadOnly, - viewerStatus, - onEmptyButtonStateClick, -}) => { - const { euiTheme } = useEuiTheme(); - - const euiEmptyPromptProps = useMemo(() => { - switch (viewerStatus) { - case ViewerStatus.ERROR: { - return { - color: 'danger' as ExpressionColor, - iconType: 'error', - title: ( - <h2 data-test-subj="errorTitle">{title || i18n.EMPTY_VIEWER_STATE_ERROR_TITLE}</h2> - ), - body: <p data-test-subj="errorBody">{body || i18n.EMPTY_VIEWER_STATE_ERROR_BODY}</p>, - 'data-test-subj': 'errorViewerState', - }; - } - case ViewerStatus.EMPTY: - return { - color: 'subdued' as ExpressionColor, - iconType: 'plusInCircle', - iconColor: euiTheme.colors.darkestShade, - title: ( - <h2 data-test-subj="emptyTitle">{title || i18n.EMPTY_VIEWER_STATE_EMPTY_TITLE}</h2> - ), - body: <p data-test-subj="emptyBody">{body || i18n.EMPTY_VIEWER_STATE_EMPTY_BODY}</p>, - 'data-test-subj': 'emptyViewerState', - actions: [ - <EuiButton - data-test-subj="emptyStateButton" - onClick={onEmptyButtonStateClick} - iconType="plusInCircle" - color="primary" - isDisabled={isReadOnly} - fill - > - {buttonText || i18n.EMPTY_VIEWER_STATE_EMPTY_VIEWER_BUTTON(listType || 'rule')} - </EuiButton>, - ], - }; - case ViewerStatus.EMPTY_SEARCH: - return { - color: 'plain' as ExpressionColor, - layout: 'horizontal' as EuiFacetGroupLayout, - hasBorder: true, - hasShadow: false, - icon: <EuiImage size="fullWidth" alt="" src={illustration} />, - title: ( - <h3 data-test-subj="emptySearchTitle"> - {title || i18n.EMPTY_VIEWER_STATE_EMPTY_SEARCH_TITLE} - </h3> - ), - body: ( - <p data-test-subj="emptySearchBody"> - {body || i18n.EMPTY_VIEWER_STATE_EMPTY_SEARCH_BODY} - </p> - ), - 'data-test-subj': 'emptySearchViewerState', - }; - } - }, [ - viewerStatus, - euiTheme.colors.darkestShade, - title, - body, - onEmptyButtonStateClick, - isReadOnly, - buttonText, - listType, - ]); - - return ( - <EuiSkeletonText - lines={4} - data-test-subj="loadingViewerState" - isLoading={viewerStatus === ViewerStatus.LOADING || viewerStatus === ViewerStatus.SEARCHING} - > - <EuiPanel css={panelCss} color={viewerStatus === 'empty_search' ? 'subdued' : 'transparent'}> - <EuiEmptyPrompt {...euiEmptyPromptProps} /> - </EuiPanel> - </EuiSkeletonText> - ); -}; - -export const EmptyViewerState = React.memo(EmptyViewerStateComponent); - -EmptyViewerState.displayName = 'EmptyViewerState'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/index.tsx deleted file mode 100644 index d2e5ac0577af..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/index.tsx +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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, { memo } from 'react'; -import type { EuiCommentProps } from '@elastic/eui'; -import { EuiAccordion, EuiCommentList, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; -import * as i18n from '../translations'; - -const accordionCss = css` - color: ${euiThemeVars.euiColorPrimary}; -`; - -export interface ExceptionItemCardCommentsProps { - comments: EuiCommentProps[]; - dataTestSubj?: string; -} - -export const ExceptionItemCardComments = memo<ExceptionItemCardCommentsProps>( - ({ comments, dataTestSubj }) => { - if (!comments.length) return null; - return ( - <EuiFlexItem data-test-subj={dataTestSubj}> - <EuiAccordion - id="exceptionItemCardComments" - buttonContent={ - <EuiText size="s" css={accordionCss} data-test-subj={`${dataTestSubj || ''}TextButton`}> - {i18n.exceptionItemCardCommentsAccordion(comments.length)} - </EuiText> - } - arrowDisplay="none" - data-test-subj="exceptionItemCardComments" - > - <EuiPanel data-test-subj="accordionContentPanel" hasBorder hasShadow paddingSize="m"> - <EuiCommentList data-test-subj="accordionCommentList" comments={comments} /> - </EuiPanel> - </EuiAccordion> - </EuiFlexItem> - ); - } -); - -ExceptionItemCardComments.displayName = 'ExceptionItemCardComments'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx deleted file mode 100644 index 8a089df54f2f..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx +++ /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", 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 { cx } from '@emotion/css'; -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 borderCss = cx( - 'eui-xScroll', - ` - border: 1px; - border-color: #d3dae6; - border-style: solid; -` -); - -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/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/index.tsx deleted file mode 100644 index b765439116ce..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/index.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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, { ElementType, FC, memo } from 'react'; -import { EuiExpression, EuiToken, EuiFlexGroup } from '@elastic/eui'; -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'; - -interface EntryContentProps { - entry: Entry; - index: number; - isNestedEntry?: boolean; - dataTestSubj?: string; - showValueListModal: ElementType; -} - -export const EntryContent: FC<EntryContentProps> = memo( - ({ entry, index, isNestedEntry = false, dataTestSubj, showValueListModal }) => { - const { field, type } = entry; - const value = getValue(entry); - const operator = 'operator' in entry ? entry.operator : ''; - - const entryKey = `${field}${type}${value}${index}`; - return ( - <div data-test-subj={`${dataTestSubj || ''}${entryKey}EntryContent`} key={entryKey}> - <div css={expressionContainerCss}> - {isNestedEntry ? ( - <EuiFlexGroup - responsive - css={nestedGroupSpaceCss} - direction="row" - alignItems="center" - gutterSize="m" - data-test-subj={`${dataTestSubj || ''}NestedEntry`} - > - <EuiToken data-test-subj="nstedEntryIcon" iconType="tokenNested" size="s" /> - - <div css={valueContainerCss}> - <EuiExpression description="" value={field} color="subdued" /> - {getValueExpression( - type as ListOperatorTypeEnum, - operator, - value, - showValueListModal - )} - </div> - </EuiFlexGroup> - ) : ( - <> - <EuiExpression - description={index === 0 ? '' : i18n.CONDITION_AND} - value={field} - color={index === 0 ? 'primary' : 'subdued'} - data-test-subj={`${dataTestSubj || ''}SingleEntry`} - /> - - {getValueExpression( - type as ListOperatorTypeEnum, - operator, - value, - showValueListModal - )} - </> - )} - </div> - </div> - ); - } -); -EntryContent.displayName = 'EntryContent'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/index.tsx deleted file mode 100644 index f472402adddc..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/index.tsx +++ /dev/null @@ -1,59 +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, { memo } from 'react'; -import { EuiPanel } from '@elastic/eui'; - -import { borderCss } from './conditions.styles'; -import { EntryContent } from './entry_content'; -import { OsCondition } from './os_conditions'; -import type { CriteriaConditionsProps, Entry } from './types'; - -export const ExceptionItemCardConditions = memo<CriteriaConditionsProps>( - ({ os, entries, dataTestSubj, showValueListModal }) => { - return ( - <EuiPanel - color="subdued" - hasBorder={true} - hasShadow={false} - data-test-subj={dataTestSubj} - className={borderCss} - > - {os?.length ? <OsCondition os={os} dataTestSubj={dataTestSubj} /> : null} - {entries.map((entry: Entry, index: number) => { - const nestedEntries = 'entries' in entry ? entry.entries : []; - return ( - <div key={`ExceptionItemCardConditionsContainer${index}`}> - <EntryContent - key={`entry${index}`} - entry={entry} - index={index} - dataTestSubj={dataTestSubj} - showValueListModal={showValueListModal} - /> - {nestedEntries?.length - ? nestedEntries.map((nestedEntry: Entry, nestedIndex: number) => ( - <EntryContent - key={`nestedEntry${index}${nestedIndex}`} - entry={nestedEntry} - index={nestedIndex} - isNestedEntry={true} - dataTestSubj={dataTestSubj} - showValueListModal={showValueListModal} - /> - )) - : null} - </div> - ); - })} - </EuiPanel> - ); - } -); -ExceptionItemCardConditions.displayName = 'ExceptionItemCardConditions'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/index.tsx deleted file mode 100644 index 0d8285924c31..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/index.tsx +++ /dev/null @@ -1,39 +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, { memo, useMemo } from 'react'; -import { EuiExpression } from '@elastic/eui'; - -import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { OS_LABELS } from '../conditions.config'; -import * as i18n from '../../translations'; - -export interface OsConditionsProps { - dataTestSubj?: string; - os: ExceptionListItemSchema['os_types']; -} - -export const OsCondition = memo<OsConditionsProps>(({ os, dataTestSubj }) => { - const osLabel = useMemo(() => { - return os.map((osValue) => OS_LABELS[osValue] ?? osValue).join(', '); - }, [os]); - return osLabel ? ( - <div data-test-subj={`${dataTestSubj || ''}Os`}> - <strong> - <EuiExpression data-test-subj="osLabel" description="" value={i18n.CONDITION_OS} /> - <EuiExpression - data-test-subj="osValue" - description={i18n.CONDITION_OPERATOR_TYPE_MATCH} - value={osLabel} - /> - </strong> - </div> - ) : null; -}); -OsCondition.displayName = 'OsCondition'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/types.ts b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/types.ts deleted file mode 100644 index 0400ca6548d9..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/types.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { - EntryExists, - EntryList, - EntryMatch, - EntryMatchAny, - EntryMatchWildcard, - EntryNested, - ExceptionListItemSchema, -} from '@kbn/securitysolution-io-ts-list-types'; -import { ElementType } from 'react'; - -export type Entry = - | EntryExists - | EntryList - | EntryMatch - | EntryMatchAny - | EntryMatchWildcard - | EntryNested; - -export type Entries = ExceptionListItemSchema['entries']; -export interface CriteriaConditionsProps { - entries: Entries; - dataTestSubj: string; - os?: ExceptionListItemSchema['os_types']; - showValueListModal: ElementType; -} diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/index.tsx deleted file mode 100644 index 72b02cc79a79..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/index.tsx +++ /dev/null @@ -1,46 +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, { memo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; -import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { HeaderMenu } from '../../header_menu'; - -export interface ExceptionItemCardHeaderProps { - item: ExceptionListItemSchema; - actions: Array<{ key: string; icon: string; label: string | boolean; onClick: () => void }>; - disableActions?: boolean; - dataTestSubj: string; -} - -export const ExceptionItemCardHeader = memo<ExceptionItemCardHeaderProps>( - ({ item, actions, disableActions = false, dataTestSubj }) => { - return ( - <EuiFlexGroup responsive data-test-subj={dataTestSubj} justifyContent="spaceBetween"> - <EuiFlexItem grow={9}> - <EuiTitle size="xs" textTransform="uppercase" data-test-subj={`${dataTestSubj}Title`}> - <h3>{item.name}</h3> - </EuiTitle> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <HeaderMenu - iconType="boxesHorizontal" - disableActions={disableActions} - actions={actions} - aria-label="Exception item actions menu" - dataTestSubj={dataTestSubj} - anchorPosition="downCenter" - /> - </EuiFlexItem> - </EuiFlexGroup> - ); - } -); - -ExceptionItemCardHeader.displayName = 'ExceptionItemCardHeader'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.ts b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.ts deleted file mode 100644 index 26af37709233..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './conditions'; -export * from './header'; -export * from './meta'; -export * from './comments'; -export * from './exception_item_card'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/details_info.test.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/details_info.test.tsx deleted file mode 100644 index cbb48e172322..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/details_info.test.tsx +++ /dev/null @@ -1,39 +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 } from '@testing-library/react'; -import { MetaInfoDetails } from '.'; - -describe('MetaInfoDetails', () => { - it('should render lastUpdate as string', () => { - const wrapper = render( - <MetaInfoDetails - dataTestSubj="MetaInfoDetails" - label="created_by" - lastUpdate="last update" - lastUpdateValue="value" - /> - ); - expect(wrapper.container).toMatchSnapshot(); - expect(wrapper.getByTestId('MetaInfoDetailslastUpdate')).toHaveTextContent('last update'); - }); - it('should render lastUpdate as JSX Element', () => { - const wrapper = render( - <MetaInfoDetails - dataTestSubj="MetaInfoDetails" - label="created_by" - lastUpdate={<p>Last update value</p>} - lastUpdateValue="value" - /> - ); - expect(wrapper.container).toMatchSnapshot(); - expect(wrapper.getByTestId('MetaInfoDetailslastUpdate')).toHaveTextContent('Last update value'); - }); -}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/index.tsx deleted file mode 100644 index cc9eaa813aa9..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/index.tsx +++ /dev/null @@ -1,69 +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, { memo } from 'react'; -import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; -import * as i18n from '../../translations'; - -interface MetaInfoDetailsProps { - label: string; - lastUpdate: JSX.Element | string; - lastUpdateValue?: string; - dataTestSubj?: string; -} - -const euiBadgeFontFamily = css` - font-family: ${euiThemeVars.euiFontFamily}; -`; -export const MetaInfoDetails = memo<MetaInfoDetailsProps>( - ({ label, lastUpdate, lastUpdateValue, dataTestSubj }) => { - return ( - <EuiFlexGroup - data-test-subj={`${dataTestSubj || ''}metaInfoDetails`} - alignItems="center" - gutterSize="s" - wrap - responsive - > - <EuiFlexItem grow={false}> - <EuiText size="xs" css={euiBadgeFontFamily}> - {label} - </EuiText> - </EuiFlexItem> - <EuiFlexItem grow={false} data-test-subj={`${dataTestSubj || ''}lastUpdate`}> - <EuiBadge color="default" css={euiBadgeFontFamily}> - {lastUpdate} - </EuiBadge> - </EuiFlexItem> - {lastUpdateValue != null && ( - <> - <EuiFlexItem grow={false}> - <EuiText size="xs" css={euiBadgeFontFamily}> - {i18n.EXCEPTION_ITEM_CARD_META_BY} - </EuiText> - </EuiFlexItem> - <EuiFlexItem grow={false} data-test-subj={`${dataTestSubj || ''}lastUpdateValue`}> - <EuiFlexGroup responsive gutterSize="xs" alignItems="center"> - <EuiFlexItem grow={false}> - <EuiBadge color="hollow" css={euiBadgeFontFamily}> - {lastUpdateValue} - </EuiBadge> - </EuiFlexItem> - </EuiFlexGroup> - </EuiFlexItem> - </> - )} - </EuiFlexGroup> - ); - } -); - -MetaInfoDetails.displayName = 'MetaInfoDetails'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx deleted file mode 100644 index d9c0f84b02d0..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx +++ /dev/null @@ -1,125 +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, { memo, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; - -import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; -import * as i18n from '../translations'; -import type { Rule } from '../../types'; -import { MetaInfoDetails } from './details_info'; -import { HeaderMenu } from '../../header_menu'; -import { generateLinkedRulesMenuItems } from '../../generate_linked_rules_menu_item'; - -const itemCss = css` - border-right: 1px solid #d3dae6; - padding: ${euiThemeVars.euiSizeS} ${euiThemeVars.euiSizeM} ${euiThemeVars.euiSizeS} 0; -`; - -export interface ExceptionItemCardMetaInfoProps { - item: ExceptionListItemSchema; - rules: Rule[]; - dataTestSubj: string; - formattedDateComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common - securityLinkAnchorComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common -} - -export const ExceptionItemCardMetaInfo = memo<ExceptionItemCardMetaInfoProps>( - ({ item, rules, dataTestSubj, securityLinkAnchorComponent, formattedDateComponent }) => { - const FormattedDateComponent = formattedDateComponent; - - const referencedLinks = useMemo( - () => - generateLinkedRulesMenuItems({ - dataTestSubj, - linkedRules: rules, - securityLinkAnchorComponent, - }), - [dataTestSubj, rules, securityLinkAnchorComponent] - ); - - const isExpired = useMemo( - () => (item.expire_time ? new Date(item.expire_time) <= new Date() : false), - [item] - ); - - return ( - <EuiFlexGroup alignItems="center" responsive gutterSize="s" data-test-subj={dataTestSubj}> - {FormattedDateComponent !== null && ( - <> - <EuiFlexItem css={itemCss} grow={false}> - <MetaInfoDetails - label={i18n.EXCEPTION_ITEM_CARD_CREATED_LABEL} - lastUpdate={ - <FormattedDateComponent - data-test-subj={`{dataTestSubj||''}formattedDateComponentCreatedBy`} - fieldName="created_at" - value={item.created_at} - /> - } - lastUpdateValue={item.created_by} - dataTestSubj={`${dataTestSubj || ''}CreatedBy`} - /> - </EuiFlexItem> - - <EuiFlexItem css={itemCss} grow={false}> - <MetaInfoDetails - label={i18n.EXCEPTION_ITEM_CARD_UPDATED_LABEL} - lastUpdate={ - <FormattedDateComponent - data-test-subj={`{dataTestSubj||''}formattedDateComponentUpdatedBy`} - fieldName="updated_at" - value={item.updated_at} - /> - } - lastUpdateValue={item.updated_by} - dataTestSubj={`${dataTestSubj || ''}UpdatedBy`} - /> - </EuiFlexItem> - {item.expire_time != null && ( - <> - <EuiFlexItem css={itemCss} grow={false}> - <MetaInfoDetails - label={ - isExpired - ? i18n.EXCEPTION_ITEM_CARD_EXPIRED_LABEL - : i18n.EXCEPTION_ITEM_CARD_EXPIRES_LABEL - } - lastUpdate={ - <FormattedDateComponent - data-test-subj={`{dataTestSubj||''}formattedDateComponentExpireTime`} - fieldName="expire_time" - value={item.expire_time} - /> - } - dataTestSubj={`${dataTestSubj || ''}ExpireTime`} - /> - </EuiFlexItem> - </> - )} - </> - )} - <EuiFlexItem> - <HeaderMenu - emptyButton - useCustomActions - iconType="list" - actions={referencedLinks} - disableActions={false} - text={i18n.AFFECTED_RULES(rules.length)} - dataTestSubj={dataTestSubj} - /> - </EuiFlexItem> - </EuiFlexGroup> - ); - } -); -ExceptionItemCardMetaInfo.displayName = 'ExceptionItemCardMetaInfo'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/translations.ts b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/translations.ts deleted file mode 100644 index 78a0dac775cf..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/translations.ts +++ /dev/null @@ -1,181 +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 { i18n } from '@kbn/i18n'; - -export const exceptionItemCardEditButton = (listType: string) => - i18n.translate('exceptionList-components.exceptions.exceptionItem.card.editItemButton', { - values: { listType }, - defaultMessage: 'Edit {listType} exception', - }); - -export const exceptionItemCardDeleteButton = (listType: string) => - i18n.translate('exceptionList-components.exceptions.exceptionItem.card.deleteItemButton', { - values: { listType }, - defaultMessage: 'Delete {listType} exception', - }); - -export const EXCEPTION_ITEM_CARD_CREATED_LABEL = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.createdLabel', - { - defaultMessage: 'Created', - } -); - -export const EXCEPTION_ITEM_CARD_UPDATED_LABEL = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.updatedLabel', - { - defaultMessage: 'Updated', - } -); - -export const EXCEPTION_ITEM_CARD_EXPIRES_LABEL = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.expiresLabel', - { - defaultMessage: 'Expires at', - } -); - -export const EXCEPTION_ITEM_CARD_EXPIRED_LABEL = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.expiredLabel', - { - defaultMessage: 'Expired at', - } -); - -export const EXCEPTION_ITEM_CARD_META_BY = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.metaDetailsBy', - { - defaultMessage: 'by', - } -); - -export const exceptionItemCardCommentsAccordion = (comments: number) => - i18n.translate('exceptionList-components.exceptions.exceptionItem.card.showCommentsLabel', { - values: { comments }, - defaultMessage: 'Show {comments, plural, =1 {comment} other {comments}} ({comments})', - }); - -export const CONDITION_OPERATOR_TYPE_MATCH = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchOperator', - { - defaultMessage: 'IS', - } -); - -export const CONDITION_OPERATOR_TYPE_NOT_MATCH = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchOperator.not', - { - defaultMessage: 'IS NOT', - } -); - -export const CONDITION_OPERATOR_TYPE_WILDCARD_MATCHES = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.wildcardMatchesOperator', - { - defaultMessage: 'MATCHES', - } -); - -export const CONDITION_OPERATOR_TYPE_WILDCARD_DOES_NOT_MATCH = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.wildcardDoesNotMatchOperator', - { - defaultMessage: 'DOES NOT MATCH', - } -); - -export const CONDITION_OPERATOR_TYPE_NESTED = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.nestedOperator', - { - defaultMessage: 'has', - } -); - -export const CONDITION_OPERATOR_TYPE_MATCH_ANY = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchAnyOperator', - { - defaultMessage: 'is one of', - } -); - -export const CONDITION_OPERATOR_TYPE_NOT_MATCH_ANY = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchAnyOperator.not', - { - defaultMessage: 'is not one of', - } -); - -export const CONDITION_OPERATOR_TYPE_EXISTS = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.existsOperator', - { - defaultMessage: 'exists', - } -); - -export const CONDITION_OPERATOR_TYPE_DOES_NOT_EXIST = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.existsOperator.not', - { - defaultMessage: 'does not exist', - } -); - -export const CONDITION_OPERATOR_TYPE_LIST = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.listOperator', - { - defaultMessage: 'included in', - } -); - -export const CONDITION_OPERATOR_TYPE_NOT_IN_LIST = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.listOperator.not', - { - defaultMessage: 'is not included in', - } -); - -export const CONDITION_AND = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.and', - { - defaultMessage: 'AND', - } -); - -export const CONDITION_OS = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.os', - { - defaultMessage: 'OS', - } -); - -export const OS_WINDOWS = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.windows', - { - defaultMessage: 'Windows', - } -); - -export const OS_LINUX = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.linux', - { - defaultMessage: 'Linux', - } -); - -export const OS_MAC = i18n.translate( - 'exceptionList-components.exceptions.exceptionItem.card.conditions.macos', - { - defaultMessage: 'Mac', - } -); - -export const AFFECTED_RULES = (numRules: number) => - i18n.translate('exceptionList-components.exceptions.card.exceptionItem.affectedRules', { - values: { numRules }, - defaultMessage: 'Affects {numRules} {numRules, plural, =1 {rule} other {rules}}', - }); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_items/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/exception_items/index.tsx deleted file mode 100644 index edad9aecdb0d..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_items/index.tsx +++ /dev/null @@ -1,155 +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, { ElementType } from 'react'; -import { css } from '@emotion/react'; -import type { FC } from 'react'; -import { EuiCommentProps, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; - -import type { - CommentsArray, - ExceptionListItemSchema, - ExceptionListTypeEnum, -} from '@kbn/securitysolution-io-ts-list-types'; - -import { euiThemeVars } from '@kbn/ui-theme'; -import { EmptyViewerState, ExceptionItemCard, Pagination, PaginationProps } from '../..'; - -import type { - RuleReferences, - ExceptionListItemIdentifiers, - ViewerStatus, - 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; - isReadOnly: boolean; - emptyViewerTitle?: string; - emptyViewerBody?: string; - emptyViewerButtonText?: string; - exceptions: ExceptionListItemSchema[]; - listType: ExceptionListTypeEnum; - ruleReferences: RuleReferences; - pagination: PaginationProps['pagination']; - editActionLabel?: string; - deleteActionLabel?: string; - dataTestSubj?: string; - securityLinkAnchorComponent: ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common - formattedDateComponent: ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common - exceptionsUtilityComponent: ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common - getFormattedComments: (comments: CommentsArray) => EuiCommentProps[]; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common - onCreateExceptionListItem?: () => void; - onDeleteException: (arg: ExceptionListItemIdentifiers) => void; - onEditExceptionItem: (item: ExceptionListItemSchema) => void; - onPaginationChange: (arg: GetExceptionItemProps) => void; - showValueListModal: ElementType; -} - -const ExceptionItemsComponent: FC<ExceptionItemsProps> = ({ - lastUpdated, - viewerStatus, - isReadOnly, - exceptions, - listType, - ruleReferences, - emptyViewerTitle, - emptyViewerBody, - emptyViewerButtonText, - pagination, - dataTestSubj, - editActionLabel, - deleteActionLabel, - securityLinkAnchorComponent, - exceptionsUtilityComponent, - formattedDateComponent, - getFormattedComments, - onPaginationChange, - onDeleteException, - onEditExceptionItem, - onCreateExceptionListItem, - showValueListModal, -}) => { - const ExceptionsUtility = exceptionsUtilityComponent; - if (!exceptions.length || viewerStatus) - return ( - <EmptyViewerState - isReadOnly={isReadOnly} - title={emptyViewerTitle} - viewerStatus={viewerStatus} - buttonText={emptyViewerButtonText} - body={emptyViewerBody} - onEmptyButtonStateClick={onCreateExceptionListItem} - /> - ); - const ShowValueListModal = showValueListModal; - return ( - <> - <ExceptionsUtility pagination={pagination} lastUpdated={lastUpdated} /> - <EuiFlexGroup direction="column" gutterSize="none" className="eui-yScrollWithShadows"> - <EuiFlexItem grow={false}> - <EuiFlexGroup - css={exceptionItemCss} - data-test-subj={`${dataTestSubj || ''}exceptionsContainer`} - direction="column" - gutterSize="s" - > - {exceptions.map((exception) => ( - <EuiFlexItem - data-test-subj={`${dataTestSubj || ''}exceptionItemContainer`} - grow={false} - key={exception.id} - > - <ExceptionItemCard - key={`${exception.id}exceptionItemCardKey`} - dataTestSubj={`${dataTestSubj || ''}exceptionItemCard`} - disableActions={isReadOnly} - exceptionItem={exception} - listType={listType} - ruleReferences={ - Object.keys(ruleReferences).length && ruleReferences[exception.list_id] - ? ruleReferences[exception.list_id].referenced_rules - : [] - } - editActionLabel={editActionLabel} - deleteActionLabel={deleteActionLabel} - onDeleteException={onDeleteException} - onEditException={onEditExceptionItem} - securityLinkAnchorComponent={securityLinkAnchorComponent} - formattedDateComponent={formattedDateComponent} - getFormattedComments={getFormattedComments} - showValueListModal={ShowValueListModal} - /> - </EuiFlexItem> - ))} - </EuiFlexGroup> - </EuiFlexItem> - </EuiFlexGroup> - <Pagination - dataTestSubj={`${dataTestSubj || ''}pagination`} - pagination={pagination} - onPaginationChange={onPaginationChange} - /> - </> - ); -}; - -ExceptionItemsComponent.displayName = 'ExceptionItemsComponent'; - -export const ExceptionItems = React.memo(ExceptionItemsComponent); - -ExceptionItems.displayName = 'ExceptionsItems'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/index.tsx deleted file mode 100644 index d68ac8f114a0..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/index.tsx +++ /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 React, { ElementType, ReactElement } from 'react'; -import { EuiContextMenuItem, EuiFlexGroup, EuiFlexItem, EuiIcon, IconType } from '@elastic/eui'; -import { Rule } from '../types'; -import { itemContentCss, containerCss } from './menu_link.styles'; - -interface MenuItemLinkedRulesProps { - leftIcon?: IconType; - dataTestSubj?: string; - linkedRules: Rule[]; - securityLinkAnchorComponent: ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common -} - -export const generateLinkedRulesMenuItems = ({ - dataTestSubj, - linkedRules, - securityLinkAnchorComponent, - leftIcon = '', -}: MenuItemLinkedRulesProps): ReactElement[] | null => { - if (!linkedRules.length || securityLinkAnchorComponent === null) return null; - - const SecurityLinkAnchor = securityLinkAnchorComponent; - return linkedRules.map((rule) => { - return ( - <EuiContextMenuItem - css={linkedRules.length > 1 ? containerCss : ''} - data-test-subj={`${dataTestSubj || ''}ActionItem${rule.id}`} - key={rule.id} - > - <EuiFlexGroup gutterSize="s" css={itemContentCss}> - {leftIcon ? ( - <EuiFlexItem data-test-subj={`${dataTestSubj || ''}LeftIcon`} grow={false}> - <EuiIcon type={leftIcon} /> - </EuiFlexItem> - ) : null} - <EuiFlexItem css={itemContentCss}> - <SecurityLinkAnchor external referenceName={rule.name} referenceId={rule.id} /> - </EuiFlexItem> - </EuiFlexGroup> - </EuiContextMenuItem> - ); - }); -}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/menu_link.styles.ts b/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/menu_link.styles.ts deleted file mode 100644 index 42f470ce39dd..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/menu_link.styles.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { 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/packages/kbn-securitysolution-exception-list-components/src/header_menu/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/header_menu/index.tsx deleted file mode 100644 index 2f39959c33fe..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/header_menu/index.tsx +++ /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", 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, ReactElement, useMemo, useState } from 'react'; -import { - EuiButtonEmpty, - EuiButtonEmptyProps, - EuiButtonIcon, - EuiContextMenuItem, - EuiContextMenuPanel, - EuiFlexGroup, - EuiPopover, - PanelPaddingSize, - PopoverAnchorPosition, -} from '@elastic/eui'; - -import { css } from '@emotion/react'; - -export interface Action { - key: string; - icon: string; - label: string | boolean; - disabled?: boolean; - onClick: (e: React.MouseEvent<Element, MouseEvent>) => void; -} - -interface HeaderMenuComponentProps { - disableActions: boolean; - actions: Action[] | ReactElement[] | null; - text?: string; - iconType?: EuiButtonEmptyProps['iconType']; - iconSide?: EuiButtonEmptyProps['iconSide']; - dataTestSubj?: string; - emptyButton?: boolean; - useCustomActions?: boolean; - anchorPosition?: PopoverAnchorPosition; - panelPaddingSize?: PanelPaddingSize; -} - -const popoverHeightStyle = css` - max-height: 300px; - height: 100%; - overflow-x: hidden; - overflow-y: auto; -`; -const HeaderMenuComponent: FC<HeaderMenuComponentProps> = ({ - text, - dataTestSubj, - actions, - disableActions, - emptyButton, - useCustomActions, - iconType, - iconSide = 'left', - anchorPosition = 'downCenter', - panelPaddingSize = 's', -}) => { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - - const onAffectedRulesClick = () => setIsPopoverOpen((isOpen) => !isOpen); - const onClosePopover = () => setIsPopoverOpen(false); - - const itemActions = useMemo(() => { - if (useCustomActions || actions === null) return actions; - return (actions as Action[]).map((action) => ( - <EuiContextMenuItem - data-test-subj={`${dataTestSubj || ''}ActionItem${action.key}`} - key={action.key} - icon={action.icon} - disabled={action.disabled} - layoutAlign="center" - onClick={(e) => { - onClosePopover(); - if (typeof action.onClick === 'function') action.onClick(e); - }} - > - {action.label} - </EuiContextMenuItem> - )); - }, [actions, dataTestSubj, useCustomActions]); - - return ( - <EuiFlexGroup responsive> - <EuiPopover - button={ - emptyButton ? ( - <EuiButtonEmpty - isDisabled={disableActions} - onClick={onAffectedRulesClick} - iconType={iconType ? iconType : undefined} - iconSide={iconSide} - data-test-subj={`${dataTestSubj || ''}EmptyButton`} - aria-label="Header menu Button Empty" - > - {text} - </EuiButtonEmpty> - ) : ( - <EuiButtonIcon - isDisabled={disableActions} - onClick={onAffectedRulesClick} - iconType={iconType ? iconType : 'boxesHorizontal'} - data-test-subj={`${dataTestSubj || ''}ButtonIcon`} - aria-label="Header menu Button Icon" - > - {text} - </EuiButtonIcon> - ) - } - onClick={(e) => e.stopPropagation()} - panelPaddingSize={panelPaddingSize} - isOpen={isPopoverOpen} - closePopover={onClosePopover} - anchorPosition={anchorPosition} - data-test-subj={`${dataTestSubj || ''}Items`} - > - {!itemActions ? null : ( - <EuiContextMenuPanel - css={popoverHeightStyle} - className="eui-scrollBar" - data-test-subj={`${dataTestSubj || ''}MenuPanel`} - size="s" - items={itemActions as ReactElement[]} - /> - )} - </EuiPopover> - </EuiFlexGroup> - ); -}; -HeaderMenuComponent.displayName = 'HeaderMenuComponent'; - -export const HeaderMenu = React.memo(HeaderMenuComponent); - -HeaderMenu.displayName = 'HeaderMenu'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/index.tsx deleted file mode 100644 index 82aec99bebb3..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/index.tsx +++ /dev/null @@ -1,111 +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 } from 'react'; -import { - EuiButton, - EuiButtonEmpty, - EuiFieldText, - EuiForm, - EuiFormRow, - EuiModal, - EuiModalBody, - EuiModalFooter, - EuiModalHeader, - EuiModalHeaderTitle, - EuiTextArea, - EuiProgress, -} from '@elastic/eui'; -import * as i18n from '../../translations'; -import { ListDetails } from '../../types'; -import { useEditModal } from './use_edit_modal'; - -interface EditModalProps { - listDetails: ListDetails; - onSave: (newListDetails: ListDetails) => void; - onCancel: () => void; -} - -const EditModalComponent: FC<EditModalProps> = ({ listDetails, onSave, onCancel }) => { - const { error, modalFormId, newListDetails, showProgress, onBlur, onSubmit, onChange } = - useEditModal({ - listDetails, - onSave, - }); - return ( - <EuiModal data-test-subj="EditModal" onClose={onCancel} initialFocus="[name=popswitch]"> - {showProgress && ( - <EuiProgress data-test-subj="editModalProgess" size="xs" position="absolute" /> - )} - <EuiModalHeader> - <EuiModalHeaderTitle data-test-subj="editModalTitle"> - {i18n.EXCEPTION_LIST_HEADER_EDIT_MODAL_TITLE(listDetails.name)} - </EuiModalHeaderTitle> - </EuiModalHeader> - - <EuiModalBody> - <EuiForm - id={modalFormId} - data-test-subj="editModalForm" - component="form" - onSubmit={onSubmit} - > - <EuiFormRow - error={error} - isInvalid={!!error} - fullWidth - label={i18n.EXCEPTION_LIST_HEADER_NAME_TEXTBOX} - > - <EuiFieldText - fullWidth - isInvalid={!!error} - onBlur={onBlur} - data-test-subj="editModalNameTextField" - name="name" - value={newListDetails.name} - onChange={onChange} - /> - </EuiFormRow> - - <EuiFormRow fullWidth label={i18n.EXCEPTION_LIST_HEADER_DESCRIPTION_TEXTBOX}> - <EuiTextArea - fullWidth - data-test-subj="editModalDescriptionTextField" - name="description" - value={newListDetails.description} - onChange={onChange} - onBlur={onBlur} - /> - </EuiFormRow> - </EuiForm> - </EuiModalBody> - - <EuiModalFooter> - <EuiButtonEmpty data-test-subj="editModalCancelBtn" onClick={onCancel}> - {i18n.EXCEPTION_LIST_HEADER_EDIT_MODAL_CANCEL_BUTTON} - </EuiButtonEmpty> - - <EuiButton - data-test-subj="editModalSaveBtn" - type="submit" - form={modalFormId} - onClick={onSubmit} - fill - > - {i18n.EXCEPTION_LIST_HEADER_EDIT_MODAL_SAVE_BUTTON} - </EuiButton> - </EuiModalFooter> - </EuiModal> - ); -}; -EditModalComponent.displayName = 'EditModalComponent'; - -export const EditModal = React.memo(EditModalComponent); - -EditModal.displayName = 'EditModal'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/list_header/index.tsx deleted file mode 100644 index 849dd4ef1ba8..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/index.tsx +++ /dev/null @@ -1,138 +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 type { FC } from 'react'; -import { EuiIcon, EuiPageHeader, EuiText } from '@elastic/eui'; -import * as i18n from '../translations'; -import { textCss, descriptionContainerCss, backTextCss } from './list_header.styles'; -import { MenuItems } from './menu_items'; -import { TextWithEdit } from '../text_with_edit'; -import { EditModal } from './edit_modal'; -import { ListDetails, Rule } from '../types'; -import { useExceptionListHeader } from './use_list_header'; -import { textWithEditContainerCss } from '../text_with_edit/text_with_edit.styles'; - -interface ExceptionListHeaderComponentProps { - name: string; - description?: string; - listId: string; - isReadonly: boolean; - linkedRules: Rule[]; - dataTestSubj?: string; - backOptions: BackOptions; - canUserEditList?: boolean; - securityLinkAnchorComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common - onEditListDetails: (listDetails: ListDetails) => void; - onDeleteList: () => void; - onManageRules: () => void; - onExportList: () => void; - onDuplicateList: () => void; -} - -export interface BackOptions { - pageId: string; - path: string; - dataTestSubj?: string; - onNavigate: (path: string) => void; -} -const ExceptionListHeaderComponent: FC<ExceptionListHeaderComponentProps> = ({ - name, - description, - listId, - linkedRules, - isReadonly, - dataTestSubj, - securityLinkAnchorComponent, - backOptions, - canUserEditList = true, - onEditListDetails, - onDeleteList, - onManageRules, - onExportList, - onDuplicateList, -}) => { - const { isModalVisible, listDetails, onEdit, onSave, onCancel } = useExceptionListHeader({ - name, - description, - onEditListDetails, - }); - return ( - <div> - <EuiPageHeader - bottomBorder - paddingSize="none" - pageTitle={ - <TextWithEdit - dataTestSubj={`${dataTestSubj || ''}Title`} - text={listDetails.name || i18n.EXCEPTION_LIST_HEADER_NAME} - isReadonly={isReadonly || !canUserEditList} - onEdit={onEdit} - /> - } - responsive - data-test-subj={`${dataTestSubj || ''}PageHeader`} - description={ - <div css={descriptionContainerCss}> - <TextWithEdit - dataTestSubj={`${dataTestSubj || ''}Description`} - textCss={textCss} - isReadonly={isReadonly || !canUserEditList} - text={listDetails.description || i18n.EXCEPTION_LIST_HEADER_DESCRIPTION} - onEdit={onEdit} - /> - <div css={textWithEditContainerCss} data-test-subj={`${dataTestSubj || ''}ListID`}> - <EuiText css={textCss}>{i18n.EXCEPTION_LIST_HEADER_LIST_ID}:</EuiText> - <EuiText css={textCss}>{listId}</EuiText> - </div> - </div> - } - rightSideItems={[ - <MenuItems - dataTestSubj={`${dataTestSubj || ''}RightSideMenuItems`} - linkedRules={linkedRules} - isReadonly={isReadonly} - canUserEditList={canUserEditList} - securityLinkAnchorComponent={securityLinkAnchorComponent} - onDeleteList={onDeleteList} - onManageRules={onManageRules} - onExportList={onExportList} - onDuplicateList={onDuplicateList} - />, - ]} - breadcrumbs={[ - { - text: ( - <div data-test-subj={`${dataTestSubj || ''}Breadcrumb`} css={backTextCss}> - <EuiIcon size="s" type="arrowLeft" /> - {i18n.EXCEPTION_LIST_HEADER_BREADCRUMB} - </div> - ), - color: 'primary', - 'aria-current': false, - href: backOptions.path, - onClick: (e) => { - e.preventDefault(); - backOptions.onNavigate(backOptions.path); - }, - }, - ]} - /> - {isModalVisible && ( - <EditModal listDetails={listDetails} onSave={onSave} onCancel={onCancel} /> - )} - </div> - ); -}; - -ExceptionListHeaderComponent.displayName = 'ExceptionListHeaderComponent'; - -export const ExceptionListHeader = React.memo(ExceptionListHeaderComponent); - -ExceptionListHeader.displayName = 'ExceptionListHeader'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.styles.ts b/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.styles.ts deleted file mode 100644 index c3cba9f9dcdb..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.styles.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", 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 { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; - -export const headerMenuCss = css` - border-right: 1px solid #d3dae6; - padding: ${euiThemeVars.euiSizeXS} ${euiThemeVars.euiSizeL} ${euiThemeVars.euiSizeXS} 0; -`; - -export const noLinkedRulesCss = css` - width: max-content; -`; - -export const textCss = css` - font-size: ${euiThemeVars.euiFontSize}; - color: ${euiThemeVars.euiTextSubduedColor}; - margin-left: ${euiThemeVars.euiSizeXS}; -`; -export const descriptionContainerCss = css` - margin-top: -${euiThemeVars.euiSizeL}; - margin-bottom: -${euiThemeVars.euiSizeL}; -`; - -export const backTextCss = css` - font-size: ${euiThemeVars.euiFontSizeXS}; -`; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/index.tsx deleted file mode 100644 index f0aecf21b6c7..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/index.tsx +++ /dev/null @@ -1,136 +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 { EuiButton, EuiFlexGroup, EuiFlexItem, EuiTextColor } from '@elastic/eui'; -import React, { FC, useMemo } from 'react'; -import { HeaderMenu } from '../../header_menu'; -import { headerMenuCss, noLinkedRulesCss } from '../list_header.styles'; -import * as i18n from '../../translations'; -import { Rule } from '../../types'; -import { generateLinkedRulesMenuItems } from '../../generate_linked_rules_menu_item'; -interface MenuItemsProps { - isReadonly: boolean; - dataTestSubj?: string; - linkedRules: Rule[]; - canUserEditList?: boolean; - securityLinkAnchorComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common - onDeleteList: () => void; - onManageRules: () => void; - onExportList: () => void; - onDuplicateList: () => void; -} - -const MenuItemsComponent: FC<MenuItemsProps> = ({ - dataTestSubj, - linkedRules, - securityLinkAnchorComponent, - isReadonly, - canUserEditList = true, - onDeleteList, - onManageRules, - onExportList, - onDuplicateList, -}) => { - const referencedLinks = useMemo( - () => - generateLinkedRulesMenuItems({ - leftIcon: 'check', - dataTestSubj, - linkedRules, - securityLinkAnchorComponent, - }), - [dataTestSubj, linkedRules, securityLinkAnchorComponent] - ); - return ( - <EuiFlexGroup - direction="row" - alignItems="baseline" - justifyContent="center" - responsive - data-test-subj={`${dataTestSubj || ''}Container`} - gutterSize="l" - > - <EuiFlexItem css={headerMenuCss}> - {linkedRules.length ? ( - <HeaderMenu - dataTestSubj={`${dataTestSubj || ''}LinkedRulesMenu`} - emptyButton - useCustomActions - text={i18n.EXCEPTION_LIST_HEADER_LINKED_RULES(linkedRules.length)} - actions={referencedLinks} - disableActions={false} - iconType="arrowDown" - iconSide="right" - panelPaddingSize="none" - /> - ) : ( - <EuiTextColor data-test-subj="noLinkedRules" css={noLinkedRulesCss} color="subdued"> - {i18n.EXCEPTION_LIST_HEADER_LINKED_RULES(linkedRules.length)} - </EuiTextColor> - )} - </EuiFlexItem> - - {canUserEditList && ( - <EuiFlexItem> - <EuiButton - data-test-subj={`${dataTestSubj || ''}LinkRulesButton`} - fill - onClick={() => { - if (typeof onManageRules === 'function') onManageRules(); - }} - > - {i18n.EXCEPTION_LIST_HEADER_LINK_RULES_BUTTON} - </EuiButton> - </EuiFlexItem> - )} - <EuiFlexItem> - <HeaderMenu - iconType="boxesHorizontal" - dataTestSubj={`${dataTestSubj || ''}MenuActions`} - actions={[ - { - key: '1', - icon: 'exportAction', - label: i18n.EXCEPTION_LIST_HEADER_EXPORT_ACTION, - onClick: () => { - if (typeof onExportList === 'function') onExportList(); - }, - }, - { - key: '2', - icon: 'copy', - label: i18n.EXCEPTION_LIST_HEADER_DUPLICATE_ACTION, - onClick: () => { - if (typeof onDuplicateList === 'function') onDuplicateList(); - }, - disabled: !canUserEditList, - }, - { - key: '3', - icon: 'trash', - label: i18n.EXCEPTION_LIST_HEADER_DELETE_ACTION, - onClick: () => { - if (typeof onDeleteList === 'function') onDeleteList(); - }, - disabled: !canUserEditList, - }, - ]} - disableActions={isReadonly} - anchorPosition="downCenter" - /> - </EuiFlexItem> - </EuiFlexGroup> - ); -}; - -MenuItemsComponent.displayName = 'MenuItemsComponent'; - -export const MenuItems = React.memo(MenuItemsComponent); - -MenuItems.displayName = 'MenuItems'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.ts b/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.ts deleted file mode 100644 index 676dd08a53be..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.ts +++ /dev/null @@ -1,46 +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 { useState } from 'react'; -import { ListDetails } from '../types'; - -interface UseExceptionListHeaderProps { - name: string; - description?: string; - onEditListDetails: (listDetails: ListDetails) => void; -} -export const useExceptionListHeader = ({ - name, - description, - onEditListDetails, -}: UseExceptionListHeaderProps) => { - const [isModalVisible, setIsModalVisible] = useState(false); - const [listDetails, setListDetails] = useState<ListDetails>({ name, description }); - const onEdit = () => { - setIsModalVisible(true); - }; - const onSave = (newListDetails: ListDetails) => { - setListDetails(newListDetails); - if (typeof onEditListDetails === 'function') onEditListDetails(newListDetails); - setTimeout(() => { - setIsModalVisible(false); - }, 200); - }; - const onCancel = () => { - setIsModalVisible(false); - }; - - return { - isModalVisible, - listDetails, - onEdit, - onSave, - onCancel, - }; -}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/mocks/comments.mock.tsx b/packages/kbn-securitysolution-exception-list-components/src/mocks/comments.mock.tsx deleted file mode 100644 index 4c9b813463ca..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/mocks/comments.mock.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", 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 type { Comment, CommentsArray } from '@kbn/securitysolution-io-ts-list-types'; - -export const getCommentsMock = (): Comment => ({ - comment: 'some old comment', - created_at: '2020-04-20T15:25:31.830Z', - created_by: 'some user', - id: 'uuid_here', -}); - -export const getCommentsArrayMock = (): CommentsArray => [getCommentsMock(), getCommentsMock()]; - -export const mockGetFormattedComments = () => - getCommentsArrayMock().map((comment) => ({ - username: comment.created_by, - children: <p>{comment.comment}</p>, - })); diff --git a/packages/kbn-securitysolution-exception-list-components/src/mocks/entry.mock.ts b/packages/kbn-securitysolution-exception-list-components/src/mocks/entry.mock.ts deleted file mode 100644 index a39fe947caa2..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/mocks/entry.mock.ts +++ /dev/null @@ -1,30 +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 { Entry } from '../exception_item_card/conditions/types'; - -export const includedListTypeEntry: Entry = { - field: '', - operator: 'included', - type: 'list', - list: { id: 'list_id', type: 'boolean' }, -}; - -export const includedMatchTypeEntry: Entry = { - field: '', - operator: 'included', - type: 'match', - value: 'matches value', -}; - -export const includedExistsTypeEntry: Entry = { - field: '', - operator: 'included', - type: 'exists', -}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/mocks/exception_list_item_schema.mock.ts b/packages/kbn-securitysolution-exception-list-components/src/mocks/exception_list_item_schema.mock.ts deleted file mode 100644 index f9c24d46228c..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/mocks/exception_list_item_schema.mock.ts +++ /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", 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 { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; - -export const getExceptionListItemSchemaMock = ( - overrides?: Partial<ExceptionListItemSchema> -): ExceptionListItemSchema => ({ - _version: undefined, - comments: [], - created_at: '2020-04-20T15:25:31.830Z', - created_by: 'some user', - description: 'some description', - entries: [ - { - entries: [ - { field: 'nested.field', operator: 'included', type: 'match', value: 'some value' }, - ], - field: 'some.parentField', - type: 'nested', - }, - { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, - ], - expire_time: undefined, - id: '1', - item_id: 'endpoint_list_item', - list_id: 'endpoint_list_id', - meta: {}, - name: 'some name', - namespace_type: 'single', - os_types: [], - tags: ['user added string for a tag', 'malware'], - tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', - type: 'simple', - updated_at: '2020-04-20T15:25:31.830Z', - updated_by: 'some user', - ...(overrides || {}), -}); diff --git a/packages/kbn-securitysolution-exception-list-components/src/mocks/header.mock.ts b/packages/kbn-securitysolution-exception-list-components/src/mocks/header.mock.ts deleted file mode 100644 index 06aa00b5ff72..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/mocks/header.mock.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". - */ - -export const handleEdit = jest.fn(); -export const handleDelete = jest.fn(); -export const actions = [ - { - key: 'edit', - icon: 'pencil', - label: 'Edit detection exception', - onClick: handleEdit, - }, - { - key: 'delete', - icon: 'trash', - label: 'Delete detection exception', - onClick: handleDelete, - }, -]; -export const actionsWithDisabledDelete = [ - { - key: 'edit', - icon: 'pencil', - label: 'Edit detection exception', - onClick: handleEdit, - }, - { - key: 'delete', - icon: 'trash', - disabled: true, - label: 'Delete detection exception', - onClick: handleDelete, - }, -]; diff --git a/packages/kbn-securitysolution-exception-list-components/src/mocks/rule_references.mock.ts b/packages/kbn-securitysolution-exception-list-components/src/mocks/rule_references.mock.ts deleted file mode 100644 index 3592d5f03cc4..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/mocks/rule_references.mock.ts +++ /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 { Rule, RuleReference } from '../types'; - -export const rules: Rule[] = [ - { - exceptions_list: [ - { - id: '123', - list_id: 'i_exist', - namespace_type: 'single', - type: 'detection', - }, - { - id: '456', - list_id: 'i_exist_2', - namespace_type: 'single', - type: 'detection', - }, - ], - id: '1a2b3c', - name: 'Simple Rule Query', - rule_id: 'rule-2', - }, -]; - -export const ruleReference: RuleReference = { - name: 'endpoint list', - id: 'endpoint_list', - referenced_rules: rules, - listId: 'endpoint_list_id', -}; - -export const ruleReferences = { - endpoint_list_id: ruleReference, -}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/mocks/security_link_component.mock.tsx b/packages/kbn-securitysolution-exception-list-components/src/mocks/security_link_component.mock.tsx deleted file mode 100644 index 1ad7d2921c5a..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/mocks/security_link_component.mock.tsx +++ /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", 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, { ReactElement } from 'react'; -import { generateLinkedRulesMenuItems } from '../generate_linked_rules_menu_item'; -import { rules } from './rule_references.mock'; -export const securityLinkAnchorComponentMock = ({ - referenceName, - referenceId, -}: { - referenceName: string; - referenceId: string; -}) => ( - <div data-test-subj="securityLinkAnchorComponent"> - <a href={referenceId}>{referenceName}</a> - </div> -); - -export const getSecurityLinkAction = (dataTestSubj: string) => - generateLinkedRulesMenuItems({ - dataTestSubj, - linkedRules: [ - ...rules, - { - exceptions_list: [], - id: '2a2b3c', - name: 'Simple Rule Query 2', - rule_id: 'rule-2', - }, - ], - securityLinkAnchorComponent: securityLinkAnchorComponentMock, - }) as ReactElement[]; diff --git a/packages/kbn-securitysolution-exception-list-components/src/mocks/value_list_modal.mock.tsx b/packages/kbn-securitysolution-exception-list-components/src/mocks/value_list_modal.mock.tsx deleted file mode 100644 index 930439b577ff..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/mocks/value_list_modal.mock.tsx +++ /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", 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'; - -export const mockShowValueListModal = jest.fn(); -export const MockedShowValueListModal = (props: { children: React.ReactNode }) => { - mockShowValueListModal(props); - return <>{props.children}</>; -}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx b/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx deleted file mode 100644 index 38be3b8474ad..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx +++ /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 React from 'react'; -import type { FC } from 'react'; -import { EuiTablePagination } from '@elastic/eui'; - -import type { PaginationProps } from '../types'; -import { usePagination } from './use_pagination'; - -const PaginationComponent: FC<PaginationProps> = ({ - dataTestSubj, - ariaLabel, - pagination, - onPaginationChange, -}) => { - const { - pageIndex, - pageCount, - pageSize, - pageSizeOptions, - - handleItemsPerPageChange, - handlePageIndexChange, - } = usePagination({ pagination, onPaginationChange }); - - return ( - <EuiTablePagination - data-test-subj={dataTestSubj} - aria-label={ariaLabel} - pageCount={pageCount} - activePage={pageIndex} - itemsPerPage={pageSize} - onChangePage={handlePageIndexChange} - onChangeItemsPerPage={handleItemsPerPageChange} - itemsPerPageOptions={pageSizeOptions} - /> - ); -}; - -PaginationComponent.displayName = 'PaginationComponent'; - -export const Pagination = React.memo(PaginationComponent); - -Pagination.displayName = 'Pagination'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/partial_code_signature_callout/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/partial_code_signature_callout/index.tsx deleted file mode 100644 index 72836fc78fb5..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/partial_code_signature_callout/index.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 { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; - -import { EuiCallOut } from '@elastic/eui'; - -export const PartialCodeSignatureCallout = () => { - return ( - <EuiCallOut - title={i18n.translate('exceptionList-components.partialCodeSignatureCallout.title', { - defaultMessage: 'Please review your entries', - })} - iconType="warning" - color="warning" - size="s" - data-test-subj="partialCodeSignatureCallout" - > - <FormattedMessage - id="exceptionList-components.partialCodeSignatureCallout.body" - defaultMessage='Please review field values, as your filter criteria may be incomplete. We recommend both the signer name and trust status be included (using the "AND" operator) to avoid potential security gaps.' - tagName="p" - /> - </EuiCallOut> - ); -}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/search_bar/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/search_bar/index.tsx deleted file mode 100644 index 0670a2553a69..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/search_bar/index.tsx +++ /dev/null @@ -1,124 +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, { useCallback } from 'react'; -import type { FC } from 'react'; - -import type { EuiSearchBarProps, IconType, SearchFilterConfig } from '@elastic/eui'; -import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiSearchBar } from '@elastic/eui'; -import type { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import type { GetExceptionItemProps } from '../types'; - -const ITEMS_SCHEMA = { - strict: true, - fields: { - created_by: { - type: 'string', - }, - description: { - type: 'string', - }, - id: { - type: 'string', - }, - item_id: { - type: 'string', - }, - list_id: { - type: 'string', - }, - name: { - type: 'string', - }, - os_types: { - type: 'string', - }, - tags: { - type: 'string', - }, - }, -}; -interface SearchBarProps { - addExceptionButtonText?: string; - placeholdertext?: string; - canAddException?: boolean; // TODO what is the default value - - // TODO: REFACTOR: not to send the listType and handle it in the Parent - // Exception list type used to determine what type of item is - // being created when "onAddExceptionClick" is invoked - listType: ExceptionListTypeEnum; - isSearching?: boolean; - dataTestSubj?: string; - filters?: SearchFilterConfig[]; // TODO about filters - isButtonFilled?: boolean; - buttonIconType?: IconType; - onSearch: (arg: GetExceptionItemProps) => void; - onAddExceptionClick: (type: ExceptionListTypeEnum) => void; -} -const SearchBarComponent: FC<SearchBarProps> = ({ - addExceptionButtonText, - placeholdertext, - canAddException, - listType, - isSearching, - dataTestSubj, - filters = [], - isButtonFilled = true, - buttonIconType, - onSearch, - onAddExceptionClick, -}) => { - const handleOnSearch = useCallback<NonNullable<EuiSearchBarProps['onChange']>>( - ({ queryText }): void => { - onSearch({ search: queryText }); - }, - [onSearch] - ); - - const handleAddException = useCallback(() => { - // TODO: ASK YARA why we need to send the listType - onAddExceptionClick(listType); - }, [onAddExceptionClick, listType]); - - return ( - <EuiFlexGroup alignItems="center"> - <EuiFlexItem grow={true}> - <EuiSearchBar - box={{ - placeholder: placeholdertext, - incremental: false, - schema: ITEMS_SCHEMA, - 'data-test-subj': `${dataTestSubj || ''}searchBar`, - }} - filters={filters} - onChange={handleOnSearch} - /> - </EuiFlexItem> - {!canAddException && ( - <EuiFlexItem grow={false}> - <EuiButton - data-test-subj={`${dataTestSubj || ''}Button`} - onClick={handleAddException} - isDisabled={isSearching} - fill={isButtonFilled} - iconType={buttonIconType} - > - {addExceptionButtonText} - </EuiButton> - </EuiFlexItem> - )} - </EuiFlexGroup> - ); -}; - -SearchBarComponent.displayName = 'SearchBarComponent'; - -export const SearchBar = React.memo(SearchBarComponent); - -SearchBar.displayName = 'SearchBar'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/index.tsx deleted file mode 100644 index aa5625cfe671..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/index.tsx +++ /dev/null @@ -1,53 +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 } from 'react'; -import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { Interpolation, Theme } from '@emotion/react'; -import { textWithEditContainerCss, editIconCss } from './text_with_edit.styles'; -interface TextWithEditProps { - isReadonly: boolean; - dataTestSubj?: string; - text: string; - textCss?: Interpolation<Theme>; - onEdit?: () => void; -} - -const TextWithEditComponent: FC<TextWithEditProps> = ({ - isReadonly, - dataTestSubj, - text, - onEdit, - textCss, -}) => { - return ( - <EuiFlexGroup css={textWithEditContainerCss}> - <EuiFlexItem grow={10}> - <span css={textCss} data-test-subj={`${dataTestSubj || ''}Text`}> - {text} - </span> - </EuiFlexItem> - <EuiFlexItem grow={false} css={editIconCss}> - {isReadonly ? null : ( - <EuiButtonIcon - data-test-subj={`${dataTestSubj || ''}EditIcon`} - aria-label="Edit Text List Header" - iconType="pencil" - onClick={() => (typeof onEdit === 'function' ? onEdit() : null)} - /> - )} - </EuiFlexItem> - </EuiFlexGroup> - ); -}; -TextWithEditComponent.displayName = 'TextWithEditComponent'; - -export const TextWithEdit = React.memo(TextWithEditComponent); - -TextWithEdit.displayName = 'TextWithEdit'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.styles.ts b/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.styles.ts deleted file mode 100644 index e293ea073041..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.styles.ts +++ /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", 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 { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; - -export const textWithEditContainerCss = css` - display: flex; - width: fit-content; - align-items: baseline; - padding-bottom: ${euiThemeVars.euiSizeS}; - h1 { - margin-bottom: 0; - } -`; -export const editIconCss = css` - button { - margin-left: -${euiThemeVars.euiSizeM}; - } -`; diff --git a/packages/kbn-securitysolution-exception-list-components/src/translations.ts b/packages/kbn-securitysolution-exception-list-components/src/translations.ts deleted file mode 100644 index dff688d156da..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/translations.ts +++ /dev/null @@ -1,156 +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 { i18n } from '@kbn/i18n'; - -export const EMPTY_VIEWER_STATE_EMPTY_TITLE = i18n.translate( - 'exceptionList-components.empty.viewer.state.empty.title', - { - defaultMessage: 'Add exceptions to this list', - } -); - -export const EMPTY_VIEWER_STATE_EMPTY_BODY = i18n.translate( - 'exceptionList-components.empty.viewer.state.empty.body', - { - defaultMessage: 'There is no exception in your list. Create your first exception.', - } -); -export const EMPTY_VIEWER_STATE_EMPTY_SEARCH_TITLE = i18n.translate( - 'exceptionList-components.empty.viewer.state.empty_search.search.title', - { - defaultMessage: 'No results match your search criteria', - } -); - -export const EMPTY_VIEWER_STATE_EMPTY_SEARCH_BODY = i18n.translate( - 'exceptionList-components.empty.viewer.state.empty_search.body', - { - defaultMessage: 'Try modifying your search', - } -); - -export const EMPTY_VIEWER_STATE_EMPTY_VIEWER_BUTTON = (exceptionType: string) => - i18n.translate('exceptionList-components.empty.viewer.state.empty.viewer_button', { - values: { exceptionType }, - defaultMessage: 'Create {exceptionType} exception', - }); - -export const EMPTY_VIEWER_STATE_ERROR_TITLE = i18n.translate( - 'exceptionList-components.empty.viewer.state.error_title', - { - defaultMessage: 'Unable to load exception items', - } -); - -export const EMPTY_VIEWER_STATE_ERROR_BODY = i18n.translate( - 'exceptionList-components.empty.viewer.state.error_body', - { - defaultMessage: - 'There was an error loading the exception items. Contact your administrator for help.', - } -); -export const EXCEPTION_LIST_HEADER_EXPORT_ACTION = i18n.translate( - 'exceptionList-components.exception_list_header_export_action', - { - defaultMessage: 'Export exception list', - } -); -export const EXCEPTION_LIST_HEADER_DELETE_ACTION = i18n.translate( - 'exceptionList-components.exception_list_header_delete_action', - { - defaultMessage: 'Delete exception list', - } -); -export const EXCEPTION_LIST_HEADER_DUPLICATE_ACTION = i18n.translate( - 'exceptionList-components.exception_list_header_duplicate_action', - { - defaultMessage: 'Duplicate exception list', - } -); -export const EXCEPTION_LIST_HEADER_LINK_RULES_BUTTON = i18n.translate( - 'exceptionList-components.exception_list_header_link_rules_button', - { - defaultMessage: 'Link rules', - } -); - -export const EXCEPTION_LIST_HEADER_LINKED_RULES = (noOfRules: number) => - i18n.translate('exceptionList-components.exception_list_header_linked_rules', { - values: { noOfRules }, - defaultMessage: 'Linked to {noOfRules} rules', - }); - -export const EXCEPTION_LIST_HEADER_BREADCRUMB = i18n.translate( - 'exceptionList-components.exception_list_header_breadcrumb', - { - defaultMessage: 'Shared Exception Lists', - } -); - -export const EXCEPTION_LIST_HEADER_LIST_ID = i18n.translate( - 'exceptionList-components.exception_list_header_list_id', - { - defaultMessage: 'List ID', - } -); - -export const EXCEPTION_LIST_HEADER_NAME = i18n.translate( - 'exceptionList-components.exception_list_header_name', - { - defaultMessage: 'Add a name', - } -); - -export const EXCEPTION_LIST_HEADER_DESCRIPTION = i18n.translate( - 'exceptionList-components.exception_list_header_description', - { - defaultMessage: 'Add a description', - } -); - -export const EXCEPTION_LIST_HEADER_EDIT_MODAL_TITLE = (listName: string) => - i18n.translate('exceptionList-components.exception_list_header_edit_modal_name', { - defaultMessage: 'Edit {listName}', - values: { listName }, - }); - -export const EXCEPTION_LIST_HEADER_EDIT_MODAL_SAVE_BUTTON = i18n.translate( - 'exceptionList-components.exception_list_header_edit_modal_save_button', - { - defaultMessage: 'Save', - } -); - -export const EXCEPTION_LIST_HEADER_EDIT_MODAL_CANCEL_BUTTON = i18n.translate( - 'exceptionList-components.exception_list_header_edit_modal_cancel_button', - { - defaultMessage: 'Cancel', - } -); -export const EXCEPTION_LIST_HEADER_NAME_TEXTBOX = i18n.translate( - 'exceptionList-components.exception_list_header_Name_textbox', - { - defaultMessage: 'Name', - } -); - -export const EXCEPTION_LIST_HEADER_DESCRIPTION_TEXTBOX = i18n.translate( - 'exceptionList-components.exception_list_header_description_textbox', - { - defaultMessage: 'Description (optional)', - } -); - -export const LIST_NAME_REQUIRED_ERROR = i18n.translate( - 'exceptionList-components.exception_list_header_description_textboxexceptionList-components.exception_list_header_name_required_eror', - { - defaultMessage: 'List name cannot be empty', - } -); diff --git a/packages/kbn-securitysolution-exception-list-components/src/types/index.ts b/packages/kbn-securitysolution-exception-list-components/src/types/index.ts deleted file mode 100644 index 57a0ee3901ba..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/types/index.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", 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 { ListArray } from '@kbn/securitysolution-io-ts-list-types'; - -import type { Pagination } from '@elastic/eui'; -import type { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; - -export interface GetExceptionItemProps { - pagination?: PaginationProps['pagination']; - search?: string; - filters?: string; -} - -export interface PaginationProps { - dataTestSubj?: string; - ariaLabel?: string; - pagination: Pagination & { pageSize: number }; - onPaginationChange: (arg: GetExceptionItemProps) => void; -} - -export enum ViewerStatus { - ERROR = 'error', - EMPTY = 'empty', - EMPTY_SEARCH = 'empty_search', - LOADING = 'loading', - SEARCHING = 'searching', - DELETING = 'deleting', -} - -export interface ExceptionListSummaryProps { - pagination: Pagination; - // Corresponds to last time exception items were fetched - lastUpdated: string | number | null; -} - -export type ViewerFlyoutName = 'addException' | 'editException' | null; - -export interface RuleReferences { - [key: string]: RuleReference; -} - -export interface ExceptionListItemIdentifiers { - id: string; - name: string; - namespaceType: NamespaceType; -} - -export enum ListTypeText { - ENDPOINT = 'endpoint', - DETECTION = 'empty', - RULE_DEFAULT = 'empty_search', -} -export interface Rule { - name: string; - id: string; - rule_id: string; - exceptions_list?: ListArray; -} - -export interface RuleReference { - name: string; - id: string; - referenced_rules: Rule[]; - listId?: string; -} - -export interface ListDetails { - name: string; - description?: string; -} diff --git a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/index.ts b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/index.ts deleted file mode 100644 index 5a0843be833f..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/index.ts +++ /dev/null @@ -1,10 +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". - */ - -export { ValueWithSpaceWarning } from './value_with_space_warning'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.ts b/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.ts deleted file mode 100644 index d5a6392815a7..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.ts +++ /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 { paramContainsSpace, autoCompletei18n } from '@kbn/securitysolution-autocomplete'; - -interface UseValueWithSpaceWarningResult { - showSpaceWarningIcon: boolean; - warningText: string; -} -interface UseValueWithSpaceWarningProps { - value: string | string[]; - tooltipIconText?: string; -} - -export const useValueWithSpaceWarning = ({ - value, - tooltipIconText, -}: UseValueWithSpaceWarningProps): UseValueWithSpaceWarningResult => { - const showSpaceWarningIcon = Array.isArray(value) - ? value.find(paramContainsSpace) - : paramContainsSpace(value); - - return { - showSpaceWarningIcon: !!showSpaceWarningIcon, - warningText: tooltipIconText || autoCompletei18n.FIELD_SPACE_WARNING, - }; -}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/wildcard_with_wrong_operator_callout/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/wildcard_with_wrong_operator_callout/index.tsx deleted file mode 100644 index d68782b4c17f..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/src/wildcard_with_wrong_operator_callout/index.tsx +++ /dev/null @@ -1,53 +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 { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; - -import { EuiCallOut } from '@elastic/eui'; - -export const WildCardWithWrongOperatorCallout = () => { - return ( - <EuiCallOut - title={i18n.translate('exceptionList-components.wildcardWithWrongOperatorCallout.title', { - defaultMessage: 'Please review your entries', - })} - iconType="warning" - color="warning" - size="s" - data-test-subj="wildcardWithWrongOperatorCallout" - > - <p> - <FormattedMessage - id="exceptionList-components.wildcardWithWrongOperatorCallout.body" - defaultMessage='Using a "*" or a "?" in the value with the "is" operator can make the entry ineffective. {operator} to "{matches}" to ensure wildcards run properly.' - values={{ - operator: ( - <strong> - {i18n.translate( - 'exceptionList-components.wildcardWithWrongOperatorCallout.changeTheOperator', - { defaultMessage: 'Change the operator' } - )} - </strong> - ), - matches: ( - <strong> - {i18n.translate( - 'exceptionList-components.wildcardWithWrongOperatorCallout.matches', - { defaultMessage: 'matches' } - )} - </strong> - ), - }} - /> - </p> - </EuiCallOut> - ); -}; diff --git a/packages/kbn-securitysolution-exception-list-components/tsconfig.json b/packages/kbn-securitysolution-exception-list-components/tsconfig.json deleted file mode 100644 index b3df3a2aa208..000000000000 --- a/packages/kbn-securitysolution-exception-list-components/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node", - "react", - "@emotion/react/types/css-prop" - ] - }, - "include": [ - "**/*.ts", - "**/*.tsx", - "**/*.d.ts" - ], - "kbn_references": [ - "@kbn/securitysolution-io-ts-list-types", - "@kbn/securitysolution-autocomplete", - "@kbn/ui-theme", - "@kbn/i18n", - "@kbn/i18n-react", - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.gen.ts b/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.gen.ts deleted file mode 100644 index aedd1f5fc5cc..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.gen.ts +++ /dev/null @@ -1,39 +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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Create shared exception list API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { - ExceptionListName, - ExceptionListDescription, - ExceptionList, -} from '../model/exception_list_common.gen'; - -export type CreateSharedExceptionListRequestBody = z.infer< - typeof CreateSharedExceptionListRequestBody ->; -export const CreateSharedExceptionListRequestBody = z.object({ - name: ExceptionListName, - description: ExceptionListDescription, -}); -export type CreateSharedExceptionListRequestBodyInput = z.input< - typeof CreateSharedExceptionListRequestBody ->; - -export type CreateSharedExceptionListResponse = z.infer<typeof CreateSharedExceptionListResponse>; -export const CreateSharedExceptionListResponse = ExceptionList; diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.schema.yaml deleted file mode 100644 index c4cee089e583..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.schema.yaml +++ /dev/null @@ -1,68 +0,0 @@ -openapi: 3.0.0 -info: - title: Create shared exception list API endpoint - version: '2023-10-31' -paths: - /api/exceptions/shared: - post: - x-labels: [serverless, ess] - operationId: CreateSharedExceptionList - x-codegen-enabled: true - summary: Create a shared exception list - description: | - An exception list groups exception items and can be associated with detection rules. A shared exception list can apply to multiple detection rules. - > info - > All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - name: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListName' - description: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListDescription' - required: - - name - - description - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionList' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 409: - description: Exception list already exists response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.schema.yaml deleted file mode 100644 index 92afc3232efe..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.schema.yaml +++ /dev/null @@ -1,70 +0,0 @@ -openapi: 3.0.0 -info: - title: Delete exception list API endpoint - version: '2023-10-31' -paths: - /api/exception_lists: - delete: - x-labels: [serverless, ess] - operationId: DeleteExceptionList - x-codegen-enabled: true - summary: Delete an exception list - description: Delete an exception list using the `id` or `list_id` field. - parameters: - - name: id - in: query - required: false - description: Either `id` or `list_id` must be specified - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListId' - - name: list_id - in: query - required: false - description: Either `id` or `list_id` must be specified - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListHumanId' - - name: namespace_type - in: query - required: false - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' - default: single - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionList' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: Exception list not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.schema.yaml deleted file mode 100644 index 9f57afcd5ab1..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.schema.yaml +++ /dev/null @@ -1,70 +0,0 @@ -openapi: 3.0.0 -info: - title: Delete exception list item API endpoint - version: '2023-10-31' -paths: - /api/exception_lists/items: - delete: - x-labels: [serverless, ess] - operationId: DeleteExceptionListItem - x-codegen-enabled: true - summary: Delete an exception list item - description: Delete an exception list item using the `id` or `item_id` field. - parameters: - - name: id - in: query - required: false - description: Either `id` or `item_id` must be specified - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemId' - - name: item_id - in: query - required: false - description: Either `id` or `item_id` must be specified - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemHumanId' - - name: namespace_type - in: query - required: false - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' - default: single - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItem' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: Exception list item not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.schema.yaml deleted file mode 100644 index 758171327ee4..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.schema.yaml +++ /dev/null @@ -1,71 +0,0 @@ -openapi: 3.0.0 -info: - title: Duplicate exception list API endpoint - version: '2023-10-31' -paths: - /api/exception_lists/_duplicate: - post: - x-labels: [serverless, ess] - operationId: DuplicateExceptionList - x-codegen-enabled: true - summary: Duplicate an exception list - description: Duplicate an existing exception list. - parameters: - - name: list_id - in: query - required: true - description: Exception list's human identifier - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListHumanId' - - name: namespace_type - in: query - required: true - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' - - name: include_expired_exceptions - in: query - required: true - description: Determines whether to include expired exceptions in the exported list - schema: - type: string - enum: ['true', 'false'] - default: 'true' - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionList' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 405: - description: Exception list to duplicate not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.gen.ts b/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.gen.ts deleted file mode 100644 index 9645b8ac793c..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.gen.ts +++ /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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Export exception list API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { - ExceptionListId, - ExceptionListHumanId, - ExceptionNamespaceType, -} from '../model/exception_list_common.gen'; - -export type ExportExceptionListRequestQuery = z.infer<typeof ExportExceptionListRequestQuery>; -export const ExportExceptionListRequestQuery = z.object({ - /** - * Exception list's identifier - */ - id: ExceptionListId, - /** - * Exception list's human identifier - */ - list_id: ExceptionListHumanId, - namespace_type: ExceptionNamespaceType, - /** - * Determines whether to include expired exceptions in the exported list - */ - include_expired_exceptions: z.enum(['true', 'false']).default('true'), -}); -export type ExportExceptionListRequestQueryInput = z.input<typeof ExportExceptionListRequestQuery>; diff --git a/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.schema.yaml deleted file mode 100644 index 3232f46c238c..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.schema.yaml +++ /dev/null @@ -1,79 +0,0 @@ -openapi: 3.0.0 -info: - title: Export exception list API endpoint - version: '2023-10-31' -paths: - /api/exception_lists/_export: - post: - x-labels: [serverless, ess] - operationId: ExportExceptionList - x-codegen-enabled: true - summary: Export an exception list - description: Export an exception list and its associated items to an NDJSON file. - parameters: - - name: id - in: query - required: true - description: Exception list's identifier - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListId' - - name: list_id - in: query - required: true - description: Exception list's human identifier - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListHumanId' - - name: namespace_type - in: query - required: true - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' - - name: include_expired_exceptions - in: query - required: true - description: Determines whether to include expired exceptions in the exported list - schema: - type: string - enum: ['true', 'false'] - default: 'true' - responses: - 200: - description: Successful response - content: - application/ndjson: - schema: - type: string - format: binary - description: A `.ndjson` file containing specified exception list and its items - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: Exception list not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/index.ts b/packages/kbn-securitysolution-exceptions-common/api/index.ts deleted file mode 100644 index fd6722b67ace..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './model/exception_list_common.gen'; -export * from './model/exception_list_item_entry.gen'; -export * from './create_exception_list_item/create_exception_list_item.gen'; -export * from './create_rule_exceptions/create_rule_exceptions.gen'; -export * from './create_shared_exceptions_list/create_shared_exceptions_list.gen'; -export * from './create_exception_list/create_exception_list.gen'; -export * from './delete_exception_list_item/delete_exception_list_item.gen'; -export * from './delete_exception_list/delete_exception_list.gen'; -export * from './duplicate_exception_list/duplicate_exception_list.gen'; -export * from './export_exception_list/export_exception_list.gen'; -export * from './find_exception_list_items/find_exception_list_items.gen'; -export * from './find_exception_lists/find_exception_lists.gen'; -export * from './import_exceptions/import_exceptions.gen'; -export * from './read_exception_list_item/read_exception_list_item.gen'; -export * from './read_exception_list/read_exception_list.gen'; -export * from './read_exception_list_summary/read_exception_list_summary.gen'; -export * from './update_exception_list_item/update_exception_list_item.gen'; -export * from './update_exception_list/update_exception_list.gen'; diff --git a/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.schema.yaml deleted file mode 100644 index 883a62e5bbec..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.schema.yaml +++ /dev/null @@ -1,147 +0,0 @@ -openapi: 3.0.0 -info: - title: Common Exception List Item Entry Attributes - version: 'not applicable' -paths: {} -components: - x-codegen-enabled: true - schemas: - ExceptionListItemEntryOperator: - type: string - enum: [excluded, included] - - ExceptionListItemEntryMatch: - type: object - properties: - type: - type: string - enum: [match] - field: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - value: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - operator: - $ref: '#/components/schemas/ExceptionListItemEntryOperator' - required: - - type - - field - - value - - operator - - ExceptionListItemEntryMatchAny: - type: object - properties: - type: - type: string - enum: [match_any] - field: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - value: - type: array - items: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - minItems: 1 - operator: - $ref: '#/components/schemas/ExceptionListItemEntryOperator' - required: - - type - - field - - value - - operator - - ExceptionListItemEntryList: - type: object - properties: - type: - type: string - enum: [list] - field: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - list: - type: object - properties: - id: - $ref: '../../../kbn-securitysolution-lists-common/api/model/list_common.schema.yaml#/components/schemas/ListId' - type: - $ref: '../../../kbn-securitysolution-lists-common/api/model/list_common.schema.yaml#/components/schemas/ListType' - required: [id, type] - operator: - $ref: '#/components/schemas/ExceptionListItemEntryOperator' - required: - - type - - field - - list - - operator - - ExceptionListItemEntryExists: - type: object - properties: - type: - type: string - enum: [exists] - field: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - operator: - $ref: '#/components/schemas/ExceptionListItemEntryOperator' - required: - - type - - field - - operator - - ExceptionListItemEntryNestedEntryItem: - oneOf: - - $ref: '#/components/schemas/ExceptionListItemEntryMatch' - - $ref: '#/components/schemas/ExceptionListItemEntryMatchAny' - - $ref: '#/components/schemas/ExceptionListItemEntryExists' - - ExceptionListItemEntryNested: - type: object - properties: - type: - type: string - enum: [nested] - field: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - entries: - type: array - items: - $ref: '#/components/schemas/ExceptionListItemEntryNestedEntryItem' - minItems: 1 - required: - - type - - field - - entries - - ExceptionListItemEntryMatchWildcard: - type: object - properties: - type: - type: string - enum: [wildcard] - field: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - value: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - operator: - $ref: '#/components/schemas/ExceptionListItemEntryOperator' - required: - - type - - field - - value - - operator - - ExceptionListItemEntry: - discriminator: - propertyName: type - anyOf: - - $ref: '#/components/schemas/ExceptionListItemEntryMatch' - - $ref: '#/components/schemas/ExceptionListItemEntryMatchAny' - - $ref: '#/components/schemas/ExceptionListItemEntryList' - - $ref: '#/components/schemas/ExceptionListItemEntryExists' - - $ref: '#/components/schemas/ExceptionListItemEntryNested' - - $ref: '#/components/schemas/ExceptionListItemEntryMatchWildcard' - - ExceptionListItemEntryArray: - type: array - items: - $ref: '#/components/schemas/ExceptionListItemEntry' diff --git a/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts b/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts deleted file mode 100644 index 4827baab85e9..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts +++ /dev/null @@ -1,427 +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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Exceptions API client for quickstart - * version: Bundle (no version) - */ - -import type { KbnClient } from '@kbn/test'; -import { ToolingLog } from '@kbn/tooling-log'; -import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; -import { replaceParams } from '@kbn/openapi-common/shared'; -import { catchAxiosErrorFormatAndThrow } from '@kbn/securitysolution-utils'; - -import type { - CreateExceptionListItemRequestBodyInput, - CreateExceptionListItemResponse, -} from './create_exception_list_item/create_exception_list_item.gen'; -import type { - CreateExceptionListRequestBodyInput, - CreateExceptionListResponse, -} from './create_exception_list/create_exception_list.gen'; -import type { - CreateRuleExceptionListItemsRequestParamsInput, - CreateRuleExceptionListItemsRequestBodyInput, - CreateRuleExceptionListItemsResponse, -} from './create_rule_exceptions/create_rule_exceptions.gen'; -import type { - CreateSharedExceptionListRequestBodyInput, - CreateSharedExceptionListResponse, -} from './create_shared_exceptions_list/create_shared_exceptions_list.gen'; -import type { - DeleteExceptionListItemRequestQueryInput, - DeleteExceptionListItemResponse, -} from './delete_exception_list_item/delete_exception_list_item.gen'; -import type { - DeleteExceptionListRequestQueryInput, - DeleteExceptionListResponse, -} from './delete_exception_list/delete_exception_list.gen'; -import type { - DuplicateExceptionListRequestQueryInput, - DuplicateExceptionListResponse, -} from './duplicate_exception_list/duplicate_exception_list.gen'; -import type { ExportExceptionListRequestQueryInput } from './export_exception_list/export_exception_list.gen'; -import type { - FindExceptionListItemsRequestQueryInput, - FindExceptionListItemsResponse, -} from './find_exception_list_items/find_exception_list_items.gen'; -import type { - FindExceptionListsRequestQueryInput, - FindExceptionListsResponse, -} from './find_exception_lists/find_exception_lists.gen'; -import type { - ImportExceptionListRequestQueryInput, - ImportExceptionListResponse, -} from './import_exceptions/import_exceptions.gen'; -import type { - ReadExceptionListItemRequestQueryInput, - ReadExceptionListItemResponse, -} from './read_exception_list_item/read_exception_list_item.gen'; -import type { - ReadExceptionListSummaryRequestQueryInput, - ReadExceptionListSummaryResponse, -} from './read_exception_list_summary/read_exception_list_summary.gen'; -import type { - ReadExceptionListRequestQueryInput, - ReadExceptionListResponse, -} from './read_exception_list/read_exception_list.gen'; -import type { - UpdateExceptionListItemRequestBodyInput, - UpdateExceptionListItemResponse, -} from './update_exception_list_item/update_exception_list_item.gen'; -import type { - UpdateExceptionListRequestBodyInput, - UpdateExceptionListResponse, -} from './update_exception_list/update_exception_list.gen'; - -export interface ClientOptions { - kbnClient: KbnClient; - log: ToolingLog; -} - -export class Client { - readonly kbnClient: KbnClient; - readonly log: ToolingLog; - - constructor(options: ClientOptions) { - this.kbnClient = options.kbnClient; - this.log = options.log; - } - /** - * An exception list groups exception items and can be associated with detection rules. You can assign detection rules with multiple exception lists. -> info -> All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. - - */ - async createExceptionList(props: CreateExceptionListProps) { - this.log.info(`${new Date().toISOString()} Calling API CreateExceptionList`); - return this.kbnClient - .request<CreateExceptionListResponse>({ - path: '/api/exception_lists', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Create an exception item and associate it with the specified exception list. -> info -> Before creating exception items, you must create an exception list. - - */ - async createExceptionListItem(props: CreateExceptionListItemProps) { - this.log.info(`${new Date().toISOString()} Calling API CreateExceptionListItem`); - return this.kbnClient - .request<CreateExceptionListItemResponse>({ - path: '/api/exception_lists/items', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Create exception items that apply to a single detection rule. - */ - async createRuleExceptionListItems(props: CreateRuleExceptionListItemsProps) { - this.log.info(`${new Date().toISOString()} Calling API CreateRuleExceptionListItems`); - return this.kbnClient - .request<CreateRuleExceptionListItemsResponse>({ - path: replaceParams('/api/detection_engine/rules/{id}/exceptions', props.params), - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * An exception list groups exception items and can be associated with detection rules. A shared exception list can apply to multiple detection rules. -> info -> All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. - - */ - async createSharedExceptionList(props: CreateSharedExceptionListProps) { - this.log.info(`${new Date().toISOString()} Calling API CreateSharedExceptionList`); - return this.kbnClient - .request<CreateSharedExceptionListResponse>({ - path: '/api/exceptions/shared', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Delete an exception list using the `id` or `list_id` field. - */ - async deleteExceptionList(props: DeleteExceptionListProps) { - this.log.info(`${new Date().toISOString()} Calling API DeleteExceptionList`); - return this.kbnClient - .request<DeleteExceptionListResponse>({ - path: '/api/exception_lists', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'DELETE', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Delete an exception list item using the `id` or `item_id` field. - */ - async deleteExceptionListItem(props: DeleteExceptionListItemProps) { - this.log.info(`${new Date().toISOString()} Calling API DeleteExceptionListItem`); - return this.kbnClient - .request<DeleteExceptionListItemResponse>({ - path: '/api/exception_lists/items', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'DELETE', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Duplicate an existing exception list. - */ - async duplicateExceptionList(props: DuplicateExceptionListProps) { - this.log.info(`${new Date().toISOString()} Calling API DuplicateExceptionList`); - return this.kbnClient - .request<DuplicateExceptionListResponse>({ - path: '/api/exception_lists/_duplicate', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Export an exception list and its associated items to an NDJSON file. - */ - async exportExceptionList(props: ExportExceptionListProps) { - this.log.info(`${new Date().toISOString()} Calling API ExportExceptionList`); - return this.kbnClient - .request({ - path: '/api/exception_lists/_export', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Get a list of all exception list items in the specified list. - */ - async findExceptionListItems(props: FindExceptionListItemsProps) { - this.log.info(`${new Date().toISOString()} Calling API FindExceptionListItems`); - return this.kbnClient - .request<FindExceptionListItemsResponse>({ - path: '/api/exception_lists/items/_find', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Get a list of all exception lists. - */ - async findExceptionLists(props: FindExceptionListsProps) { - this.log.info(`${new Date().toISOString()} Calling API FindExceptionLists`); - return this.kbnClient - .request<FindExceptionListsResponse>({ - path: '/api/exception_lists/_find', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Import an exception list and its associated items from an NDJSON file. - */ - async importExceptionList(props: ImportExceptionListProps) { - this.log.info(`${new Date().toISOString()} Calling API ImportExceptionList`); - return this.kbnClient - .request<ImportExceptionListResponse>({ - path: '/api/exception_lists/_import', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - body: props.attachment, - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Get the details of an exception list using the `id` or `list_id` field. - */ - async readExceptionList(props: ReadExceptionListProps) { - this.log.info(`${new Date().toISOString()} Calling API ReadExceptionList`); - return this.kbnClient - .request<ReadExceptionListResponse>({ - path: '/api/exception_lists', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Get the details of an exception list item using the `id` or `item_id` field. - */ - async readExceptionListItem(props: ReadExceptionListItemProps) { - this.log.info(`${new Date().toISOString()} Calling API ReadExceptionListItem`); - return this.kbnClient - .request<ReadExceptionListItemResponse>({ - path: '/api/exception_lists/items', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Get a summary of the specified exception list. - */ - async readExceptionListSummary(props: ReadExceptionListSummaryProps) { - this.log.info(`${new Date().toISOString()} Calling API ReadExceptionListSummary`); - return this.kbnClient - .request<ReadExceptionListSummaryResponse>({ - path: '/api/exception_lists/summary', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Update an exception list using the `id` or `list_id` field. - */ - async updateExceptionList(props: UpdateExceptionListProps) { - this.log.info(`${new Date().toISOString()} Calling API UpdateExceptionList`); - return this.kbnClient - .request<UpdateExceptionListResponse>({ - path: '/api/exception_lists', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'PUT', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Update an exception list item using the `id` or `item_id` field. - */ - async updateExceptionListItem(props: UpdateExceptionListItemProps) { - this.log.info(`${new Date().toISOString()} Calling API UpdateExceptionListItem`); - return this.kbnClient - .request<UpdateExceptionListItemResponse>({ - path: '/api/exception_lists/items', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'PUT', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } -} - -export interface CreateExceptionListProps { - body: CreateExceptionListRequestBodyInput; -} -export interface CreateExceptionListItemProps { - body: CreateExceptionListItemRequestBodyInput; -} -export interface CreateRuleExceptionListItemsProps { - params: CreateRuleExceptionListItemsRequestParamsInput; - body: CreateRuleExceptionListItemsRequestBodyInput; -} -export interface CreateSharedExceptionListProps { - body: CreateSharedExceptionListRequestBodyInput; -} -export interface DeleteExceptionListProps { - query: DeleteExceptionListRequestQueryInput; -} -export interface DeleteExceptionListItemProps { - query: DeleteExceptionListItemRequestQueryInput; -} -export interface DuplicateExceptionListProps { - query: DuplicateExceptionListRequestQueryInput; -} -export interface ExportExceptionListProps { - query: ExportExceptionListRequestQueryInput; -} -export interface FindExceptionListItemsProps { - query: FindExceptionListItemsRequestQueryInput; -} -export interface FindExceptionListsProps { - query: FindExceptionListsRequestQueryInput; -} -export interface ImportExceptionListProps { - query: ImportExceptionListRequestQueryInput; - attachment: FormData; -} -export interface ReadExceptionListProps { - query: ReadExceptionListRequestQueryInput; -} -export interface ReadExceptionListItemProps { - query: ReadExceptionListItemRequestQueryInput; -} -export interface ReadExceptionListSummaryProps { - query: ReadExceptionListSummaryRequestQueryInput; -} -export interface UpdateExceptionListProps { - body: UpdateExceptionListRequestBodyInput; -} -export interface UpdateExceptionListItemProps { - body: UpdateExceptionListItemRequestBodyInput; -} diff --git a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.gen.ts b/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.gen.ts deleted file mode 100644 index 67a832b01195..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.gen.ts +++ /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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Read exception list API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { - ExceptionListId, - ExceptionListHumanId, - ExceptionNamespaceType, - ExceptionList, -} from '../model/exception_list_common.gen'; - -export type ReadExceptionListRequestQuery = z.infer<typeof ReadExceptionListRequestQuery>; -export const ReadExceptionListRequestQuery = z.object({ - /** - * Either `id` or `list_id` must be specified - */ - id: ExceptionListId.optional(), - /** - * Either `id` or `list_id` must be specified - */ - list_id: ExceptionListHumanId.optional(), - namespace_type: ExceptionNamespaceType.optional().default('single'), -}); -export type ReadExceptionListRequestQueryInput = z.input<typeof ReadExceptionListRequestQuery>; - -export type ReadExceptionListResponse = z.infer<typeof ReadExceptionListResponse>; -export const ReadExceptionListResponse = ExceptionList; diff --git a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.schema.yaml deleted file mode 100644 index 0bf082c1713b..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.schema.yaml +++ /dev/null @@ -1,70 +0,0 @@ -openapi: 3.0.0 -info: - title: Read exception list API endpoint - version: '2023-10-31' -paths: - /api/exception_lists: - get: - x-labels: [serverless, ess] - operationId: ReadExceptionList - x-codegen-enabled: true - summary: Get exception list details - description: Get the details of an exception list using the `id` or `list_id` field. - parameters: - - name: id - in: query - required: false - description: Either `id` or `list_id` must be specified - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListId' - - name: list_id - in: query - required: false - description: Either `id` or `list_id` must be specified - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListHumanId' - - name: namespace_type - in: query - required: false - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' - default: single - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionList' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: Exception list item not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.schema.yaml deleted file mode 100644 index c271016a87eb..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.schema.yaml +++ /dev/null @@ -1,70 +0,0 @@ -openapi: 3.0.0 -info: - title: Read exception list item API endpoint - version: '2023-10-31' -paths: - /api/exception_lists/items: - get: - x-labels: [serverless, ess] - operationId: ReadExceptionListItem - x-codegen-enabled: true - summary: Get an exception list item - description: Get the details of an exception list item using the `id` or `item_id` field. - parameters: - - name: id - in: query - required: false - description: Either `id` or `item_id` must be specified - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemId' - - name: item_id - in: query - required: false - description: Either `id` or `item_id` must be specified - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemHumanId' - - name: namespace_type - in: query - required: false - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' - default: single - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItem' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: Exception list item not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.schema.yaml deleted file mode 100644 index b0627111e877..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.schema.yaml +++ /dev/null @@ -1,89 +0,0 @@ -openapi: 3.0.0 -info: - title: Read exception list summary API endpoint - version: '2023-10-31' -paths: - /api/exception_lists/summary: - get: - x-labels: [serverless, ess] - operationId: ReadExceptionListSummary - x-codegen-enabled: true - summary: Get an exception list summary - description: Get a summary of the specified exception list. - parameters: - - name: id - in: query - required: false - description: Exception list's identifier generated upon creation - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListId' - - name: list_id - in: query - required: false - description: Exception list's human readable identifier - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListHumanId' - - name: namespace_type - in: query - required: false - schema: - $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' - default: single - - name: filter - in: query - required: false - description: Search filter clause - schema: - type: string - responses: - 200: - description: Successful response - content: - application/json: - schema: - type: object - properties: - windows: - type: integer - minimum: 0 - linux: - type: integer - minimum: 0 - macos: - type: integer - minimum: 0 - total: - type: integer - minimum: 0 - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: Exception list not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/package.json b/packages/kbn-securitysolution-exceptions-common/package.json deleted file mode 100644 index 5148c4d5eedf..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "description": "Security Solution Exceptions common package", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "name": "@kbn/securitysolution-exceptions-common", - "private": true, - "version": "1.0.0", - "scripts": { - "openapi:generate": "node scripts/openapi_generate", - "openapi:bundle": "node scripts/openapi_bundle" - } -} diff --git a/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle.js b/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle.js deleted file mode 100644 index 70299e56eac2..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle.js +++ /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". - */ - -require('../../../src/setup_node_env'); -const { join, resolve } = require('path'); -const { bundle } = require('@kbn/openapi-bundler'); - -const ROOT = resolve(__dirname, '..'); - -(async () => { - await bundle({ - sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), - outputFilePath: join( - ROOT, - 'docs/openapi/serverless/security_solution_exceptions_api_{version}.bundled.schema.yaml' - ), - options: { - includeLabels: ['serverless'], - prototypeDocument: join(ROOT, 'scripts/openapi_bundle_info/exceptions_serverless.info.yaml'), - }, - }); - - await bundle({ - sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), - outputFilePath: join( - ROOT, - 'docs/openapi/ess/security_solution_exceptions_api_{version}.bundled.schema.yaml' - ), - options: { - includeLabels: ['ess'], - prototypeDocument: join(ROOT, 'scripts/openapi_bundle_info/exceptions_ess.info.yaml'), - }, - }); -})(); diff --git a/packages/kbn-securitysolution-exceptions-common/scripts/openapi_generate.js b/packages/kbn-securitysolution-exceptions-common/scripts/openapi_generate.js deleted file mode 100644 index 13b260476f3a..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/scripts/openapi_generate.js +++ /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". - */ - -require('../../../src/setup_node_env'); -const { join, resolve } = require('path'); -const { generate } = require('@kbn/openapi-generator'); -const { REPO_ROOT } = require('@kbn/repo-info'); - -const ROOT = resolve(__dirname, '..'); - -(async () => { - await generate({ - title: 'OpenAPI Exceptions API Schemas', - rootDir: ROOT, - sourceGlob: './api/**/*.schema.yaml', - templateName: 'zod_operation_schema', - }); - - await generate({ - title: 'Exceptions API client for tests', - rootDir: ROOT, - sourceGlob: './api/**/*.schema.yaml', - templateName: 'api_client_supertest', - skipLinting: true, - bundle: { - outFile: join( - REPO_ROOT, - 'x-pack/test/api_integration/services/security_solution_exceptions_api.gen.ts' - ), - }, - }); - - await generate({ - title: 'Exceptions API client for quickstart', - rootDir: ROOT, - sourceGlob: './api/**/*.schema.yaml', - templateName: 'api_client_quickstart', - skipLinting: true, - bundle: { - outFile: join( - REPO_ROOT, - 'packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts' - ), - }, - }); -})(); diff --git a/packages/kbn-securitysolution-exceptions-common/tsconfig.json b/packages/kbn-securitysolution-exceptions-common/tsconfig.json deleted file mode 100644 index a58753f53a4f..000000000000 --- a/packages/kbn-securitysolution-exceptions-common/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "outDir": "target/types", - "types": ["jest", "node"] - }, - "exclude": ["target/**/*"], - "extends": "../../tsconfig.base.json", - "include": ["**/*.ts"], - "kbn_references": [ - "@kbn/openapi-common", - "@kbn/zod-helpers", - "@kbn/securitysolution-lists-common", - "@kbn/test", - "@kbn/tooling-log", - "@kbn/core-http-common", - "@kbn/securitysolution-utils", - "@kbn/zod", - ] -} diff --git a/packages/kbn-securitysolution-hook-utils/index.ts b/packages/kbn-securitysolution-hook-utils/index.ts deleted file mode 100644 index be336bddcf9b..000000000000 --- a/packages/kbn-securitysolution-hook-utils/index.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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './src/types'; -export * from './src/use_async'; -export * from './src/use_is_mounted'; -export * from './src/use_observable'; -export * from './src/with_optional_signal'; diff --git a/packages/kbn-securitysolution-hook-utils/jest.config.js b/packages/kbn-securitysolution-hook-utils/jest.config.js deleted file mode 100644 index badea193370b..000000000000 --- a/packages/kbn-securitysolution-hook-utils/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-hook-utils'], -}; diff --git a/packages/kbn-securitysolution-hook-utils/package.json b/packages/kbn-securitysolution-hook-utils/package.json deleted file mode 100644 index 71a83ce3ebb4..000000000000 --- a/packages/kbn-securitysolution-hook-utils/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@kbn/securitysolution-hook-utils", - "version": "1.0.0", - "description": "Security Solution utilities for React hooks", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "private": true, - "sideEffects": false -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-hook-utils/src/types.ts b/packages/kbn-securitysolution-hook-utils/src/types.ts deleted file mode 100644 index 6642afac7381..000000000000 --- a/packages/kbn-securitysolution-hook-utils/src/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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". - */ - -/** - * Represents the state of an asynchronous task, along with an initiator - * function to kick off the work. - */ -export interface Task<Args extends unknown[], Result> { - loading: boolean; - error: unknown | undefined; - result: Result | undefined; - start: (...args: Args) => void; -} diff --git a/packages/kbn-securitysolution-hook-utils/src/use_async/index.test.ts b/packages/kbn-securitysolution-hook-utils/src/use_async/index.test.ts deleted file mode 100644 index 99417c1fe329..000000000000 --- a/packages/kbn-securitysolution-hook-utils/src/use_async/index.test.ts +++ /dev/null @@ -1,166 +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 { waitFor, renderHook, act } from '@testing-library/react'; - -import { useAsync } from '.'; - -interface TestArgs { - n: number; - s: string; -} - -type TestReturn = Promise<unknown>; - -describe('useAsync', () => { - /** - * Timeout for both jest tests and for the waitFor. - * jest tests default to 5 seconds and waitFor defaults to 1 second. - * 20_0000 = 20,000 milliseconds = 20 seconds - */ - const timeout = 20_000; - - let fn: jest.Mock<TestReturn, TestArgs[]>; - let args: TestArgs; - - beforeEach(() => { - args = { n: 1, s: 's' }; - fn = jest.fn().mockResolvedValue(false); - }); - - it('does not invoke fn if start was not called', () => { - renderHook(() => useAsync(fn)); - expect(fn).not.toHaveBeenCalled(); - }); - - it( - 'invokes the function when start is called', - async () => { - const { result } = renderHook(() => useAsync(fn)); - - act(() => { - result.current.start(args); - }); - await waitFor(() => expect(fn).toHaveBeenCalled(), { timeout }); - }, - timeout - ); - - it('invokes the function with start args', async () => { - const { result } = renderHook(() => useAsync(fn)); - const expectedArgs = { ...args }; - - act(() => { - result.current.start(args); - }); - await waitFor(() => expect(fn).toHaveBeenCalledWith(expectedArgs), { timeout }); - }); - - it( - 'populates result with the resolved value of the fn', - async () => { - const { result } = renderHook(() => useAsync(fn)); - fn.mockResolvedValue({ resolved: 'value' }); - - act(() => { - result.current.start(args); - }); - await waitFor( - () => { - expect(result.current.result).toEqual({ resolved: 'value' }); - expect(result.current.error).toBeUndefined(); - }, - { timeout } - ); - }, - timeout - ); - - it( - 'populates error if function rejects', - async () => { - fn.mockRejectedValue(new Error('whoops')); - const { result } = renderHook(() => useAsync(fn)); - - act(() => { - result.current.start(args); - }); - - await waitFor( - () => { - expect(result.current.result).toBeUndefined(); - expect(result.current.error).toEqual(new Error('whoops')); - }, - { timeout } - ); - }, - timeout - ); - - it( - 'populates the loading state while the function is pending', - async () => { - let resolve: () => void; - fn.mockImplementation(() => new Promise<void>((_resolve) => (resolve = _resolve))); - - const { result } = renderHook(() => useAsync(fn)); - - act(() => { - result.current.start(args); - }); - - expect(result.current.loading).toBe(true); - - act(() => resolve()); - await waitFor(() => expect(result.current.loading).toBe(false), { timeout }); - }, - timeout - ); - - it( - 'multiple start calls reset state', - async () => { - let resolve: (result: string) => void; - fn.mockImplementation(() => new Promise((_resolve) => (resolve = _resolve))); - - const { result } = renderHook(() => useAsync(fn)); - - act(() => { - result.current.start(args); - }); - - expect(result.current.loading).toBe(true); - - act(() => resolve('result')); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - expect(result.current.result).toBe('result'); - }, - { timeout } - ); - - act(() => { - result.current.start(args); - }); - - expect(result.current.loading).toBe(true); - expect(result.current.result).toBe(undefined); - act(() => resolve('result')); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - expect(result.current.result).toBe('result'); - }, - { timeout } - ); - }, - timeout - ); -}); diff --git a/packages/kbn-securitysolution-hook-utils/src/use_async/index.ts b/packages/kbn-securitysolution-hook-utils/src/use_async/index.ts deleted file mode 100644 index 2c695b26acdd..000000000000 --- a/packages/kbn-securitysolution-hook-utils/src/use_async/index.ts +++ /dev/null @@ -1,56 +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 { useCallback, useState } from 'react'; - -import { Task } from '../types'; -import { useIsMounted } from '../use_is_mounted'; - -/** - * - * This hook wraps a promise-returning thunk (task) in order to conditionally - * initiate the work, and automatically provide state corresponding to the - * task's status. - * - * In order to function properly and not rerender unnecessarily, ensure that - * your task is a stable function reference. - * - * @param fn a function returning a promise. - * - * @returns An {@link Task} containing the task's current state along with a - * start callback - */ -export const useAsync = <Args extends unknown[], Result>( - fn: (...args: Args) => Promise<Result> -): Task<Args, Result> => { - const isMounted = useIsMounted(); - const [loading, setLoading] = useState(false); - const [error, setError] = useState<unknown | undefined>(); - const [result, setResult] = useState<Result | undefined>(); - - const start = useCallback( - (...args: Args) => { - setLoading(true); - setResult(undefined); - setError(undefined); - fn(...args) - .then((r) => isMounted() && setResult(r)) - .catch((e) => isMounted() && setError(e)) - .finally(() => isMounted() && setLoading(false)); - }, - [fn, isMounted] - ); - - return { - error, - loading, - result, - start, - }; -}; diff --git a/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.test.ts b/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.test.ts deleted file mode 100644 index a8013a65441c..000000000000 --- a/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { renderHook } from '@testing-library/react'; - -import { useIsMounted } from '.'; - -describe('useIsMounted', () => { - it('evaluates to true when mounted', () => { - const { result } = renderHook(() => useIsMounted()); - - expect(result.current()).toEqual(true); - }); - - it('evaluates to false when unmounted', () => { - const { result, unmount } = renderHook(() => useIsMounted()); - - unmount(); - expect(result.current()).toEqual(false); - }); -}); diff --git a/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.ts b/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.ts deleted file mode 100644 index b563840d2f73..000000000000 --- a/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.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", 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 { useCallback, useEffect, useRef } from 'react'; - -type GetIsMounted = () => boolean; - -/** - * - * @returns A {@link GetIsMounted} getter function returning whether the component is currently mounted - */ -export const useIsMounted = (): GetIsMounted => { - const isMounted = useRef(false); - const getIsMounted: GetIsMounted = useCallback(() => isMounted.current, []); - const handleCleanup = useCallback(() => { - isMounted.current = false; - }, []); - - useEffect(() => { - isMounted.current = true; - return handleCleanup; - }, [handleCleanup]); - - return getIsMounted; -}; diff --git a/packages/kbn-securitysolution-hook-utils/src/use_observable/index.test.ts b/packages/kbn-securitysolution-hook-utils/src/use_observable/index.test.ts deleted file mode 100644 index 8d90bffeb3ee..000000000000 --- a/packages/kbn-securitysolution-hook-utils/src/use_observable/index.test.ts +++ /dev/null @@ -1,161 +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 { renderHook, act } from '@testing-library/react'; -import { Subject, throwError } from 'rxjs'; - -import { useObservable } from '.'; - -interface TestArgs { - n: number; - s: string; -} - -type TestReturn = Subject<unknown>; - -describe('useObservable', () => { - let fn: jest.Mock<TestReturn, TestArgs[]>; - let subject: TestReturn; - let args: TestArgs; - - beforeEach(() => { - args = { n: 1, s: 's' }; - subject = new Subject(); - fn = jest.fn().mockReturnValue(subject); - }); - - it('does not invoke fn if start was not called', () => { - renderHook(() => useObservable(fn)); - expect(fn).not.toHaveBeenCalled(); - }); - - it('invokes the function when start is called', () => { - const { result } = renderHook(() => useObservable(fn)); - - act(() => { - result.current.start(args); - }); - - expect(fn).toHaveBeenCalled(); - }); - - it('invokes the function with start args', () => { - const { result } = renderHook(() => useObservable(fn)); - const expectedArgs = { ...args }; - - act(() => { - result.current.start(args); - }); - - expect(fn).toHaveBeenCalledWith(expectedArgs); - }); - - it('populates result with the next value of the fn', () => { - const { result } = renderHook(() => useObservable(fn)); - - act(() => { - result.current.start(args); - }); - act(() => subject.next('value')); - - expect(result.current.result).toEqual('value'); - expect(result.current.error).toBeUndefined(); - }); - - it('populates error if observable throws an error', () => { - const error = new Error('whoops'); - const errorFn = () => throwError(error); - - const { result } = renderHook(() => useObservable(errorFn)); - - act(() => { - result.current.start(); - }); - - expect(result.current.result).toBeUndefined(); - expect(result.current.error).toEqual(error); - }); - - it('populates the loading state while no value has resolved', () => { - const { result } = renderHook(() => useObservable(fn)); - - act(() => { - result.current.start(args); - }); - - expect(result.current.loading).toBe(true); - - act(() => subject.next('a value')); - - expect(result.current.loading).toBe(false); - }); - - it('updates result with each resolved value', () => { - const { result } = renderHook(() => useObservable(fn)); - - act(() => { - result.current.start(args); - }); - - act(() => subject.next('a value')); - expect(result.current.result).toEqual('a value'); - - act(() => subject.next('a subsequent value')); - expect(result.current.result).toEqual('a subsequent value'); - }); - - it('does not update result with values if start has not been called', () => { - const { result } = renderHook(() => useObservable(fn)); - - act(() => subject.next('a value')); - expect(result.current.result).toBeUndefined(); - - act(() => subject.next('a subsequent value')); - expect(result.current.result).toBeUndefined(); - }); - - it('unsubscribes on unmount', () => { - const { result, unmount } = renderHook(() => useObservable(fn)); - - act(() => { - result.current.start(args); - }); - expect(subject.observers).toHaveLength(1); - - unmount(); - expect(subject.observers).toHaveLength(0); - }); - - it('multiple start calls reset state', () => { - const { result } = renderHook(() => useObservable(fn)); - - act(() => { - result.current.start(args); - }); - - expect(result.current.loading).toBe(true); - - act(() => subject.next('one value')); - - expect(result.current.loading).toBe(false); - expect(result.current.result).toBe('one value'); - - act(() => { - result.current.start(args); - }); - - expect(result.current.loading).toBe(true); - expect(result.current.result).toBe(undefined); - - act(() => subject.next('another value')); - - expect(result.current.loading).toBe(false); - expect(result.current.result).toBe('another value'); - }); -}); diff --git a/packages/kbn-securitysolution-hook-utils/src/use_observable/index.ts b/packages/kbn-securitysolution-hook-utils/src/use_observable/index.ts deleted file mode 100644 index 0b7f9ab93e02..000000000000 --- a/packages/kbn-securitysolution-hook-utils/src/use_observable/index.ts +++ /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", 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 { useCallback, useEffect, useRef, useReducer, Reducer } from 'react'; -import { Observable, Subscription } from 'rxjs'; - -import { useIsMounted } from '../use_is_mounted'; -import { Task } from '../types'; - -interface State<T> { - loading: boolean; - error?: unknown; - result?: T; -} - -export type Action<T> = - | { type: 'setResult'; result: T } - | { type: 'setError'; error: unknown } - | { type: 'load' }; - -function reducer<T>(state: State<T>, action: Action<T>) { - switch (action.type) { - case 'setResult': - return { ...state, result: action.result, loading: false }; - case 'setError': - return { ...state, error: action.error, loading: false }; - case 'load': - return { loading: true, result: undefined, error: undefined }; - } -} - -/** - * - * @param fn function returning an observable - * - * @returns An {@link Async} containing the underlying task's state along with a start callback - */ -export const useObservable = <Args extends unknown[], Result>( - fn: (...args: Args) => Observable<Result> -): Task<Args, Result> => { - const isMounted = useIsMounted(); - const subRef = useRef<Subscription | undefined>(); - const [state, dispatch] = useReducer<Reducer<State<Result>, Action<Result>>>(reducer, { - loading: false, - error: undefined, - result: undefined, - }); - - const start = useCallback( - (...args: Args) => { - if (subRef.current) { - subRef.current.unsubscribe(); - } - dispatch({ type: 'load' }); - - subRef.current = fn(...args).subscribe( - (r) => { - if (isMounted()) { - dispatch({ type: 'setResult', result: r }); - } - }, - (e) => { - if (isMounted()) { - dispatch({ type: 'setError', error: e }); - } - } - ); - }, - [fn, isMounted] - ); - - useEffect( - () => () => { - if (subRef.current) { - subRef.current.unsubscribe(); - } - }, - [] - ); - - return { - result: state.result, - error: state.error, - loading: state.loading, - start, - }; -}; diff --git a/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.test.ts b/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.test.ts deleted file mode 100644 index fa77482d24be..000000000000 --- a/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.test.ts +++ /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 { withOptionalSignal } from '.'; - -type TestFn = ({ number, signal }: { number: number; signal: AbortSignal }) => boolean; - -describe('withOptionalSignal', () => { - it('does not require a signal on the returned function', () => { - const fn = jest.fn().mockReturnValue('hello') as TestFn; - - const wrappedFn = withOptionalSignal(fn); - - expect(wrappedFn({ number: 1 })).toEqual('hello'); - }); - - it('will pass a given signal to the wrapped function', () => { - const fn = jest.fn().mockReturnValue('hello') as TestFn; - const { signal } = new AbortController(); - - const wrappedFn = withOptionalSignal(fn); - - wrappedFn({ number: 1, signal }); - expect(fn).toHaveBeenCalledWith({ number: 1, signal }); - }); -}); diff --git a/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.ts b/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.ts deleted file mode 100644 index eb7cffeb7e00..000000000000 --- a/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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". - */ - -interface SignalArgs { - signal: AbortSignal; -} - -export type OptionalSignalArgs<Args> = Omit<Args, 'signal'> & Partial<SignalArgs>; - -/** - * - * @param fn an async function receiving an AbortSignal argument - * - * @returns An async function where the AbortSignal argument is optional - */ -export const withOptionalSignal = - <Args extends SignalArgs, Result>(fn: (args: Args) => Result) => - (args: OptionalSignalArgs<Args>): Result => { - const signal = args.signal != null ? args.signal : new AbortController().signal; - return fn({ ...args, signal } as Args); - }; diff --git a/packages/kbn-securitysolution-hook-utils/tsconfig.json b/packages/kbn-securitysolution-hook-utils/tsconfig.json deleted file mode 100644 index 9b5c5373afe0..000000000000 --- a/packages/kbn-securitysolution-hook-utils/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": ["jest", "node"] - }, - "include": [ - "**/*.ts" - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/index.ts deleted file mode 100644 index 6baaf0a70403..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/index.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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './src/actions'; -export * from './src/default_actions_array'; -export * from './src/default_export_file_name'; -export * from './src/default_from_string'; -export * from './src/default_interval_string'; -export * from './src/default_language_string'; -export * from './src/default_max_signals_number'; -export * from './src/default_page'; -export * from './src/default_per_page'; -export * from './src/default_risk_score_mapping_array'; -export * from './src/default_severity_mapping_array'; -export * from './src/default_threat_array'; -export * from './src/default_to_string'; -export * from './src/default_uuid'; -export * from './src/frequency'; -export * from './src/language'; -export * from './src/machine_learning_job_id'; -export * from './src/max_signals'; -export * from './src/normalized_ml_job_id'; -export * from './src/references_default_array'; -export * from './src/risk_score'; -export * from './src/risk_score_mapping'; -export * from './src/rule_schedule'; -export * from './src/saved_object_attributes'; -export * from './src/severity'; -export * from './src/severity_mapping'; -export * from './src/threat'; -export * from './src/threat_mapping'; -export * from './src/threat_subtechnique'; -export * from './src/threat_tactic'; -export * from './src/threat_technique'; -export * from './src/throttle'; -export * from './src/type'; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/jest.config.js b/packages/kbn-securitysolution-io-ts-alerting-types/jest.config.js deleted file mode 100644 index b4c7014f9154..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-io-ts-alerting-types'], -}; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/package.json b/packages/kbn-securitysolution-io-ts-alerting-types/package.json deleted file mode 100644 index ab19f69d20b3..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@kbn/securitysolution-io-ts-alerting-types", - "version": "1.0.0", - "description": "io ts utilities and types to be shared with plugins from the security solution project", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "private": true, - "sideEffects": false -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts deleted file mode 100644 index ad1dde3da745..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts +++ /dev/null @@ -1,129 +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 { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; - -import * as t from 'io-ts'; -import { saved_object_attributes } from '../saved_object_attributes'; -import { RuleActionFrequency } from '../frequency'; - -export type RuleActionGroup = t.TypeOf<typeof RuleActionGroup>; -export const RuleActionGroup = t.string; - -export type RuleActionId = t.TypeOf<typeof RuleActionId>; -export const RuleActionId = t.string; - -export type RuleActionTypeId = t.TypeOf<typeof RuleActionTypeId>; -export const RuleActionTypeId = t.string; - -export type RuleActionUuid = t.TypeOf<typeof RuleActionUuid>; -export const RuleActionUuid = NonEmptyString; - -/** - * Params is an "object", since it is a type of RuleActionParams which is action templates. - * @see x-pack/plugins/alerting/common/rule.ts - */ -export type RuleActionParams = t.TypeOf<typeof RuleActionParams>; -export const RuleActionParams = saved_object_attributes; - -export const RuleActionAlertsFilter = t.partial({ - query: t.union([ - t.undefined, - t.intersection([ - t.strict({ - kql: t.string, - filters: t.array( - t.intersection([ - t.type({ - meta: t.partial({ - alias: t.union([t.string, t.null]), - disabled: t.boolean, - negate: t.boolean, - controlledBy: t.string, - group: t.string, - index: t.string, - isMultiIndex: t.boolean, - type: t.string, - key: t.string, - params: t.any, - value: t.string, - }), - }), - t.partial({ - $state: t.type({ store: t.any }), - query: t.record(t.string, t.any), - }), - ]) - ), - }), - t.partial({ dsl: t.string }), - ]), - ]), - timeframe: t.union([ - t.undefined, - t.strict({ - timezone: t.string, - days: t.array( - t.union([ - t.literal(1), - t.literal(2), - t.literal(3), - t.literal(4), - t.literal(5), - t.literal(6), - t.literal(7), - ]) - ), - hours: t.strict({ - start: t.string, - end: t.string, - }), - }), - ]), -}); - -export type RuleAction = t.TypeOf<typeof RuleAction>; -export const RuleAction = t.exact( - t.intersection([ - t.type({ - group: RuleActionGroup, - id: RuleActionId, - action_type_id: RuleActionTypeId, - params: RuleActionParams, - }), - t.partial({ - uuid: RuleActionUuid, - alerts_filter: RuleActionAlertsFilter, - frequency: RuleActionFrequency, - }), - ]) -); - -export type RuleActionArray = t.TypeOf<typeof RuleActionArray>; -export const RuleActionArray = t.array(RuleAction); - -export type RuleActionCamel = t.TypeOf<typeof RuleActionCamel>; -export const RuleActionCamel = t.exact( - t.intersection([ - t.type({ - group: RuleActionGroup, - id: RuleActionId, - actionTypeId: RuleActionTypeId, - params: RuleActionParams, - }), - t.partial({ - uuid: RuleActionUuid, - alertsFilter: RuleActionAlertsFilter, - frequency: RuleActionFrequency, - }), - ]) -); - -export type RuleActionArrayCamel = t.TypeOf<typeof RuleActionArrayCamel>; -export const RuleActionArrayCamel = t.array(RuleActionCamel); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.mock.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.mock.ts deleted file mode 100644 index bb398e0df846..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.mock.ts +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export const ENTRY_VALUE = 'some host name'; -export const FIELD = 'host.name'; -export const MATCH = 'match'; -export const MATCH_ANY = 'match_any'; -export const OPERATOR = 'included'; -export const NESTED = 'nested'; -export const NESTED_FIELD = 'parent.field'; -export const LIST_ID = 'some-list-id'; -export const LIST = 'list'; -export const TYPE = 'ip'; -export const EXISTS = 'exists'; -export const WILDCARD = 'wildcard'; -export const USER = 'some user'; -export const DATE_NOW = '2020-04-20T15:25:31.830Z'; - -// Exception List specific -export const ID = 'uuid_here'; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.ts deleted file mode 100644 index 0c930e5f4a80..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.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", 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". - */ - -/** - * TODO: Create a kbn-alerting-constants and add this to it. - * @deprecated Use a DEFAULT_MAX_SIGNALS from a kbn-alerting-constants package. - */ -export const DEFAULT_MAX_SIGNALS = 100; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_actions_array/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_actions_array/index.ts deleted file mode 100644 index 17c3f9301c6d..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_actions_array/index.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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { RuleActionArray } from '../actions'; - -export const DefaultActionsArray = new t.Type< - RuleActionArray, - RuleActionArray | undefined, - unknown ->( - 'DefaultActionsArray', - RuleActionArray.is, - (input, context): Either<t.Errors, RuleActionArray> => - input == null ? t.success([]) : RuleActionArray.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts deleted file mode 100644 index b07ade238db2..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts +++ /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", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultExportFileName } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_export_file_name', () => { - test('it should validate a regular string', () => { - const payload = 'some string'; - const decoded = DefaultExportFileName.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate a number', () => { - const payload = 5; - const decoded = DefaultExportFileName.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "DefaultExportFileName"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default of "export.ndjson"', () => { - const payload = null; - const decoded = DefaultExportFileName.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual('export.ndjson'); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.ts deleted file mode 100644 index dedbadfa6f52..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; - -/** - * Types the DefaultExportFileName as: - * - If null or undefined, then a default of "export.ndjson" will be used - */ -export const DefaultExportFileName = new t.Type<string, string | undefined, unknown>( - 'DefaultExportFileName', - t.string.is, - (input, context): Either<t.Errors, string> => - input == null ? t.success('export.ndjson') : t.string.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts deleted file mode 100644 index 2515b3f8b015..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts +++ /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", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultFromString } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_from_string', () => { - test('it should validate a from string', () => { - const payload = 'now-20m'; - const decoded = DefaultFromString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate a number', () => { - const payload = 5; - const decoded = DefaultFromString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "DefaultFromString"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default of "now-6m"', () => { - const payload = null; - const decoded = DefaultFromString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual('now-6m'); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.ts deleted file mode 100644 index ccf70aab8a7b..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { From } from '../from'; - -/** - * Types the DefaultFromString as: - * - If null or undefined, then a default of the string "now-6m" will be used - */ -export const DefaultFromString = new t.Type<string, string | undefined, unknown>( - 'DefaultFromString', - t.string.is, - (input, context): Either<t.Errors, string> => { - if (input == null) { - return t.success('now-6m'); - } - return From.validate(input, context); - }, - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts deleted file mode 100644 index 8944d57ba899..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts +++ /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", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultIntervalString } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_interval_string', () => { - test('it should validate a interval string', () => { - const payload = '20m'; - const decoded = DefaultIntervalString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate a number', () => { - const payload = 5; - const decoded = DefaultIntervalString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "DefaultIntervalString"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default of "5m"', () => { - const payload = null; - const decoded = DefaultIntervalString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual('5m'); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.ts deleted file mode 100644 index 3fc5d4f42ccb..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; - -/** - * Types the DefaultIntervalString as: - * - If null or undefined, then a default of the string "5m" will be used - */ -export const DefaultIntervalString = new t.Type<string, string | undefined, unknown>( - 'DefaultIntervalString', - t.string.is, - (input, context): Either<t.Errors, string> => - input == null ? t.success('5m') : t.string.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts deleted file mode 100644 index 4a8d50c88eaa..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { Language } from '../language'; -import { DefaultLanguageString } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_language_string', () => { - test('it should validate a string', () => { - const payload: Language = 'lucene'; - const decoded = DefaultLanguageString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate a number', () => { - const payload = 5; - const decoded = DefaultLanguageString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "DefaultLanguageString"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default of "kuery"', () => { - const payload = null; - const decoded = DefaultLanguageString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual('kuery'); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.ts deleted file mode 100644 index 02328d94b011..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { language } from '../language'; - -/** - * Types the DefaultLanguageString as: - * - If null or undefined, then a default of the string "kuery" will be used - */ -export const DefaultLanguageString = new t.Type<string, string | undefined, unknown>( - 'DefaultLanguageString', - t.string.is, - (input, context): Either<t.Errors, string> => - input == null ? t.success('kuery') : language.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts deleted file mode 100644 index 66eb77400ac1..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts +++ /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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultMaxSignalsNumber } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { DEFAULT_MAX_SIGNALS } from '../constants'; - -describe('default_from_string', () => { - test('it should validate a max signal number', () => { - const payload = 5; - const decoded = DefaultMaxSignalsNumber.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate a string', () => { - const payload = '5'; - const decoded = DefaultMaxSignalsNumber.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "DefaultMaxSignals"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not validate a zero', () => { - const payload = 0; - const decoded = DefaultMaxSignalsNumber.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "0" supplied to "DefaultMaxSignals"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not validate a negative number', () => { - const payload = -1; - const decoded = DefaultMaxSignalsNumber.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "-1" supplied to "DefaultMaxSignals"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default of DEFAULT_MAX_SIGNALS', () => { - const payload = null; - const decoded = DefaultMaxSignalsNumber.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(DEFAULT_MAX_SIGNALS); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.ts deleted file mode 100644 index 8acbc2f223a1..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { max_signals } from '../max_signals'; -import { DEFAULT_MAX_SIGNALS } from '../constants'; - -/** - * Types the default max signal: - * - Natural Number (positive integer and not a float), - * - greater than 1 - * - If undefined then it will use DEFAULT_MAX_SIGNALS (100) as the default - */ -export const DefaultMaxSignalsNumber = new t.Type<number, number | undefined, unknown>( - 'DefaultMaxSignals', - t.number.is, - (input, context): Either<t.Errors, number> => { - return input == null ? t.success(DEFAULT_MAX_SIGNALS) : max_signals.validate(input, context); - }, - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts deleted file mode 100644 index 5af986fefe8d..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts +++ /dev/null @@ -1,86 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultPage } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_page', () => { - test('it should validate a regular number greater than zero', () => { - const payload = 5; - const decoded = DefaultPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate a string of a number', () => { - const payload = '5'; - const decoded = DefaultPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(5); - }); - - test('it should not validate a junk string', () => { - const payload = 'invalid-string'; - const decoded = DefaultPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "NaN" supplied to "DefaultPerPage"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not validate an empty string', () => { - const payload = ''; - const decoded = DefaultPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "NaN" supplied to "DefaultPerPage"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not validate a zero', () => { - const payload = 0; - const decoded = DefaultPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "0" supplied to "DefaultPerPage"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not validate a negative number', () => { - const payload = -1; - const decoded = DefaultPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "-1" supplied to "DefaultPerPage"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default of 20', () => { - const payload = null; - const decoded = DefaultPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(1); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.ts deleted file mode 100644 index 8486c516aebf..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.ts +++ /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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; - -/** - * Types the DefaultPerPage as: - * - If a string this will convert the string to a number - * - If null or undefined, then a default of 1 will be used - * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero - */ -export const DefaultPage = new t.Type<number, number | undefined, unknown>( - 'DefaultPerPage', - t.number.is, - (input, context): Either<t.Errors, number> => { - if (input == null) { - return t.success(1); - } else if (typeof input === 'string') { - return PositiveIntegerGreaterThanZero.validate(parseInt(input, 10), context); - } else { - return PositiveIntegerGreaterThanZero.validate(input, context); - } - }, - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts deleted file mode 100644 index 78fa2a9be8c8..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts +++ /dev/null @@ -1,86 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultPerPage } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_per_page', () => { - test('it should validate a regular number greater than zero', () => { - const payload = 5; - const decoded = DefaultPerPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate a string of a number', () => { - const payload = '5'; - const decoded = DefaultPerPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(5); - }); - - test('it should not validate a junk string', () => { - const payload = 'invalid-string'; - const decoded = DefaultPerPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "NaN" supplied to "DefaultPerPage"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not validate an empty string', () => { - const payload = ''; - const decoded = DefaultPerPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "NaN" supplied to "DefaultPerPage"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not validate a zero', () => { - const payload = 0; - const decoded = DefaultPerPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "0" supplied to "DefaultPerPage"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not validate a negative number', () => { - const payload = -1; - const decoded = DefaultPerPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "-1" supplied to "DefaultPerPage"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default of 20', () => { - const payload = null; - const decoded = DefaultPerPage.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(20); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.ts deleted file mode 100644 index 4f272fc0517e..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.ts +++ /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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; - -/** - * Types the DefaultPerPage as: - * - If a string this will convert the string to a number - * - If null or undefined, then a default of 20 will be used - * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero - */ -export const DefaultPerPage = new t.Type<number, number | undefined, unknown>( - 'DefaultPerPage', - t.number.is, - (input, context): Either<t.Errors, number> => { - if (input == null) { - return t.success(20); - } else if (typeof input === 'string') { - return PositiveIntegerGreaterThanZero.validate(parseInt(input, 10), context); - } else { - return PositiveIntegerGreaterThanZero.validate(input, context); - } - }, - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_risk_score_mapping_array/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_risk_score_mapping_array/index.ts deleted file mode 100644 index 9eb6938bcfcb..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_risk_score_mapping_array/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { RiskScoreMapping } from '../risk_score_mapping'; - -/** - * Types the DefaultStringArray as: - * - If null or undefined, then a default RiskScoreMapping array will be set - */ -export const DefaultRiskScoreMappingArray = new t.Type< - RiskScoreMapping, - RiskScoreMapping | undefined, - unknown ->( - 'DefaultRiskScoreMappingArray', - RiskScoreMapping.is, - (input, context): Either<t.Errors, RiskScoreMapping> => - input == null ? t.success([]) : RiskScoreMapping.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_severity_mapping_array/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_severity_mapping_array/index.ts deleted file mode 100644 index ac72e3330f2a..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_severity_mapping_array/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { SeverityMapping } from '../severity_mapping'; - -/** - * Types the DefaultStringArray as: - * - If null or undefined, then a default SeverityMapping array will be set - */ -export const DefaultSeverityMappingArray = new t.Type< - SeverityMapping, - SeverityMapping | undefined, - unknown ->( - 'DefaultSeverityMappingArray', - SeverityMapping.is, - (input, context): Either<t.Errors, SeverityMapping> => - input == null ? t.success([]) : SeverityMapping.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts deleted file mode 100644 index ebd1ca9384b1..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts +++ /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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { Threats } from '../threat'; -import { DefaultThreatArray } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_threat_null', () => { - test('it should validate an empty array', () => { - const payload: Threats = []; - const decoded = DefaultThreatArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array of threats', () => { - const payload: Threats = [ - { - framework: 'MITRE ATTACK', - technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }], - tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA000999' }, - }, - ]; - const decoded = DefaultThreatArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate an array with a number', () => { - const payload = [ - { - framework: 'MITRE ATTACK', - technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }], - tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA000999' }, - }, - 5, - ]; - const decoded = DefaultThreatArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "DefaultThreatArray"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default empty array if not provided a value', () => { - const payload = null; - const decoded = DefaultThreatArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([]); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.ts deleted file mode 100644 index bb38b6382230..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { threats, Threats } from '../threat'; - -/** - * Types the DefaultThreatArray as: - * - If null or undefined, then an empty array will be set - */ -export const DefaultThreatArray = new t.Type<Threats, Threats | undefined, unknown>( - 'DefaultThreatArray', - threats.is, - (input, context): Either<t.Errors, Threats> => - input == null ? t.success([]) : threats.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts deleted file mode 100644 index e8bcdcc664cf..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts +++ /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", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultToString } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_to_string', () => { - test('it should validate a to string', () => { - const payload = 'now-5m'; - const decoded = DefaultToString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate a number', () => { - const payload = 5; - const decoded = DefaultToString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "DefaultToString"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default of "now"', () => { - const payload = null; - const decoded = DefaultToString.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual('now'); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.ts deleted file mode 100644 index 4074cc3bbbe9..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; - -/** - * Types the DefaultToString as: - * - If null or undefined, then a default of the string "now" will be used - */ -export const DefaultToString = new t.Type<string, string | undefined, unknown>( - 'DefaultToString', - t.string.is, - (input, context): Either<t.Errors, string> => - input == null ? t.success('now') : t.string.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts deleted file mode 100644 index 4144b4e6824c..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts +++ /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", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { v4 as uuidv4 } from 'uuid'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; - -/** - * Types the DefaultUuid as: - * - If null or undefined, then a default string uuidv4() will be - * created otherwise it will be checked just against an empty string - */ -export const DefaultUuid = new t.Type<string, string | undefined, unknown>( - 'DefaultUuid', - t.string.is, - (input, context): Either<t.Errors, string> => - input == null ? t.success(uuidv4()) : NonEmptyString.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts deleted file mode 100644 index 0247bfb155f3..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.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", 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 * as t from 'io-ts'; - -import { RuleActionThrottle } from '../throttle'; - -/** - * Action summary indicates whether we will send a summary notification about all the generate alerts or notification per individual alert - */ -export type RuleActionSummary = t.TypeOf<typeof RuleActionSummary>; -export const RuleActionSummary = t.boolean; - -/** - * The condition for throttling the notification: `onActionGroupChange`, `onActiveAlert`, or `onThrottleInterval` - */ -export type RuleActionNotifyWhen = t.TypeOf<typeof RuleActionNotifyWhen>; -export const RuleActionNotifyWhen = t.union([ - t.literal('onActionGroupChange'), - t.literal('onActiveAlert'), - t.literal('onThrottleInterval'), -]); - -/** - * The action frequency defines when the action runs (for example, only on rule execution or at specific time intervals). - */ -export type RuleActionFrequency = t.TypeOf<typeof RuleActionFrequency>; -export const RuleActionFrequency = t.type({ - summary: RuleActionSummary, - notifyWhen: RuleActionNotifyWhen, - throttle: t.union([RuleActionThrottle, t.null]), -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts deleted file mode 100644 index 390649416273..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { Either } from 'fp-ts/lib/Either'; -import * as t from 'io-ts'; -import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; - -const stringValidator = (input: unknown): input is string => typeof input === 'string'; - -export type From = t.TypeOf<typeof From>; -export const From = new t.Type<string, string, unknown>( - 'From', - t.string.is, - (input, context): Either<t.Errors, string> => { - if (stringValidator(input) && parseScheduleDates(input) == null) { - return t.failure(input, context, 'Failed to parse "from" on rule param'); - } - return t.string.validate(input, context); - }, - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/language/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/language/index.ts deleted file mode 100644 index 95017209b2c6..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/language/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", 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 * as t from 'io-ts'; - -export const language = t.keyof({ eql: null, kuery: null, lucene: null, esql: null }); -export type Language = t.TypeOf<typeof language>; - -export const languageOrUndefined = t.union([language, t.undefined]); -export type LanguageOrUndefined = t.TypeOf<typeof languageOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/machine_learning_job_id/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/machine_learning_job_id/index.ts deleted file mode 100644 index 00dc70eb5575..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/machine_learning_job_id/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -import { machine_learning_job_id_normalized } from '../normalized_ml_job_id'; - -export const machine_learning_job_id = t.union([t.string, machine_learning_job_id_normalized]); -export type MachineLearningJobId = t.TypeOf<typeof machine_learning_job_id>; - -export const machineLearningJobIdOrUndefined = t.union([machine_learning_job_id, t.undefined]); -export type MachineLearningJobIdOrUndefined = t.TypeOf<typeof machineLearningJobIdOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts deleted file mode 100644 index aa8bdf6f539a..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; -import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; - -export const max_signals = PositiveIntegerGreaterThanZero; -export type MaxSignals = t.TypeOf<typeof max_signals>; - -export const maxSignalsOrUndefined = t.union([max_signals, t.undefined]); -export type MaxSignalsOrUndefined = t.TypeOf<typeof maxSignalsOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.ts deleted file mode 100644 index 2d89147c4fa1..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -import { NonEmptyArray } from '@kbn/securitysolution-io-ts-types'; - -export const machine_learning_job_id_normalized = NonEmptyArray(t.string); -export type MachineLearningJobIdNormalized = t.TypeOf<typeof machine_learning_job_id_normalized>; - -export const machineLearningJobIdNormalizedOrUndefined = t.union([ - machine_learning_job_id_normalized, - t.undefined, -]); -export type MachineLearningJobIdNormalizedOrUndefined = t.TypeOf< - typeof machineLearningJobIdNormalizedOrUndefined ->; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts deleted file mode 100644 index 7b6146a320d6..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts +++ /dev/null @@ -1,53 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { ReferencesDefaultArray } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_string_array', () => { - test('it should validate an empty array', () => { - const payload: string[] = []; - const decoded = ReferencesDefaultArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array of strings', () => { - const payload = ['value 1', 'value 2']; - const decoded = ReferencesDefaultArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate an array with a number', () => { - const payload = ['value 1', 5]; - const decoded = ReferencesDefaultArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "referencesWithDefaultArray"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default array entry', () => { - const payload = null; - const decoded = ReferencesDefaultArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([]); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.ts deleted file mode 100644 index fcf22ad918c9..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; - -/** - * Types the ReferencesDefaultArray as: - * - If null or undefined, then a default array will be set - */ -export const ReferencesDefaultArray = new t.Type<string[], string[] | undefined, unknown>( - 'referencesWithDefaultArray', - t.array(t.string).is, - (input, context): Either<t.Errors, string[]> => - input == null ? t.success([]) : t.array(t.string).validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts deleted file mode 100644 index 85d83d0ec77d..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts +++ /dev/null @@ -1,62 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { RiskScore } from '.'; - -describe('risk_score', () => { - test('it should validate a positive number', () => { - const payload = 1; - const decoded = RiskScore.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate a zero', () => { - const payload = 0; - const decoded = RiskScore.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT validate a negative number', () => { - const payload = -1; - const decoded = RiskScore.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "-1" supplied to "RiskScore"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate a string', () => { - const payload = 'some string'; - const decoded = RiskScore.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "some string" supplied to "RiskScore"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate a risk score greater than 100', () => { - const payload = 101; - const decoded = RiskScore.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "101" supplied to "RiskScore"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts deleted file mode 100644 index ecc48e9a699d..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; - -/** - * Types the risk score as: - * - Natural Number (positive integer and not a float), - * - Between the values [0 and 100] inclusive. - */ -export type RiskScore = t.TypeOf<typeof RiskScore>; -export const RiskScore = new t.Type<number, number, unknown>( - 'RiskScore', - t.number.is, - (input, context): Either<t.Errors, number> => { - return typeof input === 'number' && Number.isSafeInteger(input) && input >= 0 && input <= 100 - ? t.success(input) - : t.failure(input, context); - }, - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.ts deleted file mode 100644 index 59f7c7ab69fb..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.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 * as t from 'io-ts'; -import { operator } from '@kbn/securitysolution-io-ts-types'; -import { RiskScore } from '../risk_score'; - -export type RiskScoreMappingItem = t.TypeOf<typeof RiskScoreMappingItem>; -export const RiskScoreMappingItem = t.exact( - t.type({ - field: t.string, - value: t.string, - operator, - risk_score: t.union([RiskScore, t.undefined]), - }) -); - -export type RiskScoreMapping = t.TypeOf<typeof RiskScoreMapping>; -export const RiskScoreMapping = t.array(RiskScoreMappingItem); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/rule_schedule/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/rule_schedule/index.ts deleted file mode 100644 index 4d0113f330ae..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/rule_schedule/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { From } from '../from'; - -export type RuleInterval = t.TypeOf<typeof RuleInterval>; -export const RuleInterval = t.string; // we need a more specific schema - -export type RuleIntervalFrom = t.TypeOf<typeof RuleIntervalFrom>; -export const RuleIntervalFrom = From; - -/** - * TODO: Create a regular expression type or custom date math part type here - */ -export type RuleIntervalTo = t.TypeOf<typeof RuleIntervalTo>; -export const RuleIntervalTo = t.string; // we need a more specific schema diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/saved_object_attributes/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/saved_object_attributes/index.ts deleted file mode 100644 index 2a7af4e66518..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/saved_object_attributes/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -/** - * TODO: This type are originally from "src/core/types/saved_objects.ts", once that is package friendly remove - * this copied type. - * - * Don't use this type, it's simply a helper type for {@link SavedObjectAttribute} - * - * @public - */ -export type SavedObjectAttributeSingle = - | string - | number - | boolean - | null - | undefined - | SavedObjectAttributes; - -/** - * TODO: This type are originally from "src/core/types/saved_objects.ts", once that is package friendly remove - * this copied type. - * - * Type definition for a Saved Object attribute value - * - * @public - */ -export type SavedObjectAttribute = SavedObjectAttributeSingle | SavedObjectAttributeSingle[]; - -/** - * TODO: This type are originally from "src/core/types/saved_objects.ts", once that is package friendly remove - * this copied type. - * - * The data for a Saved Object is stored as an object in the `attributes` - * property. - * - * @public - */ -export interface SavedObjectAttributes { - [key: string]: SavedObjectAttribute; -} - -export const saved_object_attribute_single: t.Type<SavedObjectAttributeSingle> = t.recursion( - 'saved_object_attribute_single', - () => t.union([t.string, t.number, t.boolean, t.null, t.undefined, saved_object_attributes]) -); -export const saved_object_attribute: t.Type<SavedObjectAttribute> = t.recursion( - 'saved_object_attribute', - () => t.union([saved_object_attribute_single, t.array(saved_object_attribute_single)]) -); -export const saved_object_attributes: t.Type<SavedObjectAttributes> = t.recursion( - 'saved_object_attributes', - () => t.record(t.string, saved_object_attribute) -); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/severity/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/severity/index.ts deleted file mode 100644 index fef1082129df..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/severity/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -export type Severity = t.TypeOf<typeof Severity>; -export const Severity = t.keyof({ low: null, medium: null, high: null, critical: null }); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts deleted file mode 100644 index afd7889658a3..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts +++ /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", 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 * as t from 'io-ts'; - -import { operator } from '@kbn/securitysolution-io-ts-types'; -import { Severity } from '../severity'; - -export type SeverityMappingItem = t.TypeOf<typeof SeverityMappingItem>; -export const SeverityMappingItem = t.exact( - t.type({ - field: t.string, - operator, - value: t.string, - severity: Severity, - }) -); - -export type SeverityMapping = t.TypeOf<typeof SeverityMapping>; -export const SeverityMapping = t.array(SeverityMappingItem); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts deleted file mode 100644 index e60f9ccafcc1..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; -import { threat_tactic } from '../threat_tactic'; -import { threat_techniques } from '../threat_technique'; - -export const threat_framework = t.string; - -export const threat = t.intersection([ - t.exact( - t.type({ - framework: threat_framework, - tactic: threat_tactic, - }) - ), - t.exact( - t.partial({ - technique: threat_techniques, - }) - ), -]); - -export type Threat = t.TypeOf<typeof threat>; - -export const threats = t.array(threat); -export type Threats = t.TypeOf<typeof threats>; - -export const threatsOrUndefined = t.union([threats, t.undefined]); -export type ThreatsOrUndefined = t.TypeOf<typeof threatsOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts deleted file mode 100644 index 3d3e1ab9a22b..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts +++ /dev/null @@ -1,237 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { - concurrent_searches, - items_per_search, - ThreatMapping, - threatMappingEntries, - ThreatMappingEntries, - threat_mapping, -} from '.'; -import { foldLeftRight, getPaths, exactCheck } from '@kbn/securitysolution-io-ts-utils'; - -describe('threat_mapping', () => { - describe('threatMappingEntries', () => { - test('it should validate an entry', () => { - const payload: ThreatMappingEntries = [ - { - field: 'field.one', - type: 'mapping', - value: 'field.one', - }, - ]; - const decoded = threatMappingEntries.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation with an extra entry item', () => { - const payload: Array<ThreatMappingEntries[0] & { extra: string }> = [ - { - field: 'field.one', - type: 'mapping', - value: 'field.one', - extra: 'blah', - }, - ]; - const decoded = threatMappingEntries.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation with a non string', () => { - const payload = [ - { - field: 5, - type: 'mapping', - value: 'field.one', - }, - ] as unknown as ThreatMappingEntries[]; - const decoded = threatMappingEntries.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation with a wrong type', () => { - const payload = [ - { - field: 'field.one', - type: 'invalid', - value: 'field.one', - }, - ] as unknown as ThreatMappingEntries[]; - const decoded = threatMappingEntries.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "invalid" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - }); - - describe('threat_mapping', () => { - test('it should validate a threat mapping', () => { - const payload: ThreatMapping = [ - { - entries: [ - { - field: 'field.one', - type: 'mapping', - value: 'field.one', - }, - ], - }, - ]; - const decoded = threat_mapping.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - }); - - test('it should fail validate with an extra key', () => { - const payload: Array<ThreatMapping[0] & { extra: string }> = [ - { - entries: [ - { - field: 'field.one', - type: 'mapping', - value: 'field.one', - }, - ], - extra: 'invalid', - }, - ]; - - const decoded = threat_mapping.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']); - expect(message.schema).toEqual({}); - }); - - test('it should fail validate with an extra inner entry', () => { - const payload: Array<ThreatMapping[0] & { entries: Array<{ extra: string }> }> = [ - { - entries: [ - { - field: 'field.one', - type: 'mapping', - value: 'field.one', - extra: 'blah', - }, - ], - }, - ]; - - const decoded = threat_mapping.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']); - expect(message.schema).toEqual({}); - }); - - test('it should fail validate with an extra inner entry with the wrong data type', () => { - const payload = [ - { - entries: [ - { - field: 5, - type: 'mapping', - value: 'field.one', - }, - ], - }, - ] as unknown as ThreatMapping; - - const decoded = threat_mapping.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "entries,field"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validate with empty array', () => { - const payload: string[] = []; - - const decoded = threat_mapping.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "[]" supplied to "NonEmptyArray<ThreatMap>"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when concurrent_searches is < 0', () => { - const payload = -1; - const decoded = concurrent_searches.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "-1" supplied to "PositiveIntegerGreaterThanZero"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when concurrent_searches is 0', () => { - const payload = 0; - const decoded = concurrent_searches.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "0" supplied to "PositiveIntegerGreaterThanZero"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when items_per_search is 0', () => { - const payload = 0; - const decoded = items_per_search.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "0" supplied to "PositiveIntegerGreaterThanZero"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when items_per_search is < 0', () => { - const payload = -1; - const decoded = items_per_search.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "-1" supplied to "PositiveIntegerGreaterThanZero"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts deleted file mode 100644 index 0cc8c950f754..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts +++ /dev/null @@ -1,79 +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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; -import { - NonEmptyArray, - NonEmptyString, - PositiveIntegerGreaterThanZero, -} from '@kbn/securitysolution-io-ts-types'; -import { language } from '../language'; - -export const threat_query = t.string; -export type ThreatQuery = t.TypeOf<typeof threat_query>; -export const threatQueryOrUndefined = t.union([threat_query, t.undefined]); -export type ThreatQueryOrUndefined = t.TypeOf<typeof threatQueryOrUndefined>; - -export const threat_indicator_path = t.string; -export type ThreatIndicatorPath = t.TypeOf<typeof threat_indicator_path>; -export const threatIndicatorPathOrUndefined = t.union([threat_indicator_path, t.undefined]); -export type ThreatIndicatorPathOrUndefined = t.TypeOf<typeof threatIndicatorPathOrUndefined>; - -export const threat_filters = t.array(t.unknown); // Filters are not easily type-able yet -export type ThreatFilters = t.TypeOf<typeof threat_filters>; -export const threatFiltersOrUndefined = t.union([threat_filters, t.undefined]); -export type ThreatFiltersOrUndefined = t.TypeOf<typeof threatFiltersOrUndefined>; - -export const threatMapEntry = t.exact( - t.type({ - field: NonEmptyString, - type: t.keyof({ mapping: null }), - value: NonEmptyString, - }) -); - -export type ThreatMapEntry = t.TypeOf<typeof threatMapEntry>; - -export const threatMappingEntries = t.array(threatMapEntry); -export type ThreatMappingEntries = t.TypeOf<typeof threatMappingEntries>; - -export const threatMap = t.exact( - t.type({ - entries: threatMappingEntries, - }) -); -export type ThreatMap = t.TypeOf<typeof threatMap>; - -export const threat_mapping = NonEmptyArray(threatMap, 'NonEmptyArray<ThreatMap>'); -export type ThreatMapping = t.TypeOf<typeof threat_mapping>; - -export const threatMappingOrUndefined = t.union([threat_mapping, t.undefined]); -export type ThreatMappingOrUndefined = t.TypeOf<typeof threatMappingOrUndefined>; - -export const threat_index = t.array(t.string); -export type ThreatIndex = t.TypeOf<typeof threat_index>; -export const threatIndexOrUndefined = t.union([threat_index, t.undefined]); -export type ThreatIndexOrUndefined = t.TypeOf<typeof threatIndexOrUndefined>; - -export const threat_language = t.union([language, t.undefined]); -export type ThreatLanguage = t.TypeOf<typeof threat_language>; -export const threatLanguageOrUndefined = t.union([threat_language, t.undefined]); -export type ThreatLanguageOrUndefined = t.TypeOf<typeof threatLanguageOrUndefined>; - -export const concurrent_searches = PositiveIntegerGreaterThanZero; -export type ConcurrentSearches = t.TypeOf<typeof concurrent_searches>; -export const concurrentSearchesOrUndefined = t.union([concurrent_searches, t.undefined]); -export type ConcurrentSearchesOrUndefined = t.TypeOf<typeof concurrentSearchesOrUndefined>; - -export const items_per_search = PositiveIntegerGreaterThanZero; -export type ItemsPerSearch = t.TypeOf<typeof concurrent_searches>; -export const itemsPerSearchOrUndefined = t.union([items_per_search, t.undefined]); -export type ItemsPerSearchOrUndefined = t.TypeOf<typeof itemsPerSearchOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts deleted file mode 100644 index 963bf76bf5f8..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts +++ /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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const threat_subtechnique_id = t.string; -export const threat_subtechnique_name = t.string; -export const threat_subtechnique_reference = t.string; - -export const threat_subtechnique = t.type({ - id: threat_subtechnique_id, - name: threat_subtechnique_name, - reference: threat_subtechnique_reference, -}); - -export const threat_subtechniques = t.array(threat_subtechnique); - -export type ThreatSubtechnique = t.TypeOf<typeof threat_subtechnique>; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_tactic/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_tactic/index.ts deleted file mode 100644 index a142788876e7..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_tactic/index.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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const threat_tactic_id = t.string; -export const threat_tactic_name = t.string; -export const threat_tactic_reference = t.string; - -export const threat_tactic = t.type({ - id: threat_tactic_id, - name: threat_tactic_name, - reference: threat_tactic_reference, -}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts deleted file mode 100644 index 2953ea86b3b3..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; -import { threat_subtechniques } from '../threat_subtechnique'; - -export const threat_technique_id = t.string; -export const threat_technique_name = t.string; -export const threat_technique_reference = t.string; - -export const threat_technique = t.intersection([ - t.exact( - t.type({ - id: threat_technique_id, - name: threat_technique_name, - reference: threat_technique_reference, - }) - ), - t.exact( - t.partial({ - subtechnique: threat_subtechniques, - }) - ), -]); -export const threat_techniques = t.array(threat_technique); - -export type ThreatTechnique = t.TypeOf<typeof threat_technique>; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts deleted file mode 100644 index f1d6bdca8fed..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.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", 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 * as t from 'io-ts'; -import { TimeDuration } from '@kbn/securitysolution-io-ts-types'; - -export type RuleActionThrottle = t.TypeOf<typeof RuleActionThrottle>; -export const RuleActionThrottle = t.union([ - t.literal('no_actions'), - t.literal('rule'), - TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }), -]); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.ts deleted file mode 100644 index 68c7a2f30a2b..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.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 * as t from 'io-ts'; - -export const type = t.keyof({ - eql: null, - machine_learning: null, - query: null, - saved_query: null, - threshold: null, - threat_match: null, - new_terms: null, - esql: null, -}); -export type Type = t.TypeOf<typeof type>; - -export const typeOrUndefined = t.union([type, t.undefined]); -export type TypeOrUndefined = t.TypeOf<typeof typeOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json b/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json deleted file mode 100644 index f9fb0b0fcebb..000000000000 --- a/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts" - ], - "kbn_references": [ - "@kbn/securitysolution-io-ts-types", - "@kbn/securitysolution-io-ts-utils" - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-io-ts-list-types/index.ts b/packages/kbn-securitysolution-io-ts-list-types/index.ts deleted file mode 100644 index 6d6b4e241b6d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './src/common'; -export * from './src/request'; -export * from './src/response'; -export * from './src/typescript_types'; diff --git a/packages/kbn-securitysolution-io-ts-list-types/jest.config.js b/packages/kbn-securitysolution-io-ts-list-types/jest.config.js deleted file mode 100644 index adcf48629dd7..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-io-ts-list-types'], -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/package.json b/packages/kbn-securitysolution-io-ts-list-types/package.json deleted file mode 100644 index 50c88fb22054..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@kbn/securitysolution-io-ts-list-types", - "version": "1.0.0", - "description": "io ts utilities and types to be shared with plugins from the security solution project", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "private": true, - "sideEffects": false -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.mock.ts deleted file mode 100644 index d824f23fe74b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.mock.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { Comment, CommentsArray } from '.'; -import { DATE_NOW, ID, USER } from '../../constants/index.mock'; - -export const getCommentsMock = (): Comment => ({ - comment: 'some old comment', - created_at: DATE_NOW, - created_by: USER, - id: ID, -}); - -export const getCommentsArrayMock = (): CommentsArray => [getCommentsMock(), getCommentsMock()]; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.test.ts deleted file mode 100644 index deca11f809cb..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.test.ts +++ /dev/null @@ -1,238 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getCommentsArrayMock, getCommentsMock } from './index.mock'; -import { - Comment, - comment, - CommentsArray, - commentsArray, - CommentsArrayOrUndefined, - commentsArrayOrUndefined, -} from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { DATE_NOW } from '../../constants/index.mock'; - -describe('Comment', () => { - describe('comment', () => { - test('it fails validation when "id" is undefined', () => { - const payload = { ...getCommentsMock(), id: undefined }; - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it passes validation with a typical comment', () => { - const payload = getCommentsMock(); - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it passes validation with "updated_at" and "updated_by" fields included', () => { - const payload = getCommentsMock(); - payload.updated_at = DATE_NOW; - payload.updated_by = 'someone'; - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it fails validation when undefined', () => { - const payload = undefined; - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it fails validation when "comment" is an empty string', () => { - const payload: Omit<Comment, 'comment'> & { comment: string } = { - ...getCommentsMock(), - comment: '', - }; - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "comment"']); - expect(message.schema).toEqual({}); - }); - - test('it fails validation when "comment" is not a string', () => { - const payload: Omit<Comment, 'comment'> & { comment: string[] } = { - ...getCommentsMock(), - comment: ['some value'], - }; - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "["some value"]" supplied to "comment"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it fails validation when "created_at" is not a string', () => { - const payload: Omit<Comment, 'created_at'> & { created_at: number } = { - ...getCommentsMock(), - created_at: 1, - }; - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "created_at"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it fails validation when "created_by" is not a string', () => { - const payload: Omit<Comment, 'created_by'> & { created_by: number } = { - ...getCommentsMock(), - created_by: 1, - }; - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "created_by"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it fails validation when "updated_at" is not a string', () => { - const payload: Omit<Comment, 'updated_at'> & { updated_at: number } = { - ...getCommentsMock(), - updated_at: 1, - }; - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "updated_at"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it fails validation when "updated_by" is not a string', () => { - const payload: Omit<Comment, 'updated_by'> & { updated_by: number } = { - ...getCommentsMock(), - updated_by: 1, - }; - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "updated_by"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: Comment & { - extraKey?: string; - } = getCommentsMock(); - payload.extraKey = 'some value'; - const decoded = comment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getCommentsMock()); - }); - }); - - describe('commentsArray', () => { - test('it passes validation an array of Comment', () => { - const payload = getCommentsArrayMock(); - const decoded = commentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it passes validation when a Comment includes "updated_at" and "updated_by"', () => { - const commentsPayload = getCommentsMock(); - commentsPayload.updated_at = DATE_NOW; - commentsPayload.updated_by = 'someone'; - const payload = [{ ...commentsPayload }, ...getCommentsArrayMock()]; - const decoded = commentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it fails validation when undefined', () => { - const payload = undefined; - const decoded = commentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it fails validation when array includes non Comment types', () => { - const payload = [1] as unknown as CommentsArray; - const decoded = commentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - ]); - expect(message.schema).toEqual({}); - }); - }); - - describe('commentsArrayOrUndefined', () => { - test('it passes validation an array of Comment', () => { - const payload = getCommentsArrayMock(); - const decoded = commentsArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it passes validation when undefined', () => { - const payload = undefined; - const decoded = commentsArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it fails validation when array includes non Comment types', () => { - const payload = [1] as unknown as CommentsArrayOrUndefined; - const decoded = commentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - ]); - expect(message.schema).toEqual({}); - }); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.ts deleted file mode 100644 index 3aac99e6c48e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.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 * as t from 'io-ts'; - -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; -import { created_at } from '../created_at'; -import { created_by } from '../created_by'; -import { id } from '../id'; -import { updated_at } from '../updated_at'; -import { updated_by } from '../updated_by'; - -export const comment = t.intersection([ - t.exact( - t.type({ - comment: NonEmptyString, - created_at, - created_by, - id, - }) - ), - t.exact( - t.partial({ - updated_at, - updated_by, - }) - ), -]); - -export const commentsArray = t.array(comment); -export type CommentsArray = t.TypeOf<typeof commentsArray>; -export type Comment = t.TypeOf<typeof comment>; -export const commentsArrayOrUndefined = t.union([commentsArray, t.undefined]); -export type CommentsArrayOrUndefined = t.TypeOf<typeof commentsArrayOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.mock.ts deleted file mode 100644 index bad427de9747..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.mock.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", 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 { CreateComment, CreateCommentsArray } from '.'; - -export const getCreateCommentsMock = (): CreateComment => ({ - comment: 'some comments', -}); - -export const getCreateCommentsArrayMock = (): CreateCommentsArray => [getCreateCommentsMock()]; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.test.ts deleted file mode 100644 index c537e9e28091..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.test.ts +++ /dev/null @@ -1,135 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getCreateCommentsArrayMock, getCreateCommentsMock } from './index.mock'; -import { - CreateComment, - createComment, - CreateCommentsArray, - createCommentsArray, - CreateCommentsArrayOrUndefined, - createCommentsArrayOrUndefined, -} from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('CreateComment', () => { - describe('createComment', () => { - test('it passes validation with a default comment', () => { - const payload = getCreateCommentsMock(); - const decoded = createComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it fails validation when undefined', () => { - const payload = undefined; - const decoded = createComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "{| comment: NonEmptyString |}"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it fails validation when "comment" is not a string', () => { - const payload: Omit<CreateComment, 'comment'> & { comment: string[] } = { - ...getCreateCommentsMock(), - comment: ['some value'], - }; - const decoded = createComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "["some value"]" supplied to "comment"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: CreateComment & { - extraKey?: string; - } = getCreateCommentsMock(); - payload.extraKey = 'some value'; - const decoded = createComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getCreateCommentsMock()); - }); - }); - - describe('createCommentsArray', () => { - test('it passes validation an array of comments', () => { - const payload = getCreateCommentsArrayMock(); - const decoded = createCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it fails validation when undefined', () => { - const payload = undefined; - const decoded = createCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "Array<{| comment: NonEmptyString |}>"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it fails validation when array includes non comments types', () => { - const payload = [1] as unknown as CreateCommentsArray; - const decoded = createCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"', - ]); - expect(message.schema).toEqual({}); - }); - }); - - describe('createCommentsArrayOrUndefined', () => { - test('it passes validation an array of comments', () => { - const payload = getCreateCommentsArrayMock(); - const decoded = createCommentsArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it passes validation when undefined', () => { - const payload = undefined; - const decoded = createCommentsArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it fails validation when array includes non comments types', () => { - const payload = [1] as unknown as CreateCommentsArrayOrUndefined; - const decoded = createCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"', - ]); - expect(message.schema).toEqual({}); - }); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.ts deleted file mode 100644 index f4082df289ac..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.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 * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; - -export const createComment = t.exact( - t.type({ - comment: NonEmptyString, - }) -); - -export type CreateComment = t.TypeOf<typeof createComment>; -export const createCommentsArray = t.array(createComment); -export type CreateCommentsArray = t.TypeOf<typeof createCommentsArray>; -export type CreateComments = t.TypeOf<typeof createComment>; -export const createCommentsArrayOrUndefined = t.union([createCommentsArray, t.undefined]); -export type CreateCommentsArrayOrUndefined = t.TypeOf<typeof createCommentsArrayOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/created_at/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/created_at/index.ts deleted file mode 100644 index 697a5034cbf0..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/created_at/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const created_at = t.string; // TODO: Make this into an ISO Date string check diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/created_by/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/created_by/index.ts deleted file mode 100644 index e6f3e2f90edb..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/created_by/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const created_by = t.string; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/cursor/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/cursor/index.ts deleted file mode 100644 index 4844ffe7e364..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/cursor/index.ts +++ /dev/null @@ -1,15 +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 * as t from 'io-ts'; - -export const cursor = t.string; -export type Cursor = t.TypeOf<typeof cursor>; -export const cursorOrUndefined = t.union([cursor, t.undefined]); -export type CursorOrUndefined = t.TypeOf<typeof cursorOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.test.ts deleted file mode 100644 index d0c7e3cc0447..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.test.ts +++ /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", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { CommentsArray } from '../comment'; -import { DefaultCommentsArray } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getCommentsArrayMock } from '../comment/index.mock'; - -describe('default_comments_array', () => { - test('it should pass validation when supplied an empty array', () => { - const payload: CommentsArray = []; - const decoded = DefaultCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should pass validation when supplied an array of comments', () => { - const payload: CommentsArray = getCommentsArrayMock(); - const decoded = DefaultCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation when supplied an array of numbers', () => { - const payload = [1]; - const decoded = DefaultCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an array of strings', () => { - const payload = ['some string']; - const decoded = DefaultCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default array entry', () => { - const payload = null; - const decoded = DefaultCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([]); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.ts deleted file mode 100644 index 6a89b0063351..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { comment, CommentsArray } from '../comment'; - -/** - * Types the DefaultCommentsArray as: - * - If null or undefined, then a default array of type entry will be set - */ -export const DefaultCommentsArray = new t.Type<CommentsArray, CommentsArray, unknown>( - 'DefaultCommentsArray', - t.array(comment).is, - (input): Either<t.Errors, CommentsArray> => - input == null ? t.success([]) : t.array(comment).decode(input), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.test.ts deleted file mode 100644 index ce909c0314b3..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.test.ts +++ /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", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { CommentsArray } from '../comment'; -import { DefaultCommentsArray } from '../default_comments_array'; -import { getCommentsArrayMock } from '../comment/index.mock'; - -describe('default_comments_array', () => { - test('it should pass validation when supplied an empty array', () => { - const payload: CommentsArray = []; - const decoded = DefaultCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should pass validation when supplied an array of comments', () => { - const payload: CommentsArray = getCommentsArrayMock(); - const decoded = DefaultCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation when supplied an array of numbers', () => { - const payload = [1]; - const decoded = DefaultCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an array of strings', () => { - const payload = ['some string']; - const decoded = DefaultCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default array entry', () => { - const payload = null; - const decoded = DefaultCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([]); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.ts deleted file mode 100644 index 0ed345891719..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { createComment, CreateCommentsArray } from '../create_comment'; - -/** - * Types the DefaultCreateComments as: - * - If null or undefined, then a default array of type entry will be set - */ -export const DefaultCreateCommentsArray = new t.Type< - CreateCommentsArray, - CreateCommentsArray, - unknown ->( - 'DefaultCreateComments', - t.array(createComment).is, - (input): Either<t.Errors, CreateCommentsArray> => - input == null ? t.success([]) : t.array(createComment).decode(input), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.test.ts deleted file mode 100644 index 9b6f8df6850c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.test.ts +++ /dev/null @@ -1,88 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { ImportCommentsArray } from '../import_comment'; -import { DefaultImportCommentsArray } from '.'; -import { getCommentsArrayMock } from '../comment/index.mock'; -import { getCreateCommentsArrayMock } from '../create_comment/index.mock'; - -describe('default_import_comments_array', () => { - test('it should pass validation when supplied an empty array', () => { - const payload: ImportCommentsArray = []; - const decoded = DefaultImportCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should pass validation when supplied an array of comments', () => { - const payload: ImportCommentsArray = getCommentsArrayMock(); - const decoded = DefaultImportCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should pass validation when supplied an array of new comments', () => { - const payload: ImportCommentsArray = getCreateCommentsArrayMock(); - const decoded = DefaultImportCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should pass validation when supplied an array of new and existing comments', () => { - const payload: ImportCommentsArray = [ - ...getCommentsArrayMock(), - ...getCreateCommentsArrayMock(), - ]; - const decoded = DefaultImportCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation when supplied an array of numbers', () => { - const payload = [1]; - const decoded = DefaultImportCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "DefaultImportComments"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an array of strings', () => { - const payload = ['some string']; - const decoded = DefaultImportCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "some string" supplied to "DefaultImportComments"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default array entry', () => { - const payload = null; - const decoded = DefaultImportCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([]); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.ts deleted file mode 100644 index dc8d18e7e11e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { importComment, ImportCommentsArray } from '../import_comment'; - -/** - * Types the DefaultImportCommentsArray as: - * - If null or undefined, then a default array of type ImportCommentsArray will be set - */ -export const DefaultImportCommentsArray = new t.Type< - ImportCommentsArray, - ImportCommentsArray, - unknown ->( - 'DefaultImportComments', - t.array(importComment).is, - (input, context): Either<t.Errors, ImportCommentsArray> => - input == null ? t.success([]) : t.array(importComment).validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.test.ts deleted file mode 100644 index 69cdd841c0a5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.test.ts +++ /dev/null @@ -1,62 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultNamespace } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_namespace', () => { - test('it should validate "single"', () => { - const payload = 'single'; - const decoded = DefaultNamespace.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate "agnostic"', () => { - const payload = 'agnostic'; - const decoded = DefaultNamespace.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it defaults to "single" if "undefined"', () => { - const payload = undefined; - const decoded = DefaultNamespace.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual('single'); - }); - - test('it defaults to "single" if "null"', () => { - const payload = null; - const decoded = DefaultNamespace.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual('single'); - }); - - test('it should FAIL validation if not "single" or "agnostic"', () => { - const payload = 'something else'; - const decoded = DefaultNamespace.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - `Invalid value "something else" supplied to "DefaultNamespace"`, - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.ts deleted file mode 100644 index 33f53c55b198..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.ts +++ /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", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; - -export const namespaceType = t.keyof({ agnostic: null, single: null }); -export type NamespaceType = t.TypeOf<typeof namespaceType>; - -/** - * Types the DefaultNamespace as: - * - If null or undefined, then a default string/enumeration of "single" will be used. - */ -export const DefaultNamespace = new t.Type<NamespaceType, NamespaceType | undefined, unknown>( - 'DefaultNamespace', - namespaceType.is, - (input, context): Either<t.Errors, NamespaceType> => - input == null ? t.success('single') : namespaceType.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.test.ts deleted file mode 100644 index 359674e57041..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.test.ts +++ /dev/null @@ -1,100 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultNamespaceArray, DefaultNamespaceArrayType } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_namespace_array', () => { - test('it should validate "null" single item as an array with a "single" value', () => { - const payload: DefaultNamespaceArrayType = null; - const decoded = DefaultNamespaceArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(['single']); - }); - - test('it should FAIL validation of numeric value', () => { - const payload = 5; - const decoded = DefaultNamespaceArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "5" supplied to "DefaultNamespaceArray"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should validate "undefined" item as an array with a "single" value', () => { - const payload: DefaultNamespaceArrayType = undefined; - const decoded = DefaultNamespaceArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(['single']); - }); - - test('it should validate "single" as an array of a "single" value', () => { - const payload: DefaultNamespaceArrayType = 'single'; - const decoded = DefaultNamespaceArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([payload]); - }); - - test('it should validate "agnostic" as an array of a "agnostic" value', () => { - const payload: DefaultNamespaceArrayType = 'agnostic'; - const decoded = DefaultNamespaceArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([payload]); - }); - - test('it should validate "single,agnostic" as an array of 2 values of ["single", "agnostic"] values', () => { - const payload: DefaultNamespaceArrayType = 'agnostic,single'; - const decoded = DefaultNamespaceArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(['agnostic', 'single']); - }); - - test('it should validate 3 elements of "single,agnostic,single" as an array of 3 values of ["single", "agnostic", "single"] values', () => { - const payload: DefaultNamespaceArrayType = 'single,agnostic,single'; - const decoded = DefaultNamespaceArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(['single', 'agnostic', 'single']); - }); - - test('it should validate 3 elements of "single,agnostic, single" as an array of 3 values of ["single", "agnostic", "single"] values when there are spaces', () => { - const payload: DefaultNamespaceArrayType = ' single, agnostic, single '; - const decoded = DefaultNamespaceArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(['single', 'agnostic', 'single']); - }); - - test('it should FAIL validation when given 3 elements of "single,agnostic,junk" since the 3rd value is junk', () => { - const payload: DefaultNamespaceArrayType = 'single,agnostic,junk'; - const decoded = DefaultNamespaceArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "junk" supplied to "DefaultNamespaceArray"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.ts deleted file mode 100644 index 9692b8a2783d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { namespaceType } from '../default_namespace'; - -export const namespaceTypeArray = t.array(namespaceType); -export type NamespaceTypeArray = t.TypeOf<typeof namespaceTypeArray>; - -/** - * Types the DefaultNamespaceArray as: - * - If null or undefined, then a default string array of "single" will be used. - * - If it contains a string, then it is split along the commas and puts them into an array and validates it - */ -export const DefaultNamespaceArray = new t.Type< - NamespaceTypeArray, - string | undefined | null, - unknown ->( - 'DefaultNamespaceArray', - namespaceTypeArray.is, - (input, context): Either<t.Errors, NamespaceTypeArray> => { - if (input == null) { - return t.success(['single']); - } else if (typeof input === 'string') { - const commaSeparatedValues = input - .trim() - .split(',') - .map((value) => value.trim()); - return namespaceTypeArray.validate(commaSeparatedValues, context); - } - return t.failure(input, context); - }, - String -); - -export type DefaultNamespaceArrayType = t.OutputOf<typeof DefaultNamespaceArray>; -export type DefaultNamespaceArrayTypeDecoded = t.TypeOf<typeof DefaultNamespaceArray>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.test.ts deleted file mode 100644 index aaffae53ceca..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.test.ts +++ /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", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { UpdateCommentsArray } from '../update_comment'; -import { DefaultUpdateCommentsArray } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getUpdateCommentsArrayMock } from '../update_comment/index.mock'; - -describe('default_update_comments_array', () => { - test('it should pass validation when supplied an empty array', () => { - const payload: UpdateCommentsArray = []; - const decoded = DefaultUpdateCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should pass validation when supplied an array of comments', () => { - const payload: UpdateCommentsArray = getUpdateCommentsArrayMock(); - const decoded = DefaultUpdateCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation when supplied an array of numbers', () => { - const payload = [1]; - const decoded = DefaultUpdateCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "DefaultUpdateComments"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an array of strings', () => { - const payload = ['some string']; - const decoded = DefaultUpdateCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "some string" supplied to "DefaultUpdateComments"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should return a default array entry', () => { - const payload = null; - const decoded = DefaultUpdateCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([]); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.ts deleted file mode 100644 index 47c0e562133d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { updateCommentsArray, UpdateCommentsArray } from '../update_comment'; - -/** - * Types the DefaultUpdateComments as: - * - If null or undefined, then a default array of type UpdateCommentsArray will be set - */ -export const DefaultUpdateCommentsArray = new t.Type< - UpdateCommentsArray, - UpdateCommentsArray, - unknown ->( - 'DefaultUpdateComments', - updateCommentsArray.is, - (input, context): Either<t.Errors, UpdateCommentsArray> => - input == null ? t.success([]) : updateCommentsArray.validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/description/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/description/index.ts deleted file mode 100644 index 75fd7ac92ffc..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/description/index.ts +++ /dev/null @@ -1,15 +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 * as t from 'io-ts'; - -export const description = t.string; -export type Description = t.TypeOf<typeof description>; -export const descriptionOrUndefined = t.union([description, t.undefined]); -export type DescriptionOrUndefined = t.TypeOf<typeof descriptionOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/deserializer/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/deserializer/index.ts deleted file mode 100644 index c5489a946897..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/deserializer/index.ts +++ /dev/null @@ -1,15 +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 * as t from 'io-ts'; - -export const deserializer = t.string; -export type Deserializer = t.TypeOf<typeof deserializer>; -export const deserializerOrUndefined = t.union([deserializer, t.undefined]); -export type DeserializerOrUndefined = t.TypeOf<typeof deserializerOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.mock.ts deleted file mode 100644 index bc091b1e33c3..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.mock.ts +++ /dev/null @@ -1,21 +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 { EndpointEntriesArray } from '.'; -import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; -import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; -import { getEndpointEntryNestedMock } from '../entry_nested/index.mock'; -import { getEndpointEntryMatchWildcardMock } from '../entry_match_wildcard/index.mock'; - -export const getEndpointEntriesArrayMock = (): EndpointEntriesArray => [ - getEndpointEntryMatchMock(), - getEndpointEntryMatchAnyMock(), - getEndpointEntryNestedMock(), - getEndpointEntryMatchWildcardMock(), -]; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.test.ts deleted file mode 100644 index 574bf4276461..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.test.ts +++ /dev/null @@ -1,122 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; -import { - endpointEntriesArray, - nonEmptyEndpointEntriesArray, - NonEmptyEndpointEntriesArray, -} from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; -import { getEndpointEntryNestedMock } from '../entry_nested/index.mock'; -import { getEndpointEntriesArrayMock } from './index.mock'; -import { getEntryListMock } from '../../entries_list/index.mock'; -import { getEntryExistsMock } from '../../entries_exist/index.mock'; -import { getEndpointEntryMatchWildcardMock } from '../entry_match_wildcard/index.mock'; - -describe('Endpoint', () => { - describe('entriesArray', () => { - test('it should validate an array with match entry', () => { - const payload = [getEndpointEntryMatchMock()]; - const decoded = endpointEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array with match_any entry', () => { - const payload = [getEndpointEntryMatchAnyMock()]; - const decoded = endpointEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT validate an empty array', () => { - const payload: NonEmptyEndpointEntriesArray = []; - const decoded = nonEmptyEndpointEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "[]" supplied to "NonEmptyEndpointEntriesArray"', - ]); - expect(message.schema).toEqual({}); - }); - - test('type guard for nonEmptyEndpointNestedEntries should allow array of endpoint entries', () => { - const payload: NonEmptyEndpointEntriesArray = [getEndpointEntryMatchAnyMock()]; - const guarded = nonEmptyEndpointEntriesArray.is(payload); - expect(guarded).toBeTruthy(); - }); - - test('type guard for nonEmptyEndpointNestedEntries should disallow empty arrays', () => { - const payload: NonEmptyEndpointEntriesArray = []; - const guarded = nonEmptyEndpointEntriesArray.is(payload); - expect(guarded).toBeFalsy(); - }); - - test('it should NOT validate an array with exists entry', () => { - const payload = [getEntryExistsMock()]; - const decoded = endpointEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "exists" supplied to "type"', - 'Invalid value "undefined" supplied to "value"', - 'Invalid value "undefined" supplied to "entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate an array with list entry', () => { - const payload = [getEntryListMock()]; - const decoded = endpointEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "list" supplied to "type"', - 'Invalid value "undefined" supplied to "value"', - 'Invalid value "undefined" supplied to "entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should validate an array with nested entry', () => { - const payload = [getEndpointEntryNestedMock()]; - const decoded = endpointEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array with wildcard entry', () => { - const payload = [getEndpointEntryMatchWildcardMock()]; - const decoded = endpointEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array with all types of entries', () => { - const payload = getEndpointEntriesArrayMock(); - const decoded = endpointEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.ts deleted file mode 100644 index 992237161d06..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { endpointEntryMatch } from '../entry_match'; -import { endpointEntryMatchAny } from '../entry_match_any'; -import { endpointEntryNested } from '../entry_nested'; -import { endpointEntryMatchWildcard } from '../entry_match_wildcard'; - -export const endpointEntriesArray = t.array( - t.union([ - endpointEntryMatch, - endpointEntryMatchAny, - endpointEntryMatchWildcard, - endpointEntryNested, - ]) -); -export type EndpointEntriesArray = t.TypeOf<typeof endpointEntriesArray>; - -/** - * Types the nonEmptyEndpointEntriesArray as: - * - An array of entries of length 1 or greater - * - */ -export const nonEmptyEndpointEntriesArray = new t.Type< - EndpointEntriesArray, - EndpointEntriesArray, - unknown ->( - 'NonEmptyEndpointEntriesArray', - (u: unknown): u is EndpointEntriesArray => endpointEntriesArray.is(u) && u.length > 0, - (input, context): Either<t.Errors, EndpointEntriesArray> => { - if (Array.isArray(input) && input.length === 0) { - return t.failure(input, context); - } else { - return endpointEntriesArray.validate(input, context); - } - }, - t.identity -); - -export type NonEmptyEndpointEntriesArray = t.OutputOf<typeof nonEmptyEndpointEntriesArray>; -export type NonEmptyEndpointEntriesArrayDecoded = t.TypeOf<typeof nonEmptyEndpointEntriesArray>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.mock.ts deleted file mode 100644 index 76305cf70c58..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.mock.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", 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 { EndpointEntryMatch } from '.'; -import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../../../constants/index.mock'; - -export const getEndpointEntryMatchMock = (): EndpointEntryMatch => ({ - field: FIELD, - operator: OPERATOR, - type: MATCH, - value: ENTRY_VALUE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.test.ts deleted file mode 100644 index 5c20b697b3bf..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.test.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", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEndpointEntryMatchMock } from './index.mock'; -import { EndpointEntryMatch, endpointEntryMatch } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getEntryMatchMock } from '../../entry_match/index.mock'; - -describe('endpointEntryMatch', () => { - test('it should validate an entry', () => { - const payload = getEndpointEntryMatchMock(); - const decoded = endpointEntryMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT validate when "operator" is "excluded"', () => { - // Use the generic entry mock so we can test operator: excluded - const payload = getEntryMatchMock(); - payload.operator = 'excluded'; - const decoded = endpointEntryMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "excluded" supplied to "operator"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "field" is empty string', () => { - const payload: Omit<EndpointEntryMatch, 'field'> & { field: string } = { - ...getEndpointEntryMatchMock(), - field: '', - }; - const decoded = endpointEntryMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "value" is not string', () => { - const payload: Omit<EndpointEntryMatch, 'value'> & { value: string[] } = { - ...getEndpointEntryMatchMock(), - value: ['some value'], - }; - const decoded = endpointEntryMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "["some value"]" supplied to "value"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "value" is empty string', () => { - const payload: Omit<EndpointEntryMatch, 'value'> & { value: string } = { - ...getEndpointEntryMatchMock(), - value: '', - }; - const decoded = endpointEntryMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "type" is not "match"', () => { - const payload: Omit<EndpointEntryMatch, 'type'> & { type: string } = { - ...getEndpointEntryMatchMock(), - type: 'match_any', - }; - const decoded = endpointEntryMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "match_any" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: EndpointEntryMatch & { - extraKey?: string; - } = getEndpointEntryMatchMock(); - payload.extraKey = 'some value'; - const decoded = endpointEntryMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getEntryMatchMock()); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.ts deleted file mode 100644 index 5ae0258ebfb7..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.ts +++ /dev/null @@ -1,21 +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 * as t from 'io-ts'; -import { NonEmptyString, operatorIncluded } from '@kbn/securitysolution-io-ts-types'; - -export const endpointEntryMatch = t.exact( - t.type({ - field: NonEmptyString, - operator: operatorIncluded, - type: t.keyof({ match: null }), - value: NonEmptyString, - }) -); -export type EndpointEntryMatch = t.TypeOf<typeof endpointEntryMatch>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.mock.ts deleted file mode 100644 index b3bfffd4daf9..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.mock.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", 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 { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../../../constants/index.mock'; -import { EndpointEntryMatchAny } from '.'; - -export const getEndpointEntryMatchAnyMock = (): EndpointEntryMatchAny => ({ - field: FIELD, - operator: OPERATOR, - type: MATCH_ANY, - value: [ENTRY_VALUE], -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.test.ts deleted file mode 100644 index e121eed769ea..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.test.ts +++ /dev/null @@ -1,101 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEndpointEntryMatchAnyMock } from './index.mock'; -import { EndpointEntryMatchAny, endpointEntryMatchAny } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getEntryMatchAnyMock } from '../../entry_match_any/index.mock'; - -describe('endpointEntryMatchAny', () => { - test('it should validate an entry', () => { - const payload = getEndpointEntryMatchAnyMock(); - const decoded = endpointEntryMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT validate when operator is "excluded"', () => { - // Use the generic entry mock so we can test operator: excluded - const payload = getEntryMatchAnyMock(); - payload.operator = 'excluded'; - const decoded = endpointEntryMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "excluded" supplied to "operator"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when field is empty string', () => { - const payload: Omit<EndpointEntryMatchAny, 'field'> & { field: string } = { - ...getEndpointEntryMatchAnyMock(), - field: '', - }; - const decoded = endpointEntryMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when value is empty array', () => { - const payload: Omit<EndpointEntryMatchAny, 'value'> & { value: string[] } = { - ...getEndpointEntryMatchAnyMock(), - value: [], - }; - const decoded = endpointEntryMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "[]" supplied to "value"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when value is not string array', () => { - const payload: Omit<EndpointEntryMatchAny, 'value'> & { value: string } = { - ...getEndpointEntryMatchAnyMock(), - value: 'some string', - }; - const decoded = endpointEntryMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "some string" supplied to "value"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "type" is not "match_any"', () => { - const payload: Omit<EndpointEntryMatchAny, 'type'> & { type: string } = { - ...getEndpointEntryMatchAnyMock(), - type: 'match', - }; - const decoded = endpointEntryMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: EndpointEntryMatchAny & { - extraKey?: string; - } = getEndpointEntryMatchAnyMock(); - payload.extraKey = 'some extra key'; - const decoded = endpointEntryMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getEntryMatchAnyMock()); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.ts deleted file mode 100644 index d639f4481d4d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.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 * as t from 'io-ts'; -import { - NonEmptyString, - nonEmptyOrNullableStringArray, - operatorIncluded, -} from '@kbn/securitysolution-io-ts-types'; - -export const endpointEntryMatchAny = t.exact( - t.type({ - field: NonEmptyString, - operator: operatorIncluded, - type: t.keyof({ match_any: null }), - value: nonEmptyOrNullableStringArray, - }) -); -export type EndpointEntryMatchAny = t.TypeOf<typeof endpointEntryMatchAny>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.mock.ts deleted file mode 100644 index f1b5c7e7421a..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.mock.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", 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 { ENTRY_VALUE, FIELD, OPERATOR, WILDCARD } from '../../../constants/index.mock'; -import { EndpointEntryMatchWildcard } from '.'; - -export const getEndpointEntryMatchWildcardMock = (): EndpointEntryMatchWildcard => ({ - field: FIELD, - operator: OPERATOR, - type: WILDCARD, - value: ENTRY_VALUE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.test.ts deleted file mode 100644 index 025994afcd9e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.test.ts +++ /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", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEndpointEntryMatchWildcardMock } from './index.mock'; -import { EndpointEntryMatchWildcard, endpointEntryMatchWildcard } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getEntryMatchWildcardMock } from '../../entry_match_wildcard/index.mock'; - -describe('endpointEntryMatchWildcard', () => { - test('it should validate an entry', () => { - const payload = getEndpointEntryMatchWildcardMock(); - const decoded = endpointEntryMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when "operator" is "excluded"', () => { - const payload = getEntryMatchWildcardMock(); - payload.operator = 'excluded'; - const decoded = endpointEntryMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when "field" is empty string', () => { - const payload: Omit<EndpointEntryMatchWildcard, 'field'> & { field: string } = { - ...getEndpointEntryMatchWildcardMock(), - field: '', - }; - const decoded = endpointEntryMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "value" is not string', () => { - const payload: Omit<EndpointEntryMatchWildcard, 'value'> & { value: string[] } = { - ...getEndpointEntryMatchWildcardMock(), - value: ['some value'], - }; - const decoded = endpointEntryMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "["some value"]" supplied to "value"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "value" is empty string', () => { - const payload: Omit<EndpointEntryMatchWildcard, 'value'> & { value: string } = { - ...getEndpointEntryMatchWildcardMock(), - value: '', - }; - const decoded = endpointEntryMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "type" is not "wildcard"', () => { - const payload: Omit<EndpointEntryMatchWildcard, 'type'> & { type: string } = { - ...getEndpointEntryMatchWildcardMock(), - type: 'match', - }; - const decoded = endpointEntryMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: EndpointEntryMatchWildcard & { - extraKey?: string; - } = getEndpointEntryMatchWildcardMock(); - payload.extraKey = 'some value'; - const decoded = endpointEntryMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getEntryMatchWildcardMock()); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.ts deleted file mode 100644 index b42f6245ea28..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.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 * as t from 'io-ts'; -import { - NonEmptyString, - operatorExcluded, - operatorIncluded, -} from '@kbn/securitysolution-io-ts-types'; - -export const endpointEntryMatchWildcard = t.exact( - t.type({ - field: NonEmptyString, - operator: t.union([operatorIncluded, operatorExcluded]), - type: t.keyof({ wildcard: null }), - value: NonEmptyString, - }) -); -export type EndpointEntryMatchWildcard = t.TypeOf<typeof endpointEntryMatchWildcard>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.mock.ts deleted file mode 100644 index 44c5f8db5f6e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.mock.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { EndpointEntryNested } from '.'; -import { FIELD, NESTED } from '../../../constants/index.mock'; -import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; -import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; - -export const getEndpointEntryNestedMock = (): EndpointEntryNested => ({ - entries: [getEndpointEntryMatchMock(), getEndpointEntryMatchAnyMock()], - field: FIELD, - type: NESTED, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.test.ts deleted file mode 100644 index c062deb88af8..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.test.ts +++ /dev/null @@ -1,138 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { EndpointEntryNested, endpointEntryNested } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getEndpointEntryNestedMock } from './index.mock'; -import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; -import { - nonEmptyEndpointNestedEntriesArray, - NonEmptyEndpointNestedEntriesArray, -} from '../non_empty_nested_entries_array'; -import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; -import { getEntryExistsMock } from '../../entries_exist/index.mock'; - -describe('endpointEntryNested', () => { - test('it should validate a nested entry', () => { - const payload = getEndpointEntryNestedMock(); - const decoded = endpointEntryNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when "type" is not "nested"', () => { - const payload: Omit<EndpointEntryNested, 'type'> & { type: 'match' } = { - ...getEndpointEntryNestedMock(), - type: 'match', - }; - const decoded = endpointEntryNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "field" is empty string', () => { - const payload: Omit<EndpointEntryNested, 'field'> & { - field: string; - } = { ...getEndpointEntryNestedMock(), field: '' }; - const decoded = endpointEntryNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "field" is not a string', () => { - const payload: Omit<EndpointEntryNested, 'field'> & { - field: number; - } = { ...getEndpointEntryNestedMock(), field: 1 }; - const decoded = endpointEntryNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "entries" is not an array', () => { - const payload: Omit<EndpointEntryNested, 'entries'> & { - entries: string; - } = { ...getEndpointEntryNestedMock(), entries: 'im a string' }; - const decoded = endpointEntryNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "im a string" supplied to "entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should validate when "entries" contains an entry item that is type "match"', () => { - const payload = { ...getEndpointEntryNestedMock(), entries: [getEndpointEntryMatchAnyMock()] }; - const decoded = endpointEntryNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual({ - entries: [ - { - field: 'host.name', - operator: 'included', - type: 'match_any', - value: ['some host name'], - }, - ], - field: 'host.name', - type: 'nested', - }); - }); - - test('it should NOT validate when "entries" contains an entry item that is type "exists"', () => { - const payload = { ...getEndpointEntryNestedMock(), entries: [getEntryExistsMock()] }; - const decoded = endpointEntryNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "exists" supplied to "entries,type"', - 'Invalid value "undefined" supplied to "entries,value"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: EndpointEntryNested & { - extraKey?: string; - } = getEndpointEntryNestedMock(); - payload.extraKey = 'some extra key'; - const decoded = endpointEntryNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getEndpointEntryNestedMock()); - }); - - test('type guard for nonEmptyEndpointNestedEntries should allow array of endpoint entries', () => { - const payload: NonEmptyEndpointNestedEntriesArray = [ - getEndpointEntryMatchMock(), - getEndpointEntryMatchAnyMock(), - ]; - const guarded = nonEmptyEndpointNestedEntriesArray.is(payload); - expect(guarded).toBeTruthy(); - }); - - test('type guard for nonEmptyEndpointNestedEntries should disallow empty arrays', () => { - const payload: NonEmptyEndpointNestedEntriesArray = []; - const guarded = nonEmptyEndpointNestedEntriesArray.is(payload); - expect(guarded).toBeFalsy(); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.ts deleted file mode 100644 index 6625c1dfeaf5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.ts +++ /dev/null @@ -1,21 +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 * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; -import { nonEmptyEndpointNestedEntriesArray } from '../non_empty_nested_entries_array'; - -export const endpointEntryNested = t.exact( - t.type({ - entries: nonEmptyEndpointNestedEntriesArray, - field: NonEmptyString, - type: t.keyof({ nested: null }), - }) -); -export type EndpointEntryNested = t.TypeOf<typeof endpointEntryNested>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/index.ts deleted file mode 100644 index fb42cc8ee468..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/index.ts +++ /dev/null @@ -1,11 +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". - */ - -export * from './entries'; -export * from './non_empty_nested_entries_array'; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/non_empty_nested_entries_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/non_empty_nested_entries_array/index.ts deleted file mode 100644 index 9bcfccae97b3..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/non_empty_nested_entries_array/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { endpointEntryMatch } from '../entry_match'; -import { endpointEntryMatchAny } from '../entry_match_any'; - -export const endpointNestedEntriesArray = t.array( - t.union([endpointEntryMatch, endpointEntryMatchAny]) -); -export type EndpointNestedEntriesArray = t.TypeOf<typeof endpointNestedEntriesArray>; - -/** - * Types the nonEmptyNestedEntriesArray as: - * - An array of entries of length 1 or greater - * - */ -export const nonEmptyEndpointNestedEntriesArray = new t.Type< - EndpointNestedEntriesArray, - EndpointNestedEntriesArray, - unknown ->( - 'NonEmptyEndpointNestedEntriesArray', - (u: unknown): u is EndpointNestedEntriesArray => endpointNestedEntriesArray.is(u) && u.length > 0, - (input, context): Either<t.Errors, EndpointNestedEntriesArray> => { - if (Array.isArray(input) && input.length === 0) { - return t.failure(input, context); - } else { - return endpointNestedEntriesArray.validate(input, context); - } - }, - t.identity -); - -export type NonEmptyEndpointNestedEntriesArray = t.OutputOf< - typeof nonEmptyEndpointNestedEntriesArray ->; -export type NonEmptyEndpointNestedEntriesArrayDecoded = t.TypeOf< - typeof nonEmptyEndpointNestedEntriesArray ->; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.mock.ts deleted file mode 100644 index 5b8c802c94a3..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.mock.ts +++ /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 { EntriesArray } from '.'; -import { getEntryExistsMock } from '../entries_exist/index.mock'; -import { getEntryListMock } from '../entries_list/index.mock'; -import { getEntryMatchMock } from '../entry_match/index.mock'; -import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; -import { getEntryNestedMock } from '../entry_nested/index.mock'; - -export const getListAndNonListEntriesArrayMock = (): EntriesArray => [ - getEntryMatchMock(), - getEntryMatchAnyMock(), - getEntryListMock(), - getEntryExistsMock(), - getEntryNestedMock(), -]; - -export const getListEntriesArrayMock = (): EntriesArray => [getEntryListMock(), getEntryListMock()]; - -export const getEntriesArrayMock = (): EntriesArray => [ - getEntryMatchMock(), - getEntryMatchAnyMock(), - getEntryExistsMock(), - getEntryNestedMock(), -]; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.test.ts deleted file mode 100644 index 73553be78b9a..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.test.ts +++ /dev/null @@ -1,149 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEntryMatchMock } from '../entry_match/index.mock'; -import { entriesArray, entriesArrayOrUndefined, entry } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; -import { getEntryExistsMock } from '../entries_exist/index.mock'; -import { getEntryListMock } from '../entries_list/index.mock'; -import { getEntryNestedMock } from '../entry_nested/index.mock'; -import { getEntriesArrayMock } from './index.mock'; - -describe('Entries', () => { - describe('entry', () => { - test('it should validate a match entry', () => { - const payload = getEntryMatchMock(); - const decoded = entry.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate a match_any entry', () => { - const payload = getEntryMatchAnyMock(); - const decoded = entry.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate a exists entry', () => { - const payload = getEntryExistsMock(); - const decoded = entry.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate a list entry', () => { - const payload = getEntryListMock(); - const decoded = entry.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation of nested entry', () => { - const payload = getEntryNestedMock(); - const decoded = entry.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "operator"', - 'Invalid value "nested" supplied to "type"', - 'Invalid value "undefined" supplied to "value"', - 'Invalid value "undefined" supplied to "list"', - ]); - expect(message.schema).toEqual({}); - }); - }); - - describe('entriesArray', () => { - test('it should validate an array with match entry', () => { - const payload = [getEntryMatchMock()]; - const decoded = entriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array with match_any entry', () => { - const payload = [getEntryMatchAnyMock()]; - const decoded = entriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array with exists entry', () => { - const payload = [getEntryExistsMock()]; - const decoded = entriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array with list entry', () => { - const payload = [getEntryListMock()]; - const decoded = entriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array with nested entry', () => { - const payload = [getEntryNestedMock()]; - const decoded = entriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array with all types of entries', () => { - const payload = [...getEntriesArrayMock()]; - const decoded = entriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - }); - - describe('entriesArrayOrUndefined', () => { - test('it should validate undefined', () => { - const payload = undefined; - const decoded = entriesArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array with nested entry', () => { - const payload = [getEntryNestedMock()]; - const decoded = entriesArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.ts deleted file mode 100644 index f2dbbd91dfce..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.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", 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 * as t from 'io-ts'; -import { entriesExists } from '../entries_exist'; -import { entriesList } from '../entries_list'; -import { entriesMatch } from '../entry_match'; -import { entriesMatchAny } from '../entry_match_any'; -import { entriesMatchWildcard } from '../entry_match_wildcard'; -import { entriesNested } from '../entry_nested'; - -// NOTE: Type nested is not included here to denote it's non-recursive nature. -// So a nested entry is really just a collection of `Entry` types. -export const entry = t.union([ - entriesMatch, - entriesMatchAny, - entriesList, - entriesExists, - entriesMatchWildcard, -]); -export type Entry = t.TypeOf<typeof entry>; - -export const entriesArray = t.array( - t.union([ - entriesMatch, - entriesMatchAny, - entriesList, - entriesExists, - entriesNested, - entriesMatchWildcard, - ]) -); -export type EntriesArray = t.TypeOf<typeof entriesArray>; - -export const entriesArrayOrUndefined = t.union([entriesArray, t.undefined]); -export type EntriesArrayOrUndefined = t.TypeOf<typeof entriesArrayOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.mock.ts deleted file mode 100644 index 3a588ec9cc7b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.mock.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 { EntryExists } from '.'; -import { EXISTS, FIELD, OPERATOR } from '../../constants/index.mock'; - -export const getEntryExistsMock = (): EntryExists => ({ - field: FIELD, - operator: OPERATOR, - type: EXISTS, -}); - -export const getEntryExistsExcludedMock = (): EntryExists => ({ - ...getEntryExistsMock(), - operator: 'excluded', -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.test.ts deleted file mode 100644 index 8ebf60ebc19f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.test.ts +++ /dev/null @@ -1,80 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEntryExistsMock } from './index.mock'; -import { entriesExists, EntryExists } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('entriesExists', () => { - test('it should validate an entry', () => { - const payload = getEntryExistsMock(); - const decoded = entriesExists.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when "operator" is "included"', () => { - const payload = getEntryExistsMock(); - const decoded = entriesExists.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when "operator" is "excluded"', () => { - const payload = getEntryExistsMock(); - payload.operator = 'excluded'; - const decoded = entriesExists.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when "field" is empty string', () => { - const payload: Omit<EntryExists, 'field'> & { field: string } = { - ...getEntryExistsMock(), - field: '', - }; - const decoded = entriesExists.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: EntryExists & { - extraKey?: string; - } = getEntryExistsMock(); - payload.extraKey = 'some extra key'; - const decoded = entriesExists.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getEntryExistsMock()); - }); - - test('it should FAIL validation when "type" is not "exists"', () => { - const payload: Omit<EntryExists, 'type'> & { type: string } = { - ...getEntryExistsMock(), - type: 'match', - }; - const decoded = entriesExists.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.ts deleted file mode 100644 index 460030dcc951..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.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 * as t from 'io-ts'; - -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; -import { listOperator as operator } from '../list_operator'; - -export const entriesExists = t.exact( - t.type({ - field: NonEmptyString, - operator, - type: t.keyof({ exists: null }), - }) -); -export type EntryExists = t.TypeOf<typeof entriesExists>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.mock.ts deleted file mode 100644 index 06e006ed661b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.mock.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", 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 { EntryList } from '.'; -import { FIELD, LIST, LIST_ID, OPERATOR, TYPE } from '../../constants/index.mock'; - -export const getEntryListMock = (): EntryList => ({ - field: FIELD, - list: { id: LIST_ID, type: TYPE }, - operator: OPERATOR, - type: LIST, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.test.ts deleted file mode 100644 index eb047a07f08d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEntryListMock } from './index.mock'; -import { entriesList, EntryList } from '.'; - -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('entriesList', () => { - test('it should validate an entry', () => { - const payload = getEntryListMock(); - const decoded = entriesList.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when operator is "included"', () => { - const payload = getEntryListMock(); - const decoded = entriesList.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when "operator" is "excluded"', () => { - const payload = getEntryListMock(); - payload.operator = 'excluded'; - const decoded = entriesList.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when "list" is not expected value', () => { - const payload: Omit<EntryList, 'list'> & { list: string } = { - ...getEntryListMock(), - list: 'someListId', - }; - const decoded = entriesList.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "someListId" supplied to "list"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "list.id" is empty string', () => { - const payload: Omit<EntryList, 'list'> & { list: { id: string; type: 'ip' } } = { - ...getEntryListMock(), - list: { id: '', type: 'ip' }, - }; - const decoded = entriesList.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "list,id"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "type" is not "lists"', () => { - const payload: Omit<EntryList, 'type'> & { type: 'match_any' } = { - ...getEntryListMock(), - type: 'match_any', - }; - const decoded = entriesList.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "match_any" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: EntryList & { - extraKey?: string; - } = getEntryListMock(); - payload.extraKey = 'some extra key'; - const decoded = entriesList.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getEntryListMock()); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.ts deleted file mode 100644 index f55a3f056218..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.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 * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; - -import { type } from '../type'; -import { listOperator as operator } from '../list_operator'; - -export const entriesList = t.exact( - t.type({ - field: NonEmptyString, - list: t.exact(t.type({ id: NonEmptyString, type })), - operator, - type: t.keyof({ list: null }), - }) -); -export type EntryList = t.TypeOf<typeof entriesList>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.mock.ts deleted file mode 100644 index 9e09661c2945..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.mock.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { EntryMatch } from '.'; -import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../../constants/index.mock'; - -export const getEntryMatchMock = (): EntryMatch => ({ - field: FIELD, - operator: OPERATOR, - type: MATCH, - value: ENTRY_VALUE, -}); - -export const getEntryMatchExcludeMock = (): EntryMatch => ({ - ...getEntryMatchMock(), - operator: 'excluded', -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.test.ts deleted file mode 100644 index f2f8dbb8998f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.test.ts +++ /dev/null @@ -1,108 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEntryMatchMock } from './index.mock'; -import { entriesMatch, EntryMatch } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('entriesMatch', () => { - test('it should validate an entry', () => { - const payload = getEntryMatchMock(); - const decoded = entriesMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when operator is "included"', () => { - const payload = getEntryMatchMock(); - const decoded = entriesMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when "operator" is "excluded"', () => { - const payload = getEntryMatchMock(); - payload.operator = 'excluded'; - const decoded = entriesMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when "field" is empty string', () => { - const payload: Omit<EntryMatch, 'field'> & { field: string } = { - ...getEntryMatchMock(), - field: '', - }; - const decoded = entriesMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "value" is not string', () => { - const payload: Omit<EntryMatch, 'value'> & { value: string[] } = { - ...getEntryMatchMock(), - value: ['some value'], - }; - const decoded = entriesMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "["some value"]" supplied to "value"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "value" is empty string', () => { - const payload: Omit<EntryMatch, 'value'> & { value: string } = { - ...getEntryMatchMock(), - value: '', - }; - const decoded = entriesMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "type" is not "match"', () => { - const payload: Omit<EntryMatch, 'type'> & { type: string } = { - ...getEntryMatchMock(), - type: 'match_any', - }; - const decoded = entriesMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "match_any" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: EntryMatch & { - extraKey?: string; - } = getEntryMatchMock(); - payload.extraKey = 'some value'; - const decoded = entriesMatch.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getEntryMatchMock()); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.ts deleted file mode 100644 index 17bf0ab5b89e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.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 * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; -import { listOperator as operator } from '../list_operator'; - -export const entriesMatch = t.exact( - t.type({ - field: NonEmptyString, - operator, - type: t.keyof({ match: null }), - value: NonEmptyString, - }) -); -export type EntryMatch = t.TypeOf<typeof entriesMatch>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.mock.ts deleted file mode 100644 index 7a5c4c57fa32..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.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 { EntryMatchAny } from '.'; -import { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../../constants/index.mock'; - -export const getEntryMatchAnyMock = (): EntryMatchAny => ({ - field: FIELD, - operator: OPERATOR, - type: MATCH_ANY, - value: [ENTRY_VALUE], -}); - -export const getEntryMatchAnyExcludeMock = (): EntryMatchAny => ({ - ...getEntryMatchAnyMock(), - operator: 'excluded', - value: [ENTRY_VALUE, 'some other host name'], -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.test.ts deleted file mode 100644 index 3e4752f39508..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.test.ts +++ /dev/null @@ -1,106 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEntryMatchAnyMock } from './index.mock'; -import { entriesMatchAny, EntryMatchAny } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('entriesMatchAny', () => { - test('it should validate an entry', () => { - const payload = getEntryMatchAnyMock(); - const decoded = entriesMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when operator is "included"', () => { - const payload = getEntryMatchAnyMock(); - const decoded = entriesMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when operator is "excluded"', () => { - const payload = getEntryMatchAnyMock(); - payload.operator = 'excluded'; - const decoded = entriesMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when field is empty string', () => { - const payload: Omit<EntryMatchAny, 'field'> & { field: string } = { - ...getEntryMatchAnyMock(), - field: '', - }; - const decoded = entriesMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when value is empty array', () => { - const payload: Omit<EntryMatchAny, 'value'> & { value: string[] } = { - ...getEntryMatchAnyMock(), - value: [], - }; - const decoded = entriesMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "[]" supplied to "value"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when value is not string array', () => { - const payload: Omit<EntryMatchAny, 'value'> & { value: string } = { - ...getEntryMatchAnyMock(), - value: 'some string', - }; - const decoded = entriesMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "some string" supplied to "value"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "type" is not "match_any"', () => { - const payload: Omit<EntryMatchAny, 'type'> & { type: string } = { - ...getEntryMatchAnyMock(), - type: 'match', - }; - const decoded = entriesMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: EntryMatchAny & { - extraKey?: string; - } = getEntryMatchAnyMock(); - payload.extraKey = 'some extra key'; - const decoded = entriesMatchAny.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getEntryMatchAnyMock()); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.ts deleted file mode 100644 index be08fb370abd..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -import { NonEmptyString, nonEmptyOrNullableStringArray } from '@kbn/securitysolution-io-ts-types'; -import { listOperator as operator } from '../list_operator'; - -export const entriesMatchAny = t.exact( - t.type({ - field: NonEmptyString, - operator, - type: t.keyof({ match_any: null }), - value: nonEmptyOrNullableStringArray, - }) -); -export type EntryMatchAny = t.TypeOf<typeof entriesMatchAny>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.mock.ts deleted file mode 100644 index 6dc33289d22d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.mock.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { EntryMatchWildcard } from '.'; -import { ENTRY_VALUE, FIELD, OPERATOR, WILDCARD } from '../../constants/index.mock'; - -export const getEntryMatchWildcardMock = (): EntryMatchWildcard => ({ - field: FIELD, - operator: OPERATOR, - type: WILDCARD, - value: ENTRY_VALUE, -}); - -export const getEntryMatchWildcardExcludeMock = (): EntryMatchWildcard => ({ - ...getEntryMatchWildcardMock(), - operator: 'excluded', -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.test.ts deleted file mode 100644 index a84996a6c105..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.test.ts +++ /dev/null @@ -1,106 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEntryMatchWildcardMock } from './index.mock'; -import { entriesMatchWildcard, EntryMatchWildcard } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('entriesMatchWildcard', () => { - test('it should validate an entry', () => { - const payload = getEntryMatchWildcardMock(); - const decoded = entriesMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when operator is "included"', () => { - const payload = getEntryMatchWildcardMock(); - const decoded = entriesMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when "operator" is "excluded"', () => { - const payload = getEntryMatchWildcardMock(); - payload.operator = 'excluded'; - const decoded = entriesMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when "field" is empty string', () => { - const payload: Omit<EntryMatchWildcard, 'field'> & { field: string } = { - ...getEntryMatchWildcardMock(), - field: '', - }; - const decoded = entriesMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "value" is not string', () => { - const payload: Omit<EntryMatchWildcard, 'value'> & { value: string[] } = { - ...getEntryMatchWildcardMock(), - value: ['some value'], - }; - const decoded = entriesMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "["some value"]" supplied to "value"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "value" is empty string', () => { - const payload: Omit<EntryMatchWildcard, 'value'> & { value: string } = { - ...getEntryMatchWildcardMock(), - value: '', - }; - const decoded = entriesMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "type" is not "wildcard"', () => { - const payload: Omit<EntryMatchWildcard, 'type'> & { type: string } = { - ...getEntryMatchWildcardMock(), - type: 'match', - }; - const decoded = entriesMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: EntryMatchWildcard & { - extraKey?: string; - } = getEntryMatchWildcardMock(); - payload.extraKey = 'some value'; - const decoded = entriesMatchWildcard.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getEntryMatchWildcardMock()); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.ts deleted file mode 100644 index 17afe342961c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.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 * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; -import { listOperator as operator } from '../list_operator'; - -export const entriesMatchWildcard = t.exact( - t.type({ - field: NonEmptyString, - operator, - type: t.keyof({ wildcard: null }), - value: NonEmptyString, - }) -); -export type EntryMatchWildcard = t.TypeOf<typeof entriesMatchWildcard>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.mock.ts deleted file mode 100644 index 34d687502e24..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.mock.ts +++ /dev/null @@ -1,30 +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 { EntryNested } from '.'; -import { NESTED, NESTED_FIELD } from '../../constants/index.mock'; -import { getEntryExistsMock } from '../entries_exist/index.mock'; -import { getEntryMatchExcludeMock, getEntryMatchMock } from '../entry_match/index.mock'; -import { getEntryMatchAnyExcludeMock, getEntryMatchAnyMock } from '../entry_match_any/index.mock'; - -export const getEntryNestedMock = (): EntryNested => ({ - entries: [getEntryMatchMock(), getEntryMatchAnyMock()], - field: NESTED_FIELD, - type: NESTED, -}); - -export const getEntryNestedExcludeMock = (): EntryNested => ({ - ...getEntryNestedMock(), - entries: [getEntryMatchExcludeMock(), getEntryMatchAnyExcludeMock()], -}); - -export const getEntryNestedMixedEntries = (): EntryNested => ({ - ...getEntryNestedMock(), - entries: [getEntryMatchMock(), getEntryMatchAnyExcludeMock(), getEntryExistsMock()], -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.test.ts deleted file mode 100644 index cfd43c4c0993..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.test.ts +++ /dev/null @@ -1,125 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEntryNestedMock } from './index.mock'; -import { entriesNested, EntryNested } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; -import { getEntryExistsMock } from '../entries_exist/index.mock'; - -describe('entriesNested', () => { - test('it should validate a nested entry', () => { - const payload = getEntryNestedMock(); - const decoded = entriesNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when "type" is not "nested"', () => { - const payload: Omit<EntryNested, 'type'> & { type: 'match' } = { - ...getEntryNestedMock(), - type: 'match', - }; - const decoded = entriesNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "field" is empty string', () => { - const payload: Omit<EntryNested, 'field'> & { - field: string; - } = { ...getEntryNestedMock(), field: '' }; - const decoded = entriesNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "field" is not a string', () => { - const payload: Omit<EntryNested, 'field'> & { - field: number; - } = { ...getEntryNestedMock(), field: 1 }; - const decoded = entriesNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "field"']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when "entries" is not a an array', () => { - const payload: Omit<EntryNested, 'entries'> & { - entries: string; - } = { ...getEntryNestedMock(), entries: 'im a string' }; - const decoded = entriesNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "im a string" supplied to "entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should validate when "entries" contains an entry item that is type "match"', () => { - const payload = { ...getEntryNestedMock(), entries: [getEntryMatchAnyMock()] }; - const decoded = entriesNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual({ - entries: [ - { - field: 'host.name', - operator: 'included', - type: 'match_any', - value: ['some host name'], - }, - ], - field: 'parent.field', - type: 'nested', - }); - }); - - test('it should validate when "entries" contains an entry item that is type "exists"', () => { - const payload = { ...getEntryNestedMock(), entries: [getEntryExistsMock()] }; - const decoded = entriesNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual({ - entries: [ - { - field: 'host.name', - operator: 'included', - type: 'exists', - }, - ], - field: 'parent.field', - type: 'nested', - }); - }); - - test('it should strip out extra keys', () => { - const payload: EntryNested & { - extraKey?: string; - } = getEntryNestedMock(); - payload.extraKey = 'some extra key'; - const decoded = entriesNested.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getEntryNestedMock()); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.ts deleted file mode 100644 index 8b19fee9fb5c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.ts +++ /dev/null @@ -1,21 +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 * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; -import { nonEmptyNestedEntriesArray } from '../non_empty_nested_entries_array'; - -export const entriesNested = t.exact( - t.type({ - entries: nonEmptyNestedEntriesArray, - field: NonEmptyString, - type: t.keyof({ nested: null }), - }) -); -export type EntryNested = t.TypeOf<typeof entriesNested>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.mock.ts deleted file mode 100644 index 15cdd79dc2e9..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.mock.ts +++ /dev/null @@ -1,30 +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 { ExportExceptionDetails } from '.'; - -export interface ExportExceptionDetailsMock { - listCount?: number; - missingListsCount?: number; - missingLists?: Array<Record<'list_id', string>>; - itemCount?: number; - missingItemCount?: number; - missingItems?: Array<Record<'item_id', string>>; -} - -export const getExceptionExportDetailsMock = ( - details?: ExportExceptionDetailsMock -): ExportExceptionDetails => ({ - exported_exception_list_count: details?.listCount ?? 0, - exported_exception_list_item_count: details?.itemCount ?? 0, - missing_exception_list_item_count: details?.missingItemCount ?? 0, - missing_exception_list_items: details?.missingItems ?? [], - missing_exception_lists: details?.missingLists ?? [], - missing_exception_lists_count: details?.missingListsCount ?? 0, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.test.ts deleted file mode 100644 index c9b1a4767e57..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.test.ts +++ /dev/null @@ -1,37 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getExceptionExportDetailsMock } from './index.mock'; -import { exportExceptionDetailsSchema, ExportExceptionDetails } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('exportExceptionDetails', () => { - test('it should validate export meta', () => { - const payload = getExceptionExportDetailsMock(); - const decoded = exportExceptionDetailsSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should strip out extra keys', () => { - const payload: ExportExceptionDetails & { - extraKey?: string; - } = getExceptionExportDetailsMock(); - payload.extraKey = 'some extra key'; - const decoded = exportExceptionDetailsSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getExceptionExportDetailsMock()); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.ts deleted file mode 100644 index 9e4ff134d6e9..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.ts +++ /dev/null @@ -1,36 +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 * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; - -export const exportExceptionDetails = { - exported_exception_list_count: t.number, - exported_exception_list_item_count: t.number, - missing_exception_list_item_count: t.number, - missing_exception_list_items: t.array( - t.exact( - t.type({ - item_id: NonEmptyString, - }) - ) - ), - missing_exception_lists: t.array( - t.exact( - t.type({ - list_id: NonEmptyString, - }) - ) - ), - missing_exception_lists_count: t.number, -}; - -export const exportExceptionDetailsSchema = t.exact(t.type(exportExceptionDetails)); - -export type ExportExceptionDetails = t.TypeOf<typeof exportExceptionDetailsSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.test.ts deleted file mode 100644 index 2c6aae8a5693..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.test.ts +++ /dev/null @@ -1,55 +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 { exceptionListType, ExceptionListTypeEnum } from '.'; - -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('exceptionListType', () => { - test('it should validate for "detection"', () => { - const payload = 'detection'; - const decoded = exceptionListType.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate for "rule_default"', () => { - const payload = 'rule_default'; - const decoded = exceptionListType.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate for "endpoint"', () => { - const payload = 'endpoint'; - const decoded = exceptionListType.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should contain same amount of keys as enum', () => { - // Might seem like a weird test, but its meant to - // ensure that if exceptionListType is updated, you - // also update the ExceptionListTypeEnum, a workaround - // for io-ts not yet supporting enums - // https://github.com/gcanti/io-ts/issues/67 - const keys = Object.keys(exceptionListType.keys).sort().join(',').toLowerCase(); - const enumKeys = Object.keys(ExceptionListTypeEnum).sort().join(',').toLowerCase(); - - expect(keys).toEqual(enumKeys); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.ts deleted file mode 100644 index db7633705ac5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.ts +++ /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 * as t from 'io-ts'; - -export const exceptionListType = t.keyof({ - detection: null, - rule_default: null, - endpoint: null, - endpoint_trusted_apps: null, - endpoint_events: null, - endpoint_host_isolation_exceptions: null, - endpoint_blocklists: null, -}); -export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]); -export type ExceptionListType = t.TypeOf<typeof exceptionListType>; -export type ExceptionListTypeOrUndefined = t.TypeOf<typeof exceptionListTypeOrUndefined>; -export enum ExceptionListTypeEnum { - DETECTION = 'detection', // shared exception list type - RULE_DEFAULT = 'rule_default', // rule default, cannot be shared - ENDPOINT = 'endpoint', - ENDPOINT_TRUSTED_APPS = 'endpoint', - ENDPOINT_EVENTS = 'endpoint_events', - ENDPOINT_HOST_ISOLATION_EXCEPTIONS = 'endpoint_host_isolation_exceptions', - ENDPOINT_BLOCKLISTS = 'endpoint_blocklists', -} diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list_item_type/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list_item_type/index.ts deleted file mode 100644 index 5e124b9f923d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list_item_type/index.ts +++ /dev/null @@ -1,15 +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 * as t from 'io-ts'; - -export const exceptionListItemType = t.keyof({ simple: null }); -export const exceptionListItemTypeOrUndefined = t.union([exceptionListItemType, t.undefined]); -export type ExceptionListItemType = t.TypeOf<typeof exceptionListItemType>; -export type ExceptionListItemTypeOrUndefined = t.TypeOf<typeof exceptionListItemTypeOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/expire_time/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/expire_time/index.ts deleted file mode 100644 index 4e8184d4e6ce..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/expire_time/index.ts +++ /dev/null @@ -1,15 +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 * as t from 'io-ts'; -import { IsoDateString } from '@kbn/securitysolution-io-ts-types'; - -export const expireTime = IsoDateString; -export const expireTimeOrUndefined = t.union([expireTime, t.undefined]); -export type ExpireTimeOrUndefined = t.TypeOf<typeof expireTimeOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/file/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/file/index.ts deleted file mode 100644 index f569d42c41f1..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/file/index.ts +++ /dev/null @@ -1,12 +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 * as t from 'io-ts'; - -export const file = t.object; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/filter/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/filter/index.ts deleted file mode 100644 index 70d526eaefbe..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/filter/index.ts +++ /dev/null @@ -1,15 +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 * as t from 'io-ts'; - -export const filter = t.string; -export type Filter = t.TypeOf<typeof filter>; -export const filterOrUndefined = t.union([filter, t.undefined]); -export type FilterOrUndefined = t.TypeOf<typeof filterOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/id/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/id/index.ts deleted file mode 100644 index c9f35b36ee57..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/id/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", 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 * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; - -export const id = NonEmptyString; -export type Id = t.TypeOf<typeof id>; -export const idOrUndefined = t.union([id, t.undefined]); -export type IdOrUndefined = t.TypeOf<typeof idOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/immutable/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/immutable/index.ts deleted file mode 100644 index 9bdb50647abd..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/immutable/index.ts +++ /dev/null @@ -1,15 +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 * as t from 'io-ts'; - -export const immutable = t.boolean; -export type Immutable = t.TypeOf<typeof immutable>; -export const immutableOrUndefined = t.union([immutable, t.undefined]); -export type ImmutableOrUndefined = t.TypeOf<typeof immutableOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.test.ts deleted file mode 100644 index 50121086c141..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.test.ts +++ /dev/null @@ -1,136 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getCommentsArrayMock, getCommentsMock } from '../comment/index.mock'; -import { getCreateCommentsArrayMock } from '../create_comment/index.mock'; -import { - importComment, - ImportCommentsArray, - importCommentsArray, - ImportCommentsArrayOrUndefined, - importCommentsArrayOrUndefined, -} from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('ImportComment', () => { - describe('importComment', () => { - test('it passes validation with a typical comment', () => { - const payload = getCommentsMock(); - const decoded = importComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it passes validation with a new comment', () => { - const payload = { comment: 'new comment' }; - const decoded = importComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it fails validation when undefined', () => { - const payload = undefined; - const decoded = importComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "(({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: NonEmptyString |})"', - ]); - expect(message.schema).toEqual({}); - }); - }); - - describe('importCommentsArray', () => { - test('it passes validation an array of Comment', () => { - const payload = getCommentsArrayMock(); - const decoded = importCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it passes validation an array of CreateComment', () => { - const payload = getCreateCommentsArrayMock(); - const decoded = importCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it passes validation an array of Comment and CreateComment', () => { - const payload = [...getCommentsArrayMock(), ...getCreateCommentsArrayMock()]; - const decoded = importCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it fails validation when undefined', () => { - const payload = undefined; - const decoded = importCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "Array<(({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: NonEmptyString |})>"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it fails validation when array includes non ImportComment types', () => { - const payload = [1] as unknown as ImportCommentsArray; - const decoded = importCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<(({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: NonEmptyString |})>"', - ]); - expect(message.schema).toEqual({}); - }); - }); - - describe('importCommentsArrayOrUndefined', () => { - test('it passes validation an array of ImportComment', () => { - const payload = [...getCommentsArrayMock(), ...getCreateCommentsArrayMock()]; - const decoded = importCommentsArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it passes validation when undefined', () => { - const payload = undefined; - const decoded = importCommentsArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it fails validation when array includes non ImportComment types', () => { - const payload = [1] as unknown as ImportCommentsArrayOrUndefined; - const decoded = importCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<(({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: NonEmptyString |})>"', - ]); - expect(message.schema).toEqual({}); - }); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.ts deleted file mode 100644 index 4511b28078bc..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { createComment } from '../create_comment'; -import { comment } from '../comment'; - -export const importComment = t.union([comment, createComment]); - -export type ImportComment = t.TypeOf<typeof importComment>; -export const importCommentsArray = t.array(importComment); -export type ImportCommentsArray = t.TypeOf<typeof importCommentsArray>; -export const importCommentsArrayOrUndefined = t.union([importCommentsArray, t.undefined]); -export type ImportCommentsArrayOrUndefined = t.TypeOf<typeof importCommentsArrayOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/include_expired_exceptions/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/include_expired_exceptions/index.ts deleted file mode 100644 index 7c40e74d8189..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/include_expired_exceptions/index.ts +++ /dev/null @@ -1,21 +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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const include_expired_exceptions = t.keyof({ true: null, false: null }); -export const includeExpiredExceptionsOrUndefined = t.union([ - include_expired_exceptions, - t.undefined, -]); -export type IncludeExpiredExceptionsOrUndefined = t.TypeOf< - typeof includeExpiredExceptionsOrUndefined ->; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/index.ts deleted file mode 100644 index b30ac02dab96..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/index.ts +++ /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". - */ - -export * from './comment'; -export * from './create_comment'; -export * from './created_at'; -export * from './created_by'; -export * from './cursor'; -export * from './default_namespace'; -export * from './default_namespace_array'; -export * from './default_create_comments_array'; -export * from './default_import_comments_array'; -export * from './description'; -export * from './deserializer'; -export * from './endpoint'; -export * from './entries'; -export * from './entries_exist'; -export * from './entries_list'; -export * from './entry_match'; -export * from './entry_match_any'; -export * from './entry_match_wildcard'; -export * from './entry_nested'; -export * from './exception_export_details'; -export * from './exception_list'; -export * from './exception_list_item_type'; -export * from './expire_time'; -export * from './filter'; -export * from './id'; -export * from './immutable'; -export * from './import_comment'; -export * from './item_id'; -export * from './list_id'; -export * from './list_operator'; -export * from './list_type'; -export * from './lists'; -export * from './lists_default_array'; -export * from './max_size'; -export * from './meta'; -export * from './name'; -export * from './namespace_type'; -export * from './non_empty_entries_array'; -export * from './non_empty_nested_entries_array'; -export * from './os_type'; -export * from './page'; -export * from './per_page'; -export * from './pit'; -export * from './search'; -export * from './search_after'; -export * from './serializer'; -export * from './sort_field'; -export * from './sort_order'; -export * from './tags'; -export * from './tie_breaker_id'; -export * from './timestamp'; -export * from './total'; -export * from './type'; -export * from './underscore_version'; -export * from './update_comment'; -export * from './updated_at'; -export * from './updated_by'; -export * from './refresh'; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/item/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/item/index.ts deleted file mode 100644 index d69ec5358400..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/item/index.ts +++ /dev/null @@ -1,12 +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 * as t from 'io-ts'; - -export const item = t.string; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/item_id/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/item_id/index.ts deleted file mode 100644 index 0e4551ad5c8f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/item_id/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; - -export const item_id = NonEmptyString; -export type ItemId = t.TypeOf<typeof item_id>; -export const itemIdOrUndefined = t.union([item_id, t.undefined]); -export type ItemIdOrUndefined = t.TypeOf<typeof itemIdOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/list_id/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/list_id/index.ts deleted file mode 100644 index 88e88aa8bc3c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/list_id/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; - -export const list_id = NonEmptyString; -export type ListId = t.TypeOf<typeof list_id>; -export const list_idOrUndefined = t.union([list_id, t.undefined]); -export type ListIdOrUndefined = t.TypeOf<typeof list_idOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.test.ts deleted file mode 100644 index 054af26b4a62..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.test.ts +++ /dev/null @@ -1,46 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { ListOperatorEnum as OperatorEnum, listOperator as operator } from '.'; - -describe('operator', () => { - test('it should validate for "included"', () => { - const payload = 'included'; - const decoded = operator.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate for "excluded"', () => { - const payload = 'excluded'; - const decoded = operator.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should contain same amount of keys as enum', () => { - // Might seem like a weird test, but its meant to - // ensure that if operator is updated, you - // also update the operatorEnum, a workaround - // for io-ts not yet supporting enums - // https://github.com/gcanti/io-ts/issues/67 - const keys = Object.keys(operator.keys).sort().join(',').toLowerCase(); - const enumKeys = Object.keys(OperatorEnum).sort().join(',').toLowerCase(); - - expect(keys).toEqual(enumKeys); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.ts deleted file mode 100644 index 574a64e0f032..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -export const listOperator = t.keyof({ excluded: null, included: null }); -export type ListOperator = t.TypeOf<typeof listOperator>; -export enum ListOperatorEnum { - INCLUDED = 'included', - EXCLUDED = 'excluded', -} - -export const listOperatorType = t.keyof({ - nested: null, - match: null, - match_any: null, - wildcard: null, - exists: null, - list: null, -}); -export type ListOperatorType = t.TypeOf<typeof listOperatorType>; -export enum ListOperatorTypeEnum { - NESTED = 'nested', - MATCH = 'match', - MATCH_ANY = 'match_any', - WILDCARD = 'wildcard', - EXISTS = 'exists', - LIST = 'list', -} diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/list_type/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/list_type/index.ts deleted file mode 100644 index 716d9a6b24d7..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/list_type/index.ts +++ /dev/null @@ -1,15 +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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const list_type = t.keyof({ item: null, list: null }); -export type ListType = t.TypeOf<typeof list_type>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.mock.ts deleted file mode 100644 index c8319867b577..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.mock.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { List, ListArray } from '.'; -import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; - -export const getListMock = (): List => ({ - id: 'some_uuid', - list_id: 'list_id_single', - namespace_type: 'single', - type: 'detection', -}); - -export const getEndpointListMock = (): List => ({ - id: ENDPOINT_LIST_ID, - list_id: ENDPOINT_LIST_ID, - namespace_type: 'agnostic', - type: 'endpoint', -}); - -export const getListArrayMock = (): ListArray => [getListMock(), getEndpointListMock()]; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.test.ts deleted file mode 100644 index 57c221f10384..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.test.ts +++ /dev/null @@ -1,127 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getEndpointListMock, getListArrayMock, getListMock } from './index.mock'; -import { List, list, ListArray, listArray, ListArrayOrUndefined, listArrayOrUndefined } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('Lists', () => { - describe('list', () => { - test('it should validate a list', () => { - const payload = getListMock(); - const decoded = list.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate a list with "namespace_type" of "agnostic"', () => { - const payload = getEndpointListMock(); - const decoded = list.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT validate a list without an "id"', () => { - const payload = getListMock(); - // @ts-expect-error - delete payload.id; - const decoded = list.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT validate a list without "namespace_type"', () => { - const payload = getListMock(); - // @ts-expect-error - delete payload.namespace_type; - const decoded = list.decode(payload); - const message = pipe(decoded, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "namespace_type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra keys', () => { - const payload: List & { - extraKey?: string; - } = getListMock(); - payload.extraKey = 'some value'; - const decoded = list.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getListMock()); - }); - }); - - describe('listArray', () => { - test('it should validate an array of lists', () => { - const payload = getListArrayMock(); - const decoded = listArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate when unexpected type found in array', () => { - const payload = [1] as unknown as ListArray; - const decoded = listArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "rule_default" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions" | "endpoint_blocklists", namespace_type: "agnostic" | "single" |}>"', - ]); - expect(message.schema).toEqual({}); - }); - }); - - describe('listArrayOrUndefined', () => { - test('it should validate an array of lists', () => { - const payload = getListArrayMock(); - const decoded = listArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate when undefined', () => { - const payload = undefined; - const decoded = listArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an item that is not of type "list" in array', () => { - const payload = [1] as unknown as ListArrayOrUndefined; - const decoded = listArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "rule_default" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions" | "endpoint_blocklists", namespace_type: "agnostic" | "single" |}> | undefined)"', - 'Invalid value "[1]" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "rule_default" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions" | "endpoint_blocklists", namespace_type: "agnostic" | "single" |}> | undefined)"', - ]); - expect(message.schema).toEqual({}); - }); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.ts deleted file mode 100644 index 564ce7833b0f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; -import { exceptionListType } from '../exception_list'; -import { namespaceType } from '../default_namespace'; - -export const list = t.exact( - t.type({ - id: NonEmptyString, - list_id: NonEmptyString, - type: exceptionListType, - namespace_type: namespaceType, - }) -); - -export type List = t.TypeOf<typeof list>; -export const listArray = t.array(list); -export type ListArray = t.TypeOf<typeof listArray>; -export const listArrayOrUndefined = t.union([listArray, t.undefined]); -export type ListArrayOrUndefined = t.TypeOf<typeof listArrayOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.test.ts deleted file mode 100644 index fa4f22c4718e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultListArray } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getListArrayMock } from '../lists/index.mock'; - -describe('lists_default_array', () => { - test('it should return a default array when null', () => { - const payload = null; - const decoded = DefaultListArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([]); - }); - - test('it should return a default array when undefined', () => { - const payload = undefined; - const decoded = DefaultListArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([]); - }); - - test('it should validate an empty array', () => { - const payload: string[] = []; - const decoded = DefaultListArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array of lists', () => { - const payload = getListArrayMock(); - const decoded = DefaultListArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate an array of non accepted types', () => { - // Terrible casting for purpose of tests - const payload = [1] as unknown; - const decoded = DefaultListArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "DefaultListArray"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.ts deleted file mode 100644 index a014d769410e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { list, ListArray } from '../lists'; - -/** - * Types the DefaultListArray as: - * - If null or undefined, then a default array of type list will be set - */ -export const DefaultListArray = new t.Type<ListArray, ListArray | undefined, unknown>( - 'DefaultListArray', - t.array(list).is, - (input, context): Either<t.Errors, ListArray> => - input == null ? t.success([]) : t.array(list).validate(input, context), - t.identity -); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.test.ts deleted file mode 100644 index 9ddf86818eaf..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { exactCheck } from '@kbn/securitysolution-io-ts-utils'; -import { maxSizeOrUndefined } from '.'; - -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; - -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('maxSizeOrUndefined', () => { - test('it will validate a correct max value', () => { - const payload = 123; - const decoded = maxSizeOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it will fail to validate a 0', () => { - const payload = 0; - const decoded = maxSizeOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "0" supplied to "(PositiveIntegerGreaterThanZero | undefined)"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it will fail to validate a -1', () => { - const payload = -1; - const decoded = maxSizeOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "-1" supplied to "(PositiveIntegerGreaterThanZero | undefined)"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it will fail to validate a string', () => { - const payload = '123'; - const decoded = maxSizeOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "123" supplied to "(PositiveIntegerGreaterThanZero | undefined)"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.ts deleted file mode 100644 index 0c99edf69252..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; -import * as t from 'io-ts'; - -export const max_size = PositiveIntegerGreaterThanZero; -export type MaxSize = t.TypeOf<typeof max_size>; - -export const maxSizeOrUndefined = t.union([max_size, t.undefined]); -export type MaxSizeOrUndefined = t.TypeOf<typeof maxSizeOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/meta/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/meta/index.ts deleted file mode 100644 index 07f329819fc1..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/meta/index.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", 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 * as t from 'io-ts'; - -export const meta = t.object; -export type Meta = t.TypeOf<typeof meta>; -export const metaOrUndefined = t.union([meta, t.undefined]); -export type MetaOrUndefined = t.TypeOf<typeof metaOrUndefined>; - -export const nullableMetaOrUndefined = t.union([metaOrUndefined, t.null]); -export type NullableMetaOrUndefined = t.TypeOf<typeof nullableMetaOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/name/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/name/index.ts deleted file mode 100644 index 5d4a13c06cee..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/name/index.ts +++ /dev/null @@ -1,15 +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 * as t from 'io-ts'; - -export const name = t.string; -export type Name = t.TypeOf<typeof name>; -export const nameOrUndefined = t.union([name, t.undefined]); -export type NameOrUndefined = t.TypeOf<typeof nameOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/namespace_type/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/namespace_type/index.ts deleted file mode 100644 index b8d052910b86..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/namespace_type/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import { DefaultNamespace } from '../default_namespace'; - -export const namespace_type = DefaultNamespace; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.test.ts deleted file mode 100644 index 1128dd53871e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.test.ts +++ /dev/null @@ -1,132 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { EntriesArray } from '../entries'; -import { nonEmptyEntriesArray } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getEntryMatchMock } from '../entry_match/index.mock'; -import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; -import { getEntryExistsMock } from '../entries_exist/index.mock'; -import { - getEntriesArrayMock, - getListAndNonListEntriesArrayMock, - getListEntriesArrayMock, -} from '../entries/index.mock'; -import { getEntryNestedMock } from '../entry_nested/index.mock'; - -describe('non_empty_entries_array', () => { - test('it should FAIL validation when given an empty array', () => { - const payload: EntriesArray = []; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "[]" supplied to "NonEmptyEntriesArray"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when given "undefined"', () => { - const payload = undefined; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "NonEmptyEntriesArray"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when given "null"', () => { - const payload = null; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "null" supplied to "NonEmptyEntriesArray"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should validate an array of "match" entries', () => { - const payload: EntriesArray = [getEntryMatchMock(), getEntryMatchMock()]; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array of "match_any" entries', () => { - const payload: EntriesArray = [getEntryMatchAnyMock(), getEntryMatchAnyMock()]; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array of "exists" entries', () => { - const payload: EntriesArray = [getEntryExistsMock(), getEntryExistsMock()]; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array of "list" entries', () => { - const payload: EntriesArray = [...getListEntriesArrayMock()]; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array of "nested" entries', () => { - const payload: EntriesArray = [getEntryNestedMock(), getEntryNestedMock()]; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array of entries', () => { - const payload: EntriesArray = [...getEntriesArrayMock()]; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when given an array of entries of value list and non-value list entries', () => { - const payload: EntriesArray = [...getListAndNonListEntriesArrayMock()]; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Cannot have entry of type list and other']); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when given an array of non entries', () => { - const payload = [1]; - const decoded = nonEmptyEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "NonEmptyEntriesArray"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.ts deleted file mode 100644 index a5e06d5bb2c4..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.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", 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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { entriesArray, EntriesArray } from '../entries'; -import { entriesList } from '../entries_list'; - -/** - * Types the nonEmptyEntriesArray as: - * - An array of entries of length 1 or greater - * - */ -export const nonEmptyEntriesArray = new t.Type<EntriesArray, EntriesArray, unknown>( - 'NonEmptyEntriesArray', - entriesArray.is, - (input, context): Either<t.Errors, EntriesArray> => { - if (Array.isArray(input) && input.length === 0) { - return t.failure(input, context); - } else { - if ( - Array.isArray(input) && - input.some((entry) => entriesList.is(entry)) && - input.some((entry) => !entriesList.is(entry)) - ) { - // fail when an exception item contains both a value list entry and a non-value list entry - return t.failure(input, context, 'Cannot have entry of type list and other'); - } - return entriesArray.validate(input, context); - } - }, - t.identity -); - -export type NonEmptyEntriesArray = t.OutputOf<typeof nonEmptyEntriesArray>; -export type NonEmptyEntriesArrayDecoded = t.TypeOf<typeof nonEmptyEntriesArray>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.test.ts deleted file mode 100644 index afb62d1f8768..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.test.ts +++ /dev/null @@ -1,117 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { EntriesArray } from '../entries'; -import { nonEmptyNestedEntriesArray } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getEntryMatchMock } from '../entry_match/index.mock'; -import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; -import { getEntryExistsMock } from '../entries_exist/index.mock'; -import { getEntryNestedMock } from '../entry_nested/index.mock'; - -describe('non_empty_nested_entries_array', () => { - test('it should FAIL validation when given an empty array', () => { - const payload: EntriesArray = []; - const decoded = nonEmptyNestedEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "[]" supplied to "NonEmptyNestedEntriesArray"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when given "undefined"', () => { - const payload = undefined; - const decoded = nonEmptyNestedEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "NonEmptyNestedEntriesArray"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should FAIL validation when given "null"', () => { - const payload = null; - const decoded = nonEmptyNestedEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "null" supplied to "NonEmptyNestedEntriesArray"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should validate an array of "match" entries', () => { - const payload: EntriesArray = [getEntryMatchMock(), getEntryMatchMock()]; - const decoded = nonEmptyNestedEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array of "match_any" entries', () => { - const payload: EntriesArray = [getEntryMatchAnyMock(), getEntryMatchAnyMock()]; - const decoded = nonEmptyNestedEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate an array of "exists" entries', () => { - const payload: EntriesArray = [getEntryExistsMock(), getEntryExistsMock()]; - const decoded = nonEmptyNestedEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when given an array of "nested" entries', () => { - const payload: EntriesArray = [getEntryNestedMock(), getEntryNestedMock()]; - const decoded = nonEmptyNestedEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "operator"', - 'Invalid value "nested" supplied to "type"', - 'Invalid value "undefined" supplied to "value"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should validate an array of entries', () => { - const payload: EntriesArray = [ - getEntryExistsMock(), - getEntryMatchAnyMock(), - getEntryMatchMock(), - ]; - const decoded = nonEmptyNestedEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should FAIL validation when given an array of non entries', () => { - const payload = [1]; - const decoded = nonEmptyNestedEntriesArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "NonEmptyNestedEntriesArray"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.ts deleted file mode 100644 index 73a00e75010f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.ts +++ /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 * as t from 'io-ts'; -import { Either } from 'fp-ts/lib/Either'; -import { entriesMatch } from '../entry_match'; -import { entriesMatchAny } from '../entry_match_any'; -import { entriesExists } from '../entries_exist'; - -export const nestedEntryItem = t.union([entriesMatch, entriesMatchAny, entriesExists]); -export const nestedEntriesArray = t.array(nestedEntryItem); -export type NestedEntriesArray = t.TypeOf<typeof nestedEntriesArray>; - -/** - * Types the nonEmptyNestedEntriesArray as: - * - An array of entries of length 1 or greater - * - */ -export const nonEmptyNestedEntriesArray = new t.Type< - NestedEntriesArray, - NestedEntriesArray, - unknown ->( - 'NonEmptyNestedEntriesArray', - nestedEntriesArray.is, - (input, context): Either<t.Errors, NestedEntriesArray> => { - if (Array.isArray(input) && input.length === 0) { - return t.failure(input, context); - } else { - return nestedEntriesArray.validate(input, context); - } - }, - t.identity -); - -export type NonEmptyNestedEntriesArray = t.OutputOf<typeof nonEmptyNestedEntriesArray>; -export type NonEmptyNestedEntriesArrayDecoded = t.TypeOf<typeof nonEmptyNestedEntriesArray>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.test.ts deleted file mode 100644 index 566a2aa0973a..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { exactCheck } from '@kbn/securitysolution-io-ts-utils'; -import { osType, osTypeArrayOrUndefined } from '.'; - -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; - -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('osType', () => { - test('it will validate a correct osType', () => { - const payload = 'windows'; - const decoded = osType.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it will fail to validate an incorrect osType', () => { - const payload = 'foo'; - const decoded = osType.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "foo" supplied to ""linux" | "macos" | "windows""', - ]); - expect(message.schema).toEqual({}); - }); - - test('it will default to an empty array when osTypeArrayOrUndefined is used', () => { - const payload = undefined; - const decoded = osTypeArrayOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual([]); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.ts deleted file mode 100644 index 1dfbb9687e21..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.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 * as t from 'io-ts'; -import { DefaultArray } from '@kbn/securitysolution-io-ts-types'; - -export const osType = t.keyof({ - linux: null, - macos: null, - windows: null, -}); -export type OsType = t.TypeOf<typeof osType>; - -export const osTypeArray = DefaultArray(osType); -export type OsTypeArray = t.TypeOf<typeof osTypeArray>; - -export const osTypeArrayOrUndefined = t.union([osTypeArray, t.undefined]); -export type OsTypeArrayOrUndefined = t.OutputOf<typeof osTypeArray>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/page/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/page/index.ts deleted file mode 100644 index 58c5581cc6e2..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/page/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", 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 * as t from 'io-ts'; - -export const page = t.number; // TODO: Change this out for PositiveNumber from siem -export type Page = t.TypeOf<typeof page>; - -export const pageOrUndefined = t.union([page, t.undefined]); -export type PageOrUndefined = t.TypeOf<typeof pageOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/per_page/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/per_page/index.ts deleted file mode 100644 index 9ab3d1dde38c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/per_page/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const per_page = t.number; // TODO: Change this out for PositiveNumber from siem -export type PerPage = t.TypeOf<typeof per_page>; - -export const perPageOrUndefined = t.union([per_page, t.undefined]); -export type PerPageOrUndefined = t.TypeOf<typeof perPageOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.test.ts deleted file mode 100644 index 36bc95ce1786..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.test.ts +++ /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", 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 { exactCheck } from '@kbn/securitysolution-io-ts-utils'; -import { pitOrUndefined } from '.'; - -import * as t from 'io-ts'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; - -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('pitOrUndefined', () => { - test('it will validate a correct pit', () => { - const payload = { id: '123', keepAlive: '1m' }; - const decoded = pitOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it will validate with the value of "undefined"', () => { - const obj = t.exact( - t.type({ - pit_id: pitOrUndefined, - }) - ); - const payload: t.TypeOf<typeof obj> = { - pit_id: undefined, - }; - const decoded = obj.decode({ - pit_id: undefined, - }); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it will validate a correct pit without having a "keepAlive"', () => { - const payload = { id: '123' }; - const decoded = pitOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it will fail to validate an incorrect pit', () => { - const payload = 'foo'; - const decoded = pitOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "foo" supplied to "({| id: string, keepAlive: (string | undefined) |} | undefined)"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.ts deleted file mode 100644 index 3347f986113e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -export const pitId = t.string; -export const pit = t.exact( - t.type({ - id: pitId, - keepAlive: t.union([t.string, t.undefined]), - }) -); -export const pitOrUndefined = t.union([pit, t.undefined]); - -export type Pit = t.TypeOf<typeof pit>; -export type PitId = t.TypeOf<typeof pitId>; -export type PitOrUndefined = t.TypeOf<typeof pitOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/refresh/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/refresh/index.ts deleted file mode 100644 index faa301fd450a..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/refresh/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -export const refresh = t.union([t.literal('true'), t.literal('false')]); -export const refreshWithWaitFor = t.union([ - t.literal('true'), - t.literal('false'), - t.literal('wait_for'), -]); -export type Refresh = t.TypeOf<typeof refresh>; -export type RefreshWithWaitFor = t.TypeOf<typeof refreshWithWaitFor>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/required_keep_undefined/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/required_keep_undefined/index.ts deleted file mode 100644 index 5e6b30cf720c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/required_keep_undefined/index.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". - */ - -/** - * This makes any optional property the same as Required<T> would but also has the - * added benefit of keeping your undefined. - * - * For example: - * type A = RequiredKeepUndefined<{ a?: undefined; b: number }>; - * - * will yield a type of: - * type A = { a: undefined; b: number; } - * @deprecated This has no replacement. We should stop using/relying on this and just remove it. - */ -export type RequiredKeepUndefined<T> = { [K in keyof T]-?: [T[K]] } extends infer U - ? U extends Record<keyof U, [unknown]> - ? { [K in keyof U]: U[K][0] } - : never - : never; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.test.ts deleted file mode 100644 index 1224b277c852..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.test.ts +++ /dev/null @@ -1,57 +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 { exactCheck } from '@kbn/securitysolution-io-ts-utils'; -import { searchOrUndefined } from '.'; - -import * as t from 'io-ts'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; - -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('search', () => { - test('it will validate a correct search', () => { - const payload = 'name:foo'; - const decoded = searchOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it will validate with the value of "undefined"', () => { - const obj = t.exact( - t.type({ - search: searchOrUndefined, - }) - ); - const payload: t.TypeOf<typeof obj> = { - search: undefined, - }; - const decoded = obj.decode({ - pit_id: undefined, - }); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it will fail to validate an incorrect search', () => { - const payload = ['foo']; - const decoded = searchOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "["foo"]" supplied to "(string | undefined)"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts deleted file mode 100644 index 85e360b84267..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/search/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", 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 * as t from 'io-ts'; - -export const search = t.string; -export type Search = t.TypeOf<typeof search>; - -export const searchOrUndefined = t.union([search, t.undefined]); -export type SearchOrUndefined = t.TypeOf<typeof searchOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.test.ts deleted file mode 100644 index d06198272792..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.test.ts +++ /dev/null @@ -1,57 +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 { exactCheck } from '@kbn/securitysolution-io-ts-utils'; -import { searchAfterOrUndefined } from '.'; - -import * as t from 'io-ts'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; - -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('searchAfter', () => { - test('it will validate a correct search_after', () => { - const payload = ['test-1', 'test-2']; - const decoded = searchAfterOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it will validate with the value of "undefined"', () => { - const obj = t.exact( - t.type({ - search_after: searchAfterOrUndefined, - }) - ); - const payload: t.TypeOf<typeof obj> = { - search_after: undefined, - }; - const decoded = obj.decode({ - pit_id: undefined, - }); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it will fail to validate an incorrect search_after', () => { - const payload = 'foo'; - const decoded = searchAfterOrUndefined.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "foo" supplied to "(Array<string> | undefined)"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.ts deleted file mode 100644 index 5cef26cbb0c4..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const search_after = t.array(t.string); -export type SearchAfter = t.TypeOf<typeof search_after>; - -export const searchAfterOrUndefined = t.union([search_after, t.undefined]); -export type SearchAfterOrUndefined = t.TypeOf<typeof searchAfterOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/serializer/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/serializer/index.ts deleted file mode 100644 index 7eb585c068e2..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/serializer/index.ts +++ /dev/null @@ -1,15 +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 * as t from 'io-ts'; - -export const serializer = t.string; -export type Serializer = t.TypeOf<typeof serializer>; -export const serializerOrUndefined = t.union([serializer, t.undefined]); -export type SerializerOrUndefined = t.TypeOf<typeof serializerOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_field/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_field/index.ts deleted file mode 100644 index e2c61659a94e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_field/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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const sort_field = t.string; -export const sortFieldOrUndefined = t.union([sort_field, t.undefined]); -export type SortFieldOrUndefined = t.TypeOf<typeof sortFieldOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_order/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_order/index.ts deleted file mode 100644 index fb075ff63fee..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_order/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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const sort_order = t.keyof({ asc: null, desc: null }); -export const sortOrderOrUndefined = t.union([sort_order, t.undefined]); -export type SortOrderOrUndefined = t.TypeOf<typeof sortOrderOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/tags/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/tags/index.ts deleted file mode 100644 index ad99a1d0800f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/tags/index.ts +++ /dev/null @@ -1,17 +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 * as t from 'io-ts'; - -import { DefaultStringArray } from '@kbn/securitysolution-io-ts-types'; - -export const tags = DefaultStringArray; -export type Tags = t.TypeOf<typeof tags>; -export const tagsOrUndefined = t.union([tags, t.undefined]); -export type TagsOrUndefined = t.TypeOf<typeof tagsOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/tie_breaker_id/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/tie_breaker_id/index.ts deleted file mode 100644 index 0e393c7f154b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/tie_breaker_id/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const tie_breaker_id = t.string; // TODO: Use UUID for this instead of a string for validation diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/timestamp/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/timestamp/index.ts deleted file mode 100644 index ba47dd545fbb..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/timestamp/index.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", 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 * as t from 'io-ts'; -import { IsoDateString } from '@kbn/securitysolution-io-ts-types'; - -export const timestamp = IsoDateString; -export const timestampOrUndefined = t.union([IsoDateString, t.undefined]); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/total/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/total/index.ts deleted file mode 100644 index f3c84417cd41..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/total/index.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", 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 * as t from 'io-ts'; - -export const total = t.number; // TODO: Change this out for PositiveNumber from siem -export const totalUndefined = t.union([total, t.undefined]); -export type TotalOrUndefined = t.TypeOf<typeof totalUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.test.ts deleted file mode 100644 index 60156cda9fe6..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { Type, type } from '.'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('type', () => { - test('it will work with a given expected type', () => { - const payload: Type = 'keyword'; - const decoded = type.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it will give an error if given a type that does not exist', () => { - const payload: Type | 'madeup' = 'madeup'; - const decoded = type.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "madeup" supplied to ""binary" | "boolean" | "byte" | "date" | "date_nanos" | "date_range" | "double" | "double_range" | "float" | "float_range" | "geo_point" | "geo_shape" | "half_float" | "integer" | "integer_range" | "ip" | "ip_range" | "keyword" | "long" | "long_range" | "shape" | "short" | "text""', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.ts deleted file mode 100644 index d617fd2cccdf..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.ts +++ /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", 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 * as t from 'io-ts'; - -/** - * Types of all the regular single value list items but not exception list - * or exception list types. Those types are in the list_types folder. - */ -export const type = t.keyof({ - binary: null, - boolean: null, - byte: null, - date: null, - date_nanos: null, - date_range: null, - double: null, - double_range: null, - float: null, - float_range: null, - geo_point: null, - geo_shape: null, - half_float: null, - integer: null, - integer_range: null, - ip: null, - ip_range: null, - keyword: null, - long: null, - long_range: null, - shape: null, - short: null, - text: null, -}); - -export const typeOrUndefined = t.union([type, t.undefined]); -export type Type = t.TypeOf<typeof type>; -export type TypeOrUndefined = t.TypeOf<typeof typeOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/underscore_version/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/underscore_version/index.ts deleted file mode 100644 index 2e4901990fb5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/underscore_version/index.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", 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 * as t from 'io-ts'; - -export const _version = t.string; -export const _versionOrUndefined = t.union([_version, t.undefined]); -export type _VersionOrUndefined = t.TypeOf<typeof _versionOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.mock.ts deleted file mode 100644 index c84db08f8c93..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.mock.ts +++ /dev/null @@ -1,21 +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 { UpdateComment, UpdateCommentsArray } from '.'; -import { ID } from '../../constants/index.mock'; - -export const getUpdateCommentMock = (): UpdateComment => ({ - comment: 'some comment', - id: ID, -}); - -export const getUpdateCommentsArrayMock = (): UpdateCommentsArray => [ - getUpdateCommentMock(), - getUpdateCommentMock(), -]; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.test.ts deleted file mode 100644 index 88cdfbdd573e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.test.ts +++ /dev/null @@ -1,150 +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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { getUpdateCommentMock, getUpdateCommentsArrayMock } from './index.mock'; -import { - UpdateComment, - updateComment, - UpdateCommentsArray, - updateCommentsArray, - UpdateCommentsArrayOrUndefined, - updateCommentsArrayOrUndefined, -} from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('UpdateComment', () => { - describe('updateComment', () => { - test('it should pass validation when supplied typical comment update', () => { - const payload = getUpdateCommentMock(); - const decoded = updateComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation when supplied an undefined for "comment"', () => { - const payload = getUpdateCommentMock(); - // @ts-expect-error - delete payload.comment; - const decoded = updateComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "comment"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an empty string for "comment"', () => { - const payload = { ...getUpdateCommentMock(), comment: '' }; - const decoded = updateComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "comment"']); - expect(message.schema).toEqual({}); - }); - - test('it should pass validation when supplied an undefined for "id"', () => { - const payload = getUpdateCommentMock(); - delete payload.id; - const decoded = updateComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation when supplied an empty string for "id"', () => { - const payload = { ...getUpdateCommentMock(), id: '' }; - const decoded = updateComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "id"']); - expect(message.schema).toEqual({}); - }); - - test('it should strip out extra key passed in', () => { - const payload: UpdateComment & { - extraKey?: string; - } = { ...getUpdateCommentMock(), extraKey: 'some new value' }; - const decoded = updateComment.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getUpdateCommentMock()); - }); - }); - - describe('updateCommentsArray', () => { - test('it should pass validation when supplied an array of comments', () => { - const payload = getUpdateCommentsArrayMock(); - const decoded = updateCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation when undefined', () => { - const payload = undefined; - const decoded = updateCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when array includes non comments types', () => { - const payload = [1] as unknown as UpdateCommentsArray; - const decoded = updateCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', - ]); - expect(message.schema).toEqual({}); - }); - }); - - describe('updateCommentsArrayOrUndefined', () => { - test('it should pass validation when supplied an array of comments', () => { - const payload = getUpdateCommentsArrayMock(); - const decoded = updateCommentsArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should pass validation when supplied when undefined', () => { - const payload = undefined; - const decoded = updateCommentsArrayOrUndefined.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation when array includes non comments types', () => { - const payload = [1] as unknown as UpdateCommentsArrayOrUndefined; - const decoded = updateCommentsArray.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', - ]); - expect(message.schema).toEqual({}); - }); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.ts deleted file mode 100644 index 79a721c8dad5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.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", 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 * as t from 'io-ts'; -import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; -import { id } from '../id'; - -export const updateComment = t.intersection([ - t.exact( - t.type({ - comment: NonEmptyString, - }) - ), - t.exact( - t.partial({ - id, - }) - ), -]); - -export type UpdateComment = t.TypeOf<typeof updateComment>; -export const updateCommentsArray = t.array(updateComment); -export type UpdateCommentsArray = t.TypeOf<typeof updateCommentsArray>; -export const updateCommentsArrayOrUndefined = t.union([updateCommentsArray, t.undefined]); -export type UpdateCommentsArrayOrUndefined = t.TypeOf<typeof updateCommentsArrayOrUndefined>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_at/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_at/index.ts deleted file mode 100644 index a6c0a803e6db..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_at/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const updated_at = t.string; // TODO: Make this into an ISO Date string check diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_by/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_by/index.ts deleted file mode 100644 index b99396254c91..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_by/index.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", 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". - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as t from 'io-ts'; - -export const updated_by = t.string; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/value/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/value/index.ts deleted file mode 100644 index cad092aee4b7..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/value/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -export const value = t.string; -export const valueOrUndefined = t.union([value, t.undefined]); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.mock.ts deleted file mode 100644 index 4d09fc1efbeb..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.mock.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", 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 { EndpointEntriesArray } from '../common/endpoint/entries'; -import { EntriesArray, Entry } from '../common/entries'; -import { EntryMatch } from '../common/entry_match'; -import { EntryNested } from '../common/entry_nested'; -import { OsTypeArray } from '../common/os_type'; - -export const DATE_NOW = '2020-04-20T15:25:31.830Z'; -export const OLD_DATE_RELATIVE_TO_DATE_NOW = '2020-04-19T15:25:31.830Z'; -export const USER = 'some user'; -export const ELASTIC_USER = 'elastic'; -export const LIST_INDEX = '.lists'; -export const LIST_ITEM_INDEX = '.items'; -export const NAME = 'some name'; -export const DESCRIPTION = 'some description'; -export const LIST_ID = 'some-list-id'; -export const LIST_ITEM_ID = 'some-list-item-id'; -export const TIE_BREAKER = '6a76b69d-80df-4ab2-8c3e-85f466b06a0e'; -export const TIE_BREAKERS = [ - '21530991-4051-46ec-bc35-2afa09a1b0b5', - '3c662054-ae37-4aa9-9936-3e8e2ea26775', - '60e49a20-3a23-48b6-8bf9-ed5e3b70f7a0', - '38814080-a40f-4358-992a-3b875f9b7dec', - '29fa61be-aaaf-411c-a78a-7059e3f723f1', - '9c19c959-cb9d-4cd2-99e4-1ea2baf0ef0e', - 'd409308c-f94b-4b3a-8234-bbd7a80c9140', - '87824c99-cd83-45c4-8aa6-4ad95dfea62c', - '7b940c17-9355-479f-b882-f3e575718f79', - '5983ad0c-4ef4-4fa0-8308-80ab9ecc4f74', -]; -export const META = {}; -export const TYPE = 'ip'; -export const VALUE = '127.0.0.1'; -export const VALUE_2 = '255.255.255'; -export const NAMESPACE_TYPE = 'single'; -export const NESTED_FIELD = 'parent.field'; - -// Exception List specific -export const ID = 'uuid_here'; -export const ITEM_ID = 'some-list-item-id'; -export const DETECTION_TYPE = 'detection'; -export const ENDPOINT_TYPE = 'endpoint'; -export const FIELD = 'host.name'; -export const OPERATOR = 'included'; -export const OPERATOR_EXCLUDED = 'excluded'; -export const ENTRY_VALUE = 'some host name'; -export const MATCH = 'match'; -export const MATCH_ANY = 'match_any'; -export const WILDCARD = 'wildcard'; -export const MAX_IMPORT_PAYLOAD_BYTES = 9000000; -export const IMPORT_BUFFER_SIZE = 1000; -export const LIST = 'list'; -export const EXISTS = 'exists'; -export const NESTED = 'nested'; -export const ENTRIES: EntriesArray = [ - { - entries: [{ field: 'nested.field', operator: 'included', type: 'match', value: 'some value' }], - field: 'some.parentField', - type: 'nested', - }, - { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, -]; -export const ENDPOINT_ENTRIES: EndpointEntriesArray = [ - { - entries: [{ field: 'nested.field', operator: 'included', type: 'match', value: 'some value' }], - field: 'some.parentField', - type: 'nested', - }, - { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, -]; -// ENTRIES_WITH_IDS should only be used to mock out functionality of a collection of transforms -// that are UI specific and useful for UI concerns that are inserted between the -// API and the actual user interface. In some ways these might be viewed as -// technical debt or to compensate for the differences and preferences -// of how ReactJS might prefer data vs. how we want to model data. -export const ENTRIES_WITH_IDS: EntriesArray = [ - { - entries: [ - { - field: 'nested.field', - id: '123', - operator: 'included', - type: 'match', - value: 'some value', - } as EntryMatch & { id: string }, - ], - field: 'some.parentField', - id: '123', - type: 'nested', - } as EntryNested & { id: string }, - { - field: 'some.not.nested.field', - id: '123', - operator: 'included', - type: 'match', - value: 'some value', - } as Entry & { id: string }, -]; -export const ITEM_TYPE = 'simple'; -export const OS_TYPES: OsTypeArray = ['windows']; -export const TAGS = []; -export const COMMENTS = []; -export const FILTER = 'name:Nicolas Bourbaki'; -export const CURSOR = 'c29tZXN0cmluZ2ZvcnlvdQ=='; -export const _VERSION = 'WzI5NywxXQ=='; -export const VERSION = 1; -export const IMMUTABLE = false; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.mock.ts deleted file mode 100644 index db83807b0271..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.mock.ts +++ /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 { CreateEndpointListItemSchema } from '.'; -import { - COMMENTS, - DESCRIPTION, - ENDPOINT_ENTRIES, - ITEM_TYPE, - META, - NAME, - OS_TYPES, - TAGS, -} from '../../constants/index.mock'; - -export const getCreateEndpointListItemSchemaMock = (): CreateEndpointListItemSchema => ({ - comments: COMMENTS, - description: DESCRIPTION, - entries: ENDPOINT_ENTRIES, - item_id: undefined, - meta: META, - name: NAME, - os_types: OS_TYPES, - tags: TAGS, - type: ITEM_TYPE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.test.ts deleted file mode 100644 index 9dc599a75ad5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.test.ts +++ /dev/null @@ -1,207 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getCreateEndpointListItemSchemaMock } from './index.mock'; -import { CreateEndpointListItemSchema, createEndpointListItemSchema } from '.'; -import { getCreateCommentsArrayMock } from '../../common/create_comment/index.mock'; -import { getCommentsMock } from '../../common/comment/index.mock'; -import { CommentsArray } from '../../common/comment'; - -describe('create_endpoint_list_item_schema', () => { - test('it should pass validation when supplied a typical list item request not counting the auto generated uuid', () => { - const payload = getCreateEndpointListItemSchemaMock(); - const decoded = createEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateEndpointListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation when supplied an undefined for "description"', () => { - const payload = getCreateEndpointListItemSchemaMock(); - // @ts-expect-error - delete payload.description; - const decoded = createEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an undefined for "name"', () => { - const payload = getCreateEndpointListItemSchemaMock(); - // @ts-expect-error - delete payload.name; - const decoded = createEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "name"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an undefined for "type"', () => { - const payload = getCreateEndpointListItemSchemaMock(); - // @ts-expect-error - delete payload.type; - const decoded = createEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied a "list_id" since it does not required one', () => { - const inputPayload: CreateEndpointListItemSchema & { list_id: string } = { - ...getCreateEndpointListItemSchemaMock(), - list_id: 'list-123', - }; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "list_id"']); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied a "namespace_type" since it does not required one', () => { - const inputPayload: CreateEndpointListItemSchema & { namespace_type: string } = { - ...getCreateEndpointListItemSchemaMock(), - namespace_type: 'single', - }; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "namespace_type"']); - expect(message.schema).toEqual({}); - }); - - test('it should pass validation when supplied an undefined for "meta" but strip it out and generate a correct body not counting the auto generated uuid', () => { - const payload = getCreateEndpointListItemSchemaMock(); - const outputPayload = getCreateEndpointListItemSchemaMock(); - delete payload.meta; - delete outputPayload.meta; - const decoded = createEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateEndpointListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should pass validation when supplied an undefined for "comments" but return an array and generate a correct body not counting the auto generated uuid', () => { - const inputPayload = getCreateEndpointListItemSchemaMock(); - const outputPayload = getCreateEndpointListItemSchemaMock(); - delete inputPayload.comments; - outputPayload.comments = []; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateEndpointListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should pass validation when supplied "comments" array', () => { - const inputPayload = { - ...getCreateEndpointListItemSchemaMock(), - comments: getCreateCommentsArrayMock(), - }; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateEndpointListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(inputPayload); - }); - - test('it should fail validation when supplied "comments" with "created_at", "created_by", or "id" values', () => { - const inputPayload: Omit<CreateEndpointListItemSchema, 'comments'> & { - comments?: CommentsArray; - } = { - ...getCreateEndpointListItemSchemaMock(), - comments: [getCommentsMock()], - }; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "created_at,created_by,id"']); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an undefined for "entries"', () => { - const inputPayload = getCreateEndpointListItemSchemaMock(); - const outputPayload = getCreateEndpointListItemSchemaMock(); - // @ts-expect-error - delete inputPayload.entries; - outputPayload.entries = []; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateEndpointListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should pass validation when supplied an undefined for "tags" but return an array and generate a correct body not counting the auto generated uuid', () => { - const inputPayload = getCreateEndpointListItemSchemaMock(); - const outputPayload = getCreateEndpointListItemSchemaMock(); - delete inputPayload.tags; - outputPayload.tags = []; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateEndpointListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => { - const inputPayload = getCreateEndpointListItemSchemaMock(); - delete inputPayload.item_id; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect((message.schema as CreateEndpointListItemSchema).item_id).toMatch( - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i - ); - }); - - test('it should pass validation when supplied an undefined for "item_id" and generate a correct body not counting the uuid', () => { - const inputPayload = getCreateEndpointListItemSchemaMock(); - delete inputPayload.item_id; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateEndpointListItemSchema).item_id; - expect(message.schema).toEqual(inputPayload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: CreateEndpointListItemSchema & { - extraKey: string; - } = { ...getCreateEndpointListItemSchemaMock(), extraKey: 'some new value' }; - const decoded = createEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.ts deleted file mode 100644 index f8e3965df733..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.ts +++ /dev/null @@ -1,59 +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 * as t from 'io-ts'; - -import { DefaultUuid } from '@kbn/securitysolution-io-ts-types'; -import { nonEmptyEndpointEntriesArray } from '../../common/endpoint/entries'; -import { exceptionListItemType } from '../../common/exception_list_item_type'; -import { DefaultCreateCommentsArray } from '../../common/default_create_comments_array'; -import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { CreateCommentsArray } from '../../common/create_comment'; -import { Tags } from '../../common/tags'; -import { ItemId } from '../../common/item_id'; -import { EntriesArray } from '../../common/entries'; -import { description } from '../../common/description'; -import { name } from '../../common/name'; -import { meta } from '../../common/meta'; -import { tags } from '../../common/tags'; - -export const createEndpointListItemSchema = t.intersection([ - t.exact( - t.type({ - description, - entries: nonEmptyEndpointEntriesArray, - name, - type: exceptionListItemType, - }) - ), - t.exact( - t.partial({ - comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode - item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode - meta, // defaults to undefined if not set during decode - os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode - tags, // defaults to empty array if not set during decode - }) - ), -]); - -export type CreateEndpointListItemSchema = t.OutputOf<typeof createEndpointListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type CreateEndpointListItemSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof createEndpointListItemSchema>>, - 'tags' | 'item_id' | 'entries' | 'comments' | 'os_types' -> & { - comments: CreateCommentsArray; - tags: Tags; - item_id: ItemId; - entries: EntriesArray; - os_types: OsTypeArray; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.mock.ts deleted file mode 100644 index 23b3eb19171d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.mock.ts +++ /dev/null @@ -1,63 +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 { CreateExceptionListItemSchema } from '.'; -import { - COMMENTS, - DESCRIPTION, - ENTRIES, - ITEM_ID, - ITEM_TYPE, - LIST_ID, - META, - NAME, - NAMESPACE_TYPE, - OS_TYPES, - TAGS, -} from '../../constants/index.mock'; - -export const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemSchema => ({ - comments: COMMENTS, - description: DESCRIPTION, - entries: ENTRIES, - item_id: undefined, - list_id: LIST_ID, - meta: META, - name: NAME, - namespace_type: NAMESPACE_TYPE, - os_types: OS_TYPES, - tags: TAGS, - type: ITEM_TYPE, -}); - -/** - * Useful for end to end testing - */ -export const getCreateExceptionListItemMinimalSchemaMock = (): CreateExceptionListItemSchema => ({ - description: DESCRIPTION, - entries: ENTRIES, - item_id: ITEM_ID, - list_id: LIST_ID, - name: NAME, - os_types: OS_TYPES, - type: ITEM_TYPE, -}); - -/** - * Useful for end to end testing - */ -export const getCreateExceptionListItemMinimalSchemaMockWithoutId = - (): CreateExceptionListItemSchema => ({ - description: DESCRIPTION, - entries: ENTRIES, - list_id: LIST_ID, - name: NAME, - os_types: OS_TYPES, - type: ITEM_TYPE, - }); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.test.ts deleted file mode 100644 index dc5dcb63cf1b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.test.ts +++ /dev/null @@ -1,211 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getCreateExceptionListItemSchemaMock } from './index.mock'; -import { CreateExceptionListItemSchema, createExceptionListItemSchema } from '.'; -import { getCreateCommentsArrayMock } from '../../common/create_comment/index.mock'; -import { getCommentsMock } from '../../common/comment/index.mock'; -import { CommentsArray } from '../../common/comment'; - -describe('create_exception_list_item_schema', () => { - test('it should pass validation when supplied a typical exception list item request not counting the auto generated uuid', () => { - const payload = getCreateExceptionListItemSchemaMock(); - const decoded = createExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should fail validation when supplied an undefined for "description"', () => { - const payload = getCreateExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.description; - const decoded = createExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an undefined for "name"', () => { - const payload = getCreateExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.name; - const decoded = createExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "name"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an undefined for "type"', () => { - const payload = getCreateExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.type; - const decoded = createExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an undefined for "list_id"', () => { - const inputPayload = getCreateExceptionListItemSchemaMock(); - // @ts-expect-error - delete inputPayload.list_id; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should pass validation when supplied an undefined for "meta" but strip it out and generate a correct body not counting the auto generated uuid', () => { - const payload = getCreateExceptionListItemSchemaMock(); - const outputPayload = getCreateExceptionListItemSchemaMock(); - delete payload.meta; - delete outputPayload.meta; - const decoded = createExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should pass validation when supplied an undefined for "comments" but return an array and generate a correct body not counting the auto generated uuid', () => { - const inputPayload = getCreateExceptionListItemSchemaMock(); - const outputPayload = getCreateExceptionListItemSchemaMock(); - delete inputPayload.comments; - outputPayload.comments = []; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should pass validation when supplied "comments" array', () => { - const inputPayload = { - ...getCreateExceptionListItemSchemaMock(), - comments: getCreateCommentsArrayMock(), - }; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(inputPayload); - }); - - test('it should fail validation when supplied "comments" with "created_at" or "created_by" values', () => { - const inputPayload: Omit<CreateExceptionListItemSchema, 'comments'> & { - comments?: CommentsArray; - } = { - ...getCreateExceptionListItemSchemaMock(), - comments: [getCommentsMock()], - }; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "created_at,created_by,id"']); - expect(message.schema).toEqual({}); - }); - - test('it should fail validation when supplied an undefined for "entries"', () => { - const inputPayload = getCreateExceptionListItemSchemaMock(); - const outputPayload = getCreateExceptionListItemSchemaMock(); - // @ts-expect-error - delete inputPayload.entries; - outputPayload.entries = []; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should pass validation when supplied an undefined for "namespace_type" but return enum "single" and generate a correct body not counting the auto generated uuid', () => { - const inputPayload = getCreateExceptionListItemSchemaMock(); - const outputPayload = getCreateExceptionListItemSchemaMock(); - delete inputPayload.namespace_type; - outputPayload.namespace_type = 'single'; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should pass validation when supplied an undefined for "tags" but return an array and generate a correct body not counting the auto generated uuid', () => { - const inputPayload = getCreateExceptionListItemSchemaMock(); - const outputPayload = getCreateExceptionListItemSchemaMock(); - delete inputPayload.tags; - outputPayload.tags = []; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => { - const inputPayload = getCreateExceptionListItemSchemaMock(); - delete inputPayload.item_id; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect((message.schema as CreateExceptionListItemSchema).item_id).toMatch( - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i - ); - }); - - test('it should pass validation when supplied an undefined for "item_id" and generate a correct body not counting the uuid', () => { - const inputPayload = getCreateExceptionListItemSchemaMock(); - delete inputPayload.item_id; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListItemSchema).item_id; - expect(message.schema).toEqual(inputPayload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: CreateExceptionListItemSchema & { - extraKey?: string; - } = getCreateExceptionListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = createExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.ts deleted file mode 100644 index 8445b2d03d67..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.ts +++ /dev/null @@ -1,68 +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 * as t from 'io-ts'; -import { DefaultUuid } from '@kbn/securitysolution-io-ts-types'; - -import { DefaultCreateCommentsArray } from '../../common/default_create_comments_array'; -import { CreateCommentsArray } from '../../common/create_comment'; -import { Tags } from '../../common/tags'; -import { ItemId } from '../../common/item_id'; -import { EntriesArray } from '../../common/entries'; -import { NamespaceType } from '../../common/default_namespace'; -import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { description } from '../../common/description'; -import { list_id } from '../../common/list_id'; -import { name } from '../../common/name'; -import { exceptionListItemType } from '../../common/exception_list_item_type'; -import { meta } from '../../common/meta'; -import { namespace_type } from '../../common/namespace_type'; -import { tags } from '../../common/tags'; -import { nonEmptyEntriesArray } from '../../common/non_empty_entries_array'; -import { ExpireTimeOrUndefined, expireTimeOrUndefined } from '../../common'; - -export const createExceptionListItemSchema = t.intersection([ - t.exact( - t.type({ - description, - entries: nonEmptyEntriesArray, - list_id, - name, - type: exceptionListItemType, - }) - ), - t.exact( - t.partial({ - comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode - expire_time: expireTimeOrUndefined, - item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode - meta, // defaults to undefined if not set during decode - namespace_type, // defaults to 'single' if not set during decode - os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode - tags, // defaults to empty array if not set during decode - }) - ), -]); - -export type CreateExceptionListItemSchema = t.OutputOf<typeof createExceptionListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type CreateExceptionListItemSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof createExceptionListItemSchema>>, - 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' | 'expire_time' -> & { - comments: CreateCommentsArray; - expire_time: ExpireTimeOrUndefined; - tags: Tags; - item_id: ItemId; - entries: EntriesArray; - namespace_type: NamespaceType; - os_types: OsTypeArray; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.mock.ts deleted file mode 100644 index 798d36783d7c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.mock.ts +++ /dev/null @@ -1,61 +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 { - DESCRIPTION, - ENDPOINT_TYPE, - LIST_ID, - META, - NAME, - NAMESPACE_TYPE, - VERSION, -} from '../../constants/index.mock'; - -import { CreateExceptionListSchema } from '.'; - -export const getCreateExceptionListSchemaMock = (): CreateExceptionListSchema => ({ - description: DESCRIPTION, - list_id: undefined, - meta: META, - name: NAME, - namespace_type: NAMESPACE_TYPE, - os_types: [], - tags: [], - type: ENDPOINT_TYPE, - version: VERSION, -}); - -/** - * Useful for end to end testing - */ -export const getCreateExceptionListMinimalSchemaMock = (): CreateExceptionListSchema => ({ - description: DESCRIPTION, - list_id: LIST_ID, - name: NAME, - type: ENDPOINT_TYPE, -}); - -/** - * Useful for end to end testing - */ -export const getCreateExceptionListMinimalSchemaMockWithoutId = (): CreateExceptionListSchema => ({ - description: DESCRIPTION, - name: NAME, - type: ENDPOINT_TYPE, -}); - -/** - * Useful for end to end testing with detections - */ -export const getCreateExceptionListDetectionSchemaMock = (): CreateExceptionListSchema => ({ - description: DESCRIPTION, - list_id: LIST_ID, - name: NAME, - type: 'detection', -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.test.ts deleted file mode 100644 index f6d164f8786b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.test.ts +++ /dev/null @@ -1,85 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { CreateExceptionListSchema, createExceptionListSchema } from '.'; -import { getCreateExceptionListSchemaMock } from './index.mock'; - -describe('create_exception_list_schema', () => { - test('it should validate a typical exception lists request and generate a correct body not counting the uuid', () => { - const payload = getCreateExceptionListSchemaMock(); - const decoded = createExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListSchema).list_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "meta" and generate a correct body not counting the uuid', () => { - const payload = getCreateExceptionListSchemaMock(); - delete payload.meta; - const decoded = createExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListSchema).list_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "tags" but return an array and generate a correct body not counting the uuid', () => { - const inputPayload = getCreateExceptionListSchemaMock(); - const outputPayload = getCreateExceptionListSchemaMock(); - delete inputPayload.tags; - outputPayload.tags = []; - const decoded = createExceptionListSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListSchema).list_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should accept an undefined for "list_id" and auto generate a uuid', () => { - const inputPayload = getCreateExceptionListSchemaMock(); - delete inputPayload.list_id; - const decoded = createExceptionListSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect((message.schema as CreateExceptionListSchema).list_id).toMatch( - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i - ); - }); - - test('it should accept an undefined for "list_id" and generate a correct body not counting the uuid', () => { - const inputPayload = getCreateExceptionListSchemaMock(); - delete inputPayload.list_id; - const decoded = createExceptionListSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListSchema).list_id; - expect(message.schema).toEqual(inputPayload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: CreateExceptionListSchema & { - extraKey?: string; - } = getCreateExceptionListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = createExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.ts deleted file mode 100644 index 786e39a61864..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.ts +++ /dev/null @@ -1,61 +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 * as t from 'io-ts'; -import { - DefaultUuid, - DefaultVersionNumber, - DefaultVersionNumberDecoded, -} from '@kbn/securitysolution-io-ts-types'; - -import { exceptionListType } from '../../common/exception_list'; -import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { Tags } from '../../common/tags'; -import { ListId } from '../../common/list_id'; -import { NamespaceType } from '../../common/default_namespace'; -import { name } from '../../common/name'; -import { description } from '../../common/description'; -import { namespace_type } from '../../common/namespace_type'; -import { tags } from '../../common/tags'; -import { meta } from '../../common/meta'; - -export const createExceptionListSchema = t.intersection([ - t.exact( - t.type({ - description, - name, - type: exceptionListType, - }) - ), - t.exact( - t.partial({ - list_id: DefaultUuid, // defaults to a GUID (UUID v4) string if not set during decode - meta, // defaults to undefined if not set during decode - namespace_type, // defaults to 'single' if not set during decode - os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode - tags, // defaults to empty array if not set during decode - version: DefaultVersionNumber, // defaults to numerical 1 if not set during decode - }) - ), -]); - -export type CreateExceptionListSchema = t.OutputOf<typeof createExceptionListSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type CreateExceptionListSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof createExceptionListSchema>>, - 'tags' | 'list_id' | 'namespace_type' | 'os_types' -> & { - tags: Tags; - list_id: ListId; - namespace_type: NamespaceType; - os_types: OsTypeArray; - version: DefaultVersionNumberDecoded; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.mock.ts deleted file mode 100644 index ed9a77fb859a..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.mock.ts +++ /dev/null @@ -1,36 +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 { LIST_ID, LIST_ITEM_ID, META, VALUE } from '../../constants/index.mock'; - -import { CreateListItemSchema } from '.'; - -export const getCreateListItemSchemaMock = (): CreateListItemSchema => ({ - id: LIST_ITEM_ID, - list_id: LIST_ID, - meta: META, - value: VALUE, -}); - -/** - * Useful for end to end testing - */ -export const getCreateMinimalListItemSchemaMock = (): CreateListItemSchema => ({ - id: LIST_ITEM_ID, - list_id: LIST_ID, - value: VALUE, -}); - -/** - * Useful for end to end testing - */ -export const getCreateMinimalListItemSchemaMockWithoutId = (): CreateListItemSchema => ({ - list_id: LIST_ID, - value: VALUE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.test.ts deleted file mode 100644 index 3e991933b664..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.test.ts +++ /dev/null @@ -1,59 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getCreateListItemSchemaMock } from './index.mock'; -import { CreateListItemSchema, createListItemSchema } from '.'; - -describe('create_list_item_schema', () => { - test('it should validate a typical list item request', () => { - const payload = getCreateListItemSchemaMock(); - const decoded = createListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for an id', () => { - const payload = getCreateListItemSchemaMock(); - delete payload.id; - const decoded = createListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for meta', () => { - const payload = getCreateListItemSchemaMock(); - delete payload.meta; - const decoded = createListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: CreateListItemSchema & { extraKey?: string } = getCreateListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = createListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.ts deleted file mode 100644 index f1b3c6586535..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.ts +++ /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 * as t from 'io-ts'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { list_id } from '../../common/list_id'; -import { value } from '../../common/value'; -import { id } from '../../common/id'; -import { meta } from '../../common/meta'; -import { refreshWithWaitFor } from '../../common/refresh'; - -export const createListItemSchema = t.intersection([ - t.exact( - t.type({ - list_id, - value, - }) - ), - t.exact(t.partial({ id, meta, refresh: refreshWithWaitFor })), -]); - -export type CreateListItemSchema = t.OutputOf<typeof createListItemSchema>; -export type CreateListItemSchemaDecoded = RequiredKeepUndefined< - t.TypeOf<typeof createListItemSchema> ->; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.mock.ts deleted file mode 100644 index bbdd927a58e7..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.mock.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", 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 { DESCRIPTION, LIST_ID, META, NAME, TYPE, VERSION } from '../../constants/index.mock'; - -import { CreateListSchema } from '.'; - -export const getCreateListSchemaMock = (): CreateListSchema => ({ - description: DESCRIPTION, - deserializer: undefined, - id: LIST_ID, - meta: META, - name: NAME, - serializer: undefined, - type: TYPE, - version: VERSION, -}); - -/** - * Useful for end to end tests and other mechanisms which want to fill in the values - */ -export const getCreateMinimalListSchemaMock = (): CreateListSchema => ({ - description: DESCRIPTION, - id: LIST_ID, - name: NAME, - type: TYPE, -}); - -/** - * Useful for end to end tests and other mechanisms which want to fill in the values - */ -export const getCreateMinimalListSchemaMockWithoutId = (): CreateListSchema => ({ - description: DESCRIPTION, - name: NAME, - type: TYPE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.test.ts deleted file mode 100644 index 0eb9ecd02505..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.test.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", 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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { CreateListSchema, createListSchema } from '.'; -import { getCreateListSchemaMock } from './index.mock'; - -describe('create_list_schema', () => { - test('it should validate a typical lists request', () => { - const payload = getCreateListSchemaMock(); - const decoded = createListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for an id', () => { - const payload = getCreateListSchemaMock(); - delete payload.id; - const decoded = createListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for meta', () => { - const payload = getCreateListSchemaMock(); - delete payload.meta; - const decoded = createListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for serializer', () => { - const payload = getCreateListSchemaMock(); - delete payload.serializer; - const decoded = createListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for deserializer', () => { - const payload = getCreateListSchemaMock(); - delete payload.deserializer; - const decoded = createListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: CreateListSchema & { extraKey?: string } = getCreateListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = createListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.ts deleted file mode 100644 index c6e210a999ea..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { - DefaultVersionNumber, - DefaultVersionNumberDecoded, -} from '@kbn/securitysolution-io-ts-types'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { name } from '../../common/name'; -import { description } from '../../common/description'; -import { type } from '../../common/type'; -import { deserializer } from '../../common/deserializer'; -import { id } from '../../common/id'; -import { meta } from '../../common/meta'; -import { serializer } from '../../common/serializer'; - -export const createListSchema = t.intersection([ - t.exact( - t.type({ - description, - name, - type, - }) - ), - t.exact( - t.partial({ - deserializer, // defaults to undefined if not set during decode - id, // defaults to undefined if not set during decode - meta, // defaults to undefined if not set during decode - serializer, // defaults to undefined if not set during decode - version: DefaultVersionNumber, // defaults to a numerical 1 if not set during decode - }) - ), -]); - -export type CreateListSchema = t.OutputOf<typeof createListSchema>; -export type CreateListSchemaDecoded = RequiredKeepUndefined< - Omit<t.TypeOf<typeof createListSchema>, 'version'> -> & { version: DefaultVersionNumberDecoded }; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.test.ts deleted file mode 100644 index 1eda590adb5d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.test.ts +++ /dev/null @@ -1,127 +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 { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { CreateRuleExceptionListItemSchema, createRuleExceptionListItemSchema } from '.'; -import { CreateExceptionListItemSchema } from '../create_exception_list_item_schema'; - -const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemSchema => ({ - comments: [], - description: 'some description', - entries: [ - { - field: 'host.name', - operator: 'included', - type: 'match_any', - value: ['foo', 'bar'], - }, - ], - item_id: undefined, - list_id: 'some-list-id', - name: 'some name', - namespace_type: 'single', - os_types: [], - tags: [], - type: 'simple', -}); - -describe('createRuleExceptionListItemSchema', () => { - test('empty objects do not validate', () => { - const payload = {} as CreateRuleExceptionListItemSchema; - - const decoded = createRuleExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - 'Invalid value "undefined" supplied to "entries"', - 'Invalid value "undefined" supplied to "name"', - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('items without list_id validate', () => { - const payload: CreateRuleExceptionListItemSchema = { - description: 'Exception item for rule default exception list', - entries: [ - { - field: 'some.not.nested.field', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - name: 'Sample exception item', - type: 'simple', - }; - - const decoded = createRuleExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual( - expect.objectContaining({ - comments: [], - description: 'Exception item for rule default exception list', - entries: [ - { - field: 'some.not.nested.field', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - name: 'Sample exception item', - os_types: [], - tags: [], - type: 'simple', - }) - ); - }); - - test('items with list_id do not validate', () => { - const payload = - getCreateExceptionListItemSchemaMock() as unknown as CreateRuleExceptionListItemSchema; - - const decoded = createRuleExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "some-list-id" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('made up parameters do not validate', () => { - const payload: Partial<CreateRuleExceptionListItemSchema> & { madeUp: string } = { - description: 'Exception item for rule default exception list', - entries: [ - { - field: 'some.not.nested.field', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - name: 'Sample exception item', - type: 'simple', - madeUp: 'invalid value', - }; - - const decoded = createRuleExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "madeUp"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.ts deleted file mode 100644 index 1e4a254f92b8..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.ts +++ /dev/null @@ -1,73 +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 * as t from 'io-ts'; -import { DefaultUuid } from '@kbn/securitysolution-io-ts-types'; - -import { - CreateCommentsArray, - DefaultCreateCommentsArray, - description, - EntriesArray, - exceptionListItemType, - ItemId, - meta, - NamespaceType, - namespaceType, - nonEmptyEntriesArray, - OsTypeArray, - osTypeArrayOrUndefined, - Tags, - tags, - name, - ExpireTimeOrUndefined, - expireTimeOrUndefined, -} from '../../common'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; - -export const createRuleExceptionListItemSchema = t.intersection([ - t.exact( - t.type({ - description, - entries: nonEmptyEntriesArray, - name, - type: exceptionListItemType, - }) - ), - t.exact( - t.partial({ - comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode - item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode - list_id: t.undefined, - meta, // defaults to undefined if not set during decode - namespace_type: namespaceType, // defaults to 'single' if not set during decode - os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode - tags, // defaults to empty array if not set during decode - expire_time: expireTimeOrUndefined, - }) - ), -]); - -export type CreateRuleExceptionListItemSchema = t.OutputOf< - typeof createRuleExceptionListItemSchema ->; - -// This type is used after a decode since some things are defaults after a decode. -export type CreateRuleExceptionListItemSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof createRuleExceptionListItemSchema>>, - 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' | 'expire_time' -> & { - comments: CreateCommentsArray; - tags: Tags; - item_id: ItemId; - entries: EntriesArray; - namespace_type: NamespaceType; - os_types: OsTypeArray; - expire_time: ExpireTimeOrUndefined; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.mock.ts deleted file mode 100644 index 2f7963171197..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.mock.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", 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 { ID } from '../../constants/index.mock'; - -import { DeleteEndpointListItemSchema } from '.'; - -export const getDeleteEndpointListItemSchemaMock = (): DeleteEndpointListItemSchema => ({ - id: ID, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.test.ts deleted file mode 100644 index d702632713a5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { DeleteEndpointListItemSchema, deleteEndpointListItemSchema } from '.'; -import { getDeleteEndpointListItemSchemaMock } from './index.mock'; - -describe('delete_endpoint_list_item_schema', () => { - test('it should validate a typical endpoint list item request', () => { - const payload = getDeleteEndpointListItemSchemaMock(); - const decoded = deleteEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept a value for "namespace_type" since it does not require one', () => { - const payload: DeleteEndpointListItemSchema & { - namespace_type: string; - } = { ...getDeleteEndpointListItemSchemaMock(), namespace_type: 'single' }; - // @ts-expect-error - delete payload.namespace_type; - const decoded = deleteEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getDeleteEndpointListItemSchemaMock()); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: DeleteEndpointListItemSchema & { - extraKey?: string; - } = { ...getDeleteEndpointListItemSchemaMock(), extraKey: 'some new value' }; - const decoded = deleteEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.ts deleted file mode 100644 index f968b1410a25..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -import { id } from '../../common/id'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { item_id } from '../../common/item_id'; - -export const deleteEndpointListItemSchema = t.exact( - t.partial({ - id, - item_id, - }) -); - -export type DeleteEndpointListItemSchema = t.OutputOf<typeof deleteEndpointListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type DeleteEndpointListItemSchemaDecoded = RequiredKeepUndefined< - t.TypeOf<typeof deleteEndpointListItemSchema> ->; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.mock.ts deleted file mode 100644 index 723b5acb6620..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.mock.ts +++ /dev/null @@ -1,17 +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 { ID, NAMESPACE_TYPE } from '../../constants/index.mock'; - -import { DeleteExceptionListItemSchema } from '.'; - -export const getDeleteExceptionListItemSchemaMock = (): DeleteExceptionListItemSchema => ({ - id: ID, - namespace_type: NAMESPACE_TYPE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.test.ts deleted file mode 100644 index 375fc20ba4da..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.test.ts +++ /dev/null @@ -1,49 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { DeleteExceptionListItemSchema, deleteExceptionListItemSchema } from '.'; -import { getDeleteExceptionListItemSchemaMock } from './index.mock'; - -describe('delete_exception_list_item_schema', () => { - test('it should validate a typical exception list item request', () => { - const payload = getDeleteExceptionListItemSchemaMock(); - const decoded = deleteExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "namespace_type" but default to "single"', () => { - const payload = getDeleteExceptionListItemSchemaMock(); - delete payload.namespace_type; - const decoded = deleteExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getDeleteExceptionListItemSchemaMock()); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: DeleteExceptionListItemSchema & { - extraKey?: string; - } = getDeleteExceptionListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = deleteExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.ts deleted file mode 100644 index 4d961aa9e1a5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.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", 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 * as t from 'io-ts'; - -import { NamespaceType } from '../../common/default_namespace'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; -import { item_id } from '../../common/item_id'; -import { namespace_type } from '../../common/namespace_type'; - -export const deleteExceptionListItemSchema = t.exact( - t.partial({ - id, - item_id, - namespace_type, // defaults to 'single' if not set during decode - }) -); - -export type DeleteExceptionListItemSchema = t.OutputOf<typeof deleteExceptionListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type DeleteExceptionListItemSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof deleteExceptionListItemSchema>>, - 'namespace_type' -> & { - namespace_type: NamespaceType; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.mock.ts deleted file mode 100644 index f41f5b288004..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.mock.ts +++ /dev/null @@ -1,17 +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 { ID, NAMESPACE_TYPE } from '../../constants/index.mock'; - -import { DeleteExceptionListSchema } from '.'; - -export const getDeleteExceptionListSchemaMock = (): DeleteExceptionListSchema => ({ - id: ID, - namespace_type: NAMESPACE_TYPE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.test.ts deleted file mode 100644 index 5507f855d783..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.test.ts +++ /dev/null @@ -1,49 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { DeleteExceptionListSchema, deleteExceptionListSchema } from '.'; -import { getDeleteExceptionListSchemaMock } from './index.mock'; - -describe('delete_exception_list_schema', () => { - test('it should validate a typical exception list request', () => { - const payload = getDeleteExceptionListSchemaMock(); - const decoded = deleteExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "namespace_type" but default to "single"', () => { - const payload = getDeleteExceptionListSchemaMock(); - delete payload.namespace_type; - const decoded = deleteExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getDeleteExceptionListSchemaMock()); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: DeleteExceptionListSchema & { - extraKey?: string; - } = getDeleteExceptionListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = deleteExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.ts deleted file mode 100644 index 62855c9dc212..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.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", 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 * as t from 'io-ts'; - -import { NamespaceType } from '../../common/default_namespace'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; -import { list_id } from '../../common/list_id'; -import { namespace_type } from '../../common/namespace_type'; - -export const deleteExceptionListSchema = t.exact( - t.partial({ - id, - list_id, - namespace_type, // defaults to 'single' if not set during decode - }) -); - -export type DeleteExceptionListSchema = t.OutputOf<typeof deleteExceptionListSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type DeleteExceptionListSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof deleteExceptionListSchema>>, - 'namespace_type' -> & { - namespace_type: NamespaceType; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.mock.ts deleted file mode 100644 index aaa03621d676..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.mock.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", 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 { ID, LIST_ID, VALUE } from '../../constants/index.mock'; - -import { DeleteListItemSchema } from '.'; - -export const getDeleteListItemSchemaMock = (): DeleteListItemSchema => ({ - id: ID, - list_id: LIST_ID, - value: VALUE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.test.ts deleted file mode 100644 index d838fbab4f16..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.test.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", 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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { DeleteListItemSchema, deleteListItemSchema } from '.'; -import { getDeleteListItemSchemaMock } from './index.mock'; - -describe('delete_list_item_schema', () => { - test('it should validate a typical list item request', () => { - const payload = getDeleteListItemSchemaMock(); - const decoded = deleteListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: DeleteListItemSchema & { - extraKey?: string; - } = { ...getDeleteListItemSchemaMock(), extraKey: 'some new value' }; - const decoded = deleteListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.ts deleted file mode 100644 index bbf0a09a15c8..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.ts +++ /dev/null @@ -1,30 +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 * as t from 'io-ts'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; -import { list_id } from '../../common/list_id'; -import { valueOrUndefined } from '../../common/value'; -import { refresh } from '../../common/refresh'; - -export const deleteListItemSchema = t.intersection([ - t.exact( - t.type({ - value: valueOrUndefined, - }) - ), - t.exact(t.partial({ id, list_id, refresh })), -]); - -export type DeleteListItemSchema = t.OutputOf<typeof deleteListItemSchema>; -export type DeleteListItemSchemaDecoded = RequiredKeepUndefined< - t.TypeOf<typeof deleteListItemSchema> ->; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.mock.ts deleted file mode 100644 index 4514b70ad39f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.mock.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", 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 { LIST_ID } from '../../constants/index.mock'; - -import { DeleteListSchema } from '.'; - -export const getDeleteListSchemaMock = (): DeleteListSchema => ({ - deleteReferences: false, - id: LIST_ID, - ignoreReferences: true, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.test.ts deleted file mode 100644 index 5de3ec2c2109..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.test.ts +++ /dev/null @@ -1,48 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { DeleteListSchema, deleteListSchema } from '.'; -import { getDeleteListSchemaMock } from './index.mock'; - -describe('delete_list_schema', () => { - test('it should validate a typical lists request', () => { - const payload = getDeleteListSchemaMock(); - const decoded = deleteListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for an id', () => { - const payload = getDeleteListSchemaMock(); - // @ts-expect-error - delete payload.id; - const decoded = deleteListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: DeleteListSchema & { extraKey?: string } = getDeleteListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = deleteListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.ts deleted file mode 100644 index 239a4d72113d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.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", 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 * as t from 'io-ts'; -import { DefaultStringBooleanFalse } from '@kbn/securitysolution-io-ts-types'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; - -export const deleteListSchema = t.intersection([ - t.exact( - t.type({ - id, - }) - ), - t.exact( - t.partial({ - deleteReferences: DefaultStringBooleanFalse, - ignoreReferences: DefaultStringBooleanFalse, - }) - ), -]); - -export type DeleteListSchema = RequiredKeepUndefined<t.TypeOf<typeof deleteListSchema>>; -export type DeleteListSchemaEncoded = t.OutputOf<typeof deleteListSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.mock.ts deleted file mode 100644 index 121ab5f0b914..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.mock.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", 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 { LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; - -import { DuplicateExceptionListQuerySchema } from '.'; - -export const getDuplicateExceptionListQuerySchemaMock = (): DuplicateExceptionListQuerySchema => ({ - list_id: LIST_ID, - namespace_type: NAMESPACE_TYPE, - include_expired_exceptions: 'true', -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.test.ts deleted file mode 100644 index 244adc07a81d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.test.ts +++ /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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { DuplicateExceptionListQuerySchema, duplicateExceptionListQuerySchema } from '.'; -import { getDuplicateExceptionListQuerySchemaMock } from './index.mock'; - -describe('duplicate_exceptionList_query_schema', () => { - test('it should validate a typical lists request', () => { - const payload = getDuplicateExceptionListQuerySchemaMock(); - const decoded = duplicateExceptionListQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should default namespace_type to "single" if an undefined given for namespacetype', () => { - const payload = getDuplicateExceptionListQuerySchemaMock(); - delete payload.namespace_type; - const decoded = duplicateExceptionListQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(message.schema).toEqual({ - include_expired_exceptions: 'true', - list_id: 'some-list-id', - namespace_type: 'single', - }); - }); - - test('it should NOT accept an undefined for an list_id', () => { - const payload = getDuplicateExceptionListQuerySchemaMock(); - // @ts-expect-error - delete payload.list_id; - const decoded = duplicateExceptionListQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: DuplicateExceptionListQuerySchema & { - extraKey?: string; - } = getDuplicateExceptionListQuerySchemaMock(); - payload.extraKey = 'some new value'; - const decoded = duplicateExceptionListQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts deleted file mode 100644 index b329e1244eb9..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { NamespaceType } from '../../common'; - -import { includeExpiredExceptionsOrUndefined } from '../../common/include_expired_exceptions'; -import { list_id } from '../../common/list_id'; -import { namespace_type } from '../../common/namespace_type'; - -export const duplicateExceptionListQuerySchema = t.exact( - t.type({ - list_id, - namespace_type, - include_expired_exceptions: includeExpiredExceptionsOrUndefined, - }) -); - -export type DuplicateExceptionListQuerySchema = t.OutputOf< - typeof duplicateExceptionListQuerySchema ->; - -// This type is used after a decode since some things are defaults after a decode. -export type DuplicateExceptionListQuerySchemaDecoded = Omit< - t.TypeOf<typeof duplicateExceptionListQuerySchema>, - 'namespace_type' -> & { - namespace_type: NamespaceType; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.mock.ts deleted file mode 100644 index 4f34a59407ea..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.mock.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { ID, LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; - -import { ExportExceptionListQuerySchema } from '.'; - -export const getExportExceptionListQuerySchemaMock = (): ExportExceptionListQuerySchema => ({ - id: ID, - list_id: LIST_ID, - namespace_type: NAMESPACE_TYPE, - include_expired_exceptions: 'true', -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.test.ts deleted file mode 100644 index 00b41f6d70f9..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.test.ts +++ /dev/null @@ -1,80 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { ExportExceptionListQuerySchema, exportExceptionListQuerySchema } from '.'; -import { getExportExceptionListQuerySchemaMock } from './index.mock'; - -describe('export_exception_list_schema', () => { - test('it should validate a typical lists request', () => { - const payload = getExportExceptionListQuerySchemaMock(); - const decoded = exportExceptionListQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for an id', () => { - const payload = getExportExceptionListQuerySchemaMock(); - // @ts-expect-error - delete payload.id; - const decoded = exportExceptionListQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); - expect(message.schema).toEqual({}); - }); - - test('it should default namespace_type to "single" if an undefined given for namespacetype', () => { - const payload = getExportExceptionListQuerySchemaMock(); - delete payload.namespace_type; - const decoded = exportExceptionListQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(message.schema).toEqual({ - id: 'uuid_here', - include_expired_exceptions: 'true', - list_id: 'some-list-id', - namespace_type: 'single', - }); - }); - - test('it should NOT accept an undefined for an list_id', () => { - const payload = getExportExceptionListQuerySchemaMock(); - // @ts-expect-error - delete payload.list_id; - const decoded = exportExceptionListQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ExportExceptionListQuerySchema & { - extraKey?: string; - } = getExportExceptionListQuerySchemaMock(); - payload.extraKey = 'some new value'; - const decoded = exportExceptionListQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.ts deleted file mode 100644 index 3943af148b15..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -import { id } from '../../common/id'; -import { includeExpiredExceptionsOrUndefined } from '../../common/include_expired_exceptions'; -import { list_id } from '../../common/list_id'; -import { namespace_type } from '../../common/namespace_type'; - -export const exportExceptionListQuerySchema = t.exact( - t.type({ - id, - list_id, - namespace_type, - include_expired_exceptions: includeExpiredExceptionsOrUndefined, - // TODO: Add file_name here with a default value - }) -); - -export type ExportExceptionListQuerySchema = t.OutputOf<typeof exportExceptionListQuerySchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.mock.ts deleted file mode 100644 index 3c66cae1fb0e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.mock.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", 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 { LIST_ID } from '../../constants/index.mock'; - -import { ExportListItemQuerySchema } from '.'; - -export const getExportListItemQuerySchemaMock = (): ExportListItemQuerySchema => ({ - list_id: LIST_ID, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.test.ts deleted file mode 100644 index 55713e33f160..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.test.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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { ExportListItemQuerySchema, exportListItemQuerySchema } from '.'; -import { getExportListItemQuerySchemaMock } from './index.mock'; - -describe('export_list_item_schema', () => { - test('it should validate a typical lists request', () => { - const payload = getExportListItemQuerySchemaMock(); - const decoded = exportListItemQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for an id', () => { - const payload = getExportListItemQuerySchemaMock(); - // @ts-expect-error - delete payload.list_id; - const decoded = exportListItemQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ExportListItemQuerySchema & { - extraKey?: string; - } = getExportListItemQuerySchemaMock(); - payload.extraKey = 'some new value'; - const decoded = exportListItemQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.ts deleted file mode 100644 index 86c8318f1ad5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.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 * as t from 'io-ts'; - -import { list_id } from '../../common/list_id'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; - -export const exportListItemQuerySchema = t.exact( - t.type({ - list_id, - // TODO: Add file_name here with a default value - }) -); - -export type ExportListItemQuerySchema = RequiredKeepUndefined< - t.TypeOf<typeof exportListItemQuerySchema> ->; -export type ExportListItemQuerySchemaEncoded = t.OutputOf<typeof exportListItemQuerySchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.mock.ts deleted file mode 100644 index d24d31cca5d9..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.mock.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { FILTER } from '../../constants/index.mock'; - -import { FindEndpointListItemSchema, FindEndpointListItemSchemaDecoded } from '.'; - -export const getFindEndpointListItemSchemaMock = (): FindEndpointListItemSchema => ({ - filter: FILTER, - page: '1', - per_page: '25', - sort_field: undefined, - sort_order: undefined, -}); - -export const getFindEndpointListItemSchemaDecodedMock = (): FindEndpointListItemSchemaDecoded => ({ - filter: FILTER, - page: 1, - per_page: 25, - sort_field: undefined, - sort_order: undefined, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.test.ts deleted file mode 100644 index 776cc2976868..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.test.ts +++ /dev/null @@ -1,109 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { - getFindEndpointListItemSchemaDecodedMock, - getFindEndpointListItemSchemaMock, -} from './index.mock'; -import { FindEndpointListItemSchema, findEndpointListItemSchema } from '.'; - -describe('find_endpoint_list_item_schema', () => { - test('it should validate a typical find item request', () => { - const payload = getFindEndpointListItemSchemaMock(); - const decoded = findEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getFindEndpointListItemSchemaDecodedMock()); - }); - - test('it should validate and empty object since everything is optional and has defaults', () => { - const payload: FindEndpointListItemSchema = {}; - const decoded = findEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate with page missing', () => { - const payload = getFindEndpointListItemSchemaMock(); - delete payload.page; - const decoded = findEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindEndpointListItemSchemaDecodedMock(); - delete expected.page; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with pre_page missing', () => { - const payload = getFindEndpointListItemSchemaMock(); - delete payload.per_page; - const decoded = findEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindEndpointListItemSchemaDecodedMock(); - delete expected.per_page; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with filter missing', () => { - const payload = getFindEndpointListItemSchemaMock(); - delete payload.filter; - const decoded = findEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindEndpointListItemSchemaDecodedMock(); - delete expected.filter; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with sort_field missing', () => { - const payload = getFindEndpointListItemSchemaMock(); - delete payload.sort_field; - const decoded = findEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindEndpointListItemSchemaDecodedMock(); - delete expected.sort_field; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with sort_order missing', () => { - const payload = getFindEndpointListItemSchemaMock(); - delete payload.sort_order; - const decoded = findEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindEndpointListItemSchemaDecodedMock(); - delete expected.sort_order; - expect(message.schema).toEqual(expected); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: FindEndpointListItemSchema & { - extraKey: string; - } = { ...getFindEndpointListItemSchemaMock(), extraKey: 'some new value' }; - const decoded = findEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.ts deleted file mode 100644 index a76854d3b4bf..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.ts +++ /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 * as t from 'io-ts'; -import { StringToPositiveNumber } from '@kbn/securitysolution-io-ts-types'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { filter } from '../../common/filter'; -import { sort_field } from '../../common/sort_field'; -import { sort_order } from '../../common/sort_order'; - -export const findEndpointListItemSchema = t.exact( - t.partial({ - filter, // defaults to undefined if not set during decode - page: StringToPositiveNumber, // defaults to undefined if not set during decode - per_page: StringToPositiveNumber, // defaults to undefined if not set during decode - sort_field, // defaults to undefined if not set during decode - sort_order, // defaults to undefined if not set during decode - }) -); - -export type FindEndpointListItemSchema = t.OutputOf<typeof findEndpointListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type FindEndpointListItemSchemaDecoded = RequiredKeepUndefined< - t.TypeOf<typeof findEndpointListItemSchema> ->; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.mock.ts deleted file mode 100644 index 02b89bdf4c73..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.mock.ts +++ /dev/null @@ -1,58 +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 { FILTER, LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; - -import { FindExceptionListItemSchema, FindExceptionListItemSchemaDecoded } from '.'; - -export const getFindExceptionListItemSchemaMock = (): FindExceptionListItemSchema => ({ - filter: FILTER, - list_id: LIST_ID, - namespace_type: NAMESPACE_TYPE, - page: '1', - per_page: '25', - search: undefined, - sort_field: undefined, - sort_order: undefined, -}); - -export const getFindExceptionListItemSchemaMultipleMock = (): FindExceptionListItemSchema => ({ - filter: 'name:Sofia Kovalevskaya,name:Hypatia,name:Sophie Germain', - list_id: 'list-1,list-2,list-3', - namespace_type: 'single,single,agnostic', - page: '1', - per_page: '25', - search: undefined, - sort_field: undefined, - sort_order: undefined, -}); - -export const getFindExceptionListItemSchemaDecodedMock = - (): FindExceptionListItemSchemaDecoded => ({ - filter: [FILTER], - list_id: [LIST_ID], - namespace_type: [NAMESPACE_TYPE], - page: 1, - per_page: 25, - search: undefined, - sort_field: undefined, - sort_order: undefined, - }); - -export const getFindExceptionListItemSchemaDecodedMultipleMock = - (): FindExceptionListItemSchemaDecoded => ({ - filter: ['name:Sofia Kovalevskaya', 'name:Hypatia', 'name:Sophie Germain'], - list_id: ['list-1', 'list-2', 'list-3'], - namespace_type: ['single', 'single', 'agnostic'], - page: 1, - per_page: 25, - search: undefined, - sort_field: undefined, - sort_order: undefined, - }); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.test.ts deleted file mode 100644 index da9c98d48e62..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.test.ts +++ /dev/null @@ -1,150 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { LIST_ID } from '../../constants/index.mock'; - -import { - getFindExceptionListItemSchemaDecodedMock, - getFindExceptionListItemSchemaDecodedMultipleMock, - getFindExceptionListItemSchemaMock, - getFindExceptionListItemSchemaMultipleMock, -} from './index.mock'; -import { - FindExceptionListItemSchema, - FindExceptionListItemSchemaDecoded, - findExceptionListItemSchema, -} from '.'; - -describe('find_list_item_schema', () => { - test('it should validate a typical find item request', () => { - const payload = getFindExceptionListItemSchemaMock(); - const decoded = findExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getFindExceptionListItemSchemaDecodedMock()); - }); - - test('it should validate a typical find item request with multiple input strings turned into array elements', () => { - const payload = getFindExceptionListItemSchemaMultipleMock(); - const decoded = findExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getFindExceptionListItemSchemaDecodedMultipleMock()); - }); - - test('it should validate just a list_id where it decodes into an array for list_id and adds a namespace_type of "single" as an array', () => { - const payload: FindExceptionListItemSchema = { list_id: LIST_ID }; - const decoded = findExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected: FindExceptionListItemSchemaDecoded = { - filter: [], - list_id: [LIST_ID], - namespace_type: ['single'], - page: undefined, - per_page: undefined, - search: undefined, - sort_field: undefined, - sort_order: undefined, - }; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with page missing', () => { - const payload = getFindExceptionListItemSchemaMock(); - delete payload.page; - const decoded = findExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindExceptionListItemSchemaDecodedMock(); - delete expected.page; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with per_page missing', () => { - const payload = getFindExceptionListItemSchemaMock(); - delete payload.per_page; - const decoded = findExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindExceptionListItemSchemaDecodedMock(); - delete expected.per_page; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with filter missing and add filter as an empty array', () => { - const payload = getFindExceptionListItemSchemaMock(); - delete payload.filter; - const decoded = findExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected: FindExceptionListItemSchemaDecoded = { - ...getFindExceptionListItemSchemaDecodedMock(), - filter: [], - }; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with sort_field missing', () => { - const payload = getFindExceptionListItemSchemaMock(); - delete payload.sort_field; - const decoded = findExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindExceptionListItemSchemaDecodedMock(); - delete expected.sort_field; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with sort_order missing', () => { - const payload = getFindExceptionListItemSchemaMock(); - delete payload.sort_order; - const decoded = findExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindExceptionListItemSchemaDecodedMock(); - delete expected.sort_order; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with search missing', () => { - const payload = getFindExceptionListItemSchemaMock(); - delete payload.search; - const decoded = findExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindExceptionListItemSchemaDecodedMock(); - delete expected.search; - expect(message.schema).toEqual(expected); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: FindExceptionListItemSchema & { - extraKey: string; - } = { ...getFindExceptionListItemSchemaMock(), extraKey: 'some new value' }; - const decoded = findExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts deleted file mode 100644 index 88258538e031..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts +++ /dev/null @@ -1,55 +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 * as t from 'io-ts'; -import { - EmptyStringArray, - EmptyStringArrayDecoded, - NonEmptyStringArray, - StringToPositiveNumber, -} from '@kbn/securitysolution-io-ts-types'; - -import { - DefaultNamespaceArray, - DefaultNamespaceArrayTypeDecoded, -} from '../../common/default_namespace_array'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { sort_field } from '../../common/sort_field'; -import { sort_order } from '../../common/sort_order'; -import { search } from '../../common/search'; - -export const findExceptionListItemSchema = t.intersection([ - t.exact( - t.type({ - list_id: NonEmptyStringArray, - }) - ), - t.exact( - t.partial({ - filter: EmptyStringArray, // defaults to an empty array [] if not set during decode - namespace_type: DefaultNamespaceArray, // defaults to ['single'] if not set during decode - page: StringToPositiveNumber, // defaults to undefined if not set during decode - per_page: StringToPositiveNumber, // defaults to undefined if not set during decode - search, - sort_field, // defaults to undefined if not set during decode - sort_order, // defaults to undefined if not set during decode - }) - ), -]); - -export type FindExceptionListItemSchema = t.OutputOf<typeof findExceptionListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type FindExceptionListItemSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof findExceptionListItemSchema>>, - 'namespace_type' | 'filter' -> & { - filter: EmptyStringArrayDecoded; - namespace_type: DefaultNamespaceArrayTypeDecoded; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.mock.ts deleted file mode 100644 index 0b8c9e3db9bb..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.mock.ts +++ /dev/null @@ -1,30 +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 { FILTER, NAMESPACE_TYPE } from '../../constants/index.mock'; - -import { FindExceptionListSchema, FindExceptionListSchemaDecoded } from '.'; - -export const getFindExceptionListSchemaMock = (): FindExceptionListSchema => ({ - filter: FILTER, - namespace_type: NAMESPACE_TYPE, - page: '1', - per_page: '25', - sort_field: undefined, - sort_order: undefined, -}); - -export const getFindExceptionListSchemaDecodedMock = (): FindExceptionListSchemaDecoded => ({ - filter: FILTER, - namespace_type: [NAMESPACE_TYPE], - page: 1, - per_page: 25, - sort_field: undefined, - sort_order: undefined, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.test.ts deleted file mode 100644 index fd105d3d2f61..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.test.ts +++ /dev/null @@ -1,121 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { - getFindExceptionListSchemaDecodedMock, - getFindExceptionListSchemaMock, -} from './index.mock'; -import { - FindExceptionListSchema, - FindExceptionListSchemaDecoded, - findExceptionListSchema, -} from '.'; - -describe('find_exception_list_schema', () => { - test('it should validate a typical find item request', () => { - const payload = getFindExceptionListSchemaMock(); - const decoded = findExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getFindExceptionListSchemaDecodedMock()); - }); - - test('it should validate and empty object since everything is optional and will respond only with namespace_type filled out to be "single"', () => { - const payload: FindExceptionListSchema = {}; - const decoded = findExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected: FindExceptionListSchemaDecoded = { - filter: undefined, - namespace_type: ['single'], - page: undefined, - per_page: undefined, - sort_field: undefined, - sort_order: undefined, - }; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with page missing', () => { - const payload = getFindExceptionListSchemaMock(); - delete payload.page; - const decoded = findExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindExceptionListSchemaDecodedMock(); - delete expected.page; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with pre_page missing', () => { - const payload = getFindExceptionListSchemaMock(); - delete payload.per_page; - const decoded = findExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindExceptionListSchemaDecodedMock(); - delete expected.per_page; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with filter missing', () => { - const payload = getFindExceptionListSchemaMock(); - delete payload.filter; - const decoded = findExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindExceptionListSchemaDecodedMock(); - delete expected.filter; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with sort_field missing', () => { - const payload = getFindExceptionListSchemaMock(); - delete payload.sort_field; - const decoded = findExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindExceptionListSchemaDecodedMock(); - delete expected.sort_field; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with sort_order missing', () => { - const payload = getFindExceptionListSchemaMock(); - delete payload.sort_order; - const decoded = findExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindExceptionListSchemaDecodedMock(); - delete expected.sort_order; - expect(message.schema).toEqual(expected); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: FindExceptionListSchema & { - extraKey: string; - } = { ...getFindExceptionListSchemaMock(), extraKey: 'some new value' }; - const decoded = findExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.ts deleted file mode 100644 index 1e22f752d014..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.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", 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 * as t from 'io-ts'; -import { StringToPositiveNumber } from '@kbn/securitysolution-io-ts-types'; - -import { DefaultNamespaceArray, NamespaceTypeArray } from '../../common/default_namespace_array'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { filter } from '../../common/filter'; -import { sort_field } from '../../common/sort_field'; -import { sort_order } from '../../common/sort_order'; - -export const findExceptionListSchema = t.exact( - t.partial({ - filter, // defaults to undefined if not set during decode - namespace_type: DefaultNamespaceArray, // defaults to 'single' if not set during decode - page: StringToPositiveNumber, // defaults to undefined if not set during decode - per_page: StringToPositiveNumber, // defaults to undefined if not set during decode - sort_field, // defaults to undefined if not set during decode - sort_order, // defaults to undefined if not set during decode - }) -); - -export type FindExceptionListSchema = t.OutputOf<typeof findExceptionListSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type FindExceptionListSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof findExceptionListSchema>>, - 'namespace_type' -> & { - namespace_type: NamespaceTypeArray; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.mock.ts deleted file mode 100644 index 601406ce3e37..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.mock.ts +++ /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 { CURSOR, FILTER, LIST_ID } from '../../constants/index.mock'; - -import { FindListItemSchema, FindListItemSchemaDecoded } from '.'; - -export const getFindListItemSchemaMock = (): FindListItemSchema => ({ - cursor: CURSOR, - filter: FILTER, - list_id: LIST_ID, - page: '1', - per_page: '25', - sort_field: undefined, - sort_order: undefined, -}); - -export const getFindListItemSchemaDecodedMock = (): FindListItemSchemaDecoded => ({ - cursor: CURSOR, - filter: FILTER, - list_id: LIST_ID, - page: 1, - per_page: 25, - sort_field: undefined, - sort_order: undefined, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.test.ts deleted file mode 100644 index b80629f8546b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.test.ts +++ /dev/null @@ -1,105 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { LIST_ID } from '../../constants/index.mock'; - -import { FindListItemSchema, FindListItemSchemaDecoded, findListItemSchema } from '.'; -import { getFindListItemSchemaDecodedMock, getFindListItemSchemaMock } from './index.mock'; - -describe('find_list_item_schema', () => { - test('it should validate a typical find item request', () => { - const payload = getFindListItemSchemaMock(); - const decoded = findListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getFindListItemSchemaDecodedMock()); - }); - - test('it should validate just a list_id where it decodes into an array for list_id and adds a namespace_type of "single"', () => { - const payload: FindListItemSchema = { list_id: LIST_ID }; - const decoded = findListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected: FindListItemSchemaDecoded = { - cursor: undefined, - filter: undefined, - list_id: LIST_ID, - page: undefined, - per_page: undefined, - sort_field: undefined, - sort_order: undefined, - }; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with page missing', () => { - const payload = getFindListItemSchemaMock(); - delete payload.page; - const decoded = findListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindListItemSchemaDecodedMock(); - delete expected.page; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with pre_page missing', () => { - const payload = getFindListItemSchemaMock(); - delete payload.per_page; - const decoded = findListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindListItemSchemaDecodedMock(); - delete expected.per_page; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with sort_field missing', () => { - const payload = getFindListItemSchemaMock(); - delete payload.sort_field; - const decoded = findListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindListItemSchemaDecodedMock(); - delete expected.sort_field; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with sort_order missing', () => { - const payload = getFindListItemSchemaMock(); - delete payload.sort_order; - const decoded = findListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindListItemSchemaDecodedMock(); - delete expected.sort_order; - expect(message.schema).toEqual(expected); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: FindListItemSchema & { - extraKey: string; - } = { ...getFindListItemSchemaMock(), extraKey: 'some new value' }; - const decoded = findListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.ts deleted file mode 100644 index 4509f6bc973b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.ts +++ /dev/null @@ -1,37 +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 * as t from 'io-ts'; - -import { StringToPositiveNumber } from '@kbn/securitysolution-io-ts-types'; -import { filter } from '../../common/filter'; -import { cursor } from '../../common/cursor'; -import { sort_field } from '../../common/sort_field'; -import { sort_order } from '../../common/sort_order'; -import { list_id } from '../../common/list_id'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; - -export const findListItemSchema = t.intersection([ - t.exact(t.type({ list_id })), - t.exact( - t.partial({ - cursor, // defaults to undefined if not set during decode - filter, // defaults to undefined if not set during decode - page: StringToPositiveNumber, // defaults to undefined if not set during decode - per_page: StringToPositiveNumber, // defaults to undefined if not set during decode - sort_field, // defaults to undefined if not set during decode - sort_order, // defaults to undefined if not set during decode - }) - ), -]); - -export type FindListItemSchema = t.OutputOf<typeof findListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type FindListItemSchemaDecoded = RequiredKeepUndefined<t.TypeOf<typeof findListItemSchema>>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.mock.ts deleted file mode 100644 index 48c9671e4ac1..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.mock.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", 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 { FILTER } from '../../constants/index.mock'; - -import { FindListSchema, FindListSchemaEncoded } from '.'; - -export const getFindListSchemaMock = (): FindListSchemaEncoded => ({ - filter: FILTER, - page: '1', - per_page: '25', - sort_field: undefined, - sort_order: undefined, -}); - -export const getFindListSchemaDecodedMock = (): FindListSchema => ({ - cursor: undefined, - filter: FILTER, - page: 1, - per_page: 25, - sort_field: undefined, - sort_order: undefined, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.test.ts deleted file mode 100644 index ba03ddcb8526..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.test.ts +++ /dev/null @@ -1,106 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getFindListSchemaDecodedMock, getFindListSchemaMock } from './index.mock'; -import { FindListSchemaEncoded, findListSchema } from '.'; - -describe('find_list_schema', () => { - test('it should validate a typical find item request', () => { - const payload = getFindListSchemaMock(); - const decoded = findListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getFindListSchemaDecodedMock()); - }); - - test('it should validate and empty object since everything is optional and will respond with an empty object', () => { - const payload: FindListSchemaEncoded = {}; - const decoded = findListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should validate with page missing', () => { - const payload = getFindListSchemaMock(); - delete payload.page; - const decoded = findListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindListSchemaDecodedMock(); - delete expected.page; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with pre_page missing', () => { - const payload = getFindListSchemaMock(); - delete payload.per_page; - const decoded = findListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindListSchemaDecodedMock(); - delete expected.per_page; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with filter missing', () => { - const payload = getFindListSchemaMock(); - delete payload.filter; - const decoded = findListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindListSchemaDecodedMock(); - delete expected.filter; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with sort_field missing', () => { - const payload = getFindListSchemaMock(); - delete payload.sort_field; - const decoded = findListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindListSchemaDecodedMock(); - delete expected.sort_field; - expect(message.schema).toEqual(expected); - }); - - test('it should validate with sort_order missing', () => { - const payload = getFindListSchemaMock(); - delete payload.sort_order; - const decoded = findListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - const expected = getFindListSchemaDecodedMock(); - delete expected.sort_order; - expect(message.schema).toEqual(expected); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: FindListSchemaEncoded & { - extraKey: string; - } = { ...getFindListSchemaMock(), extraKey: 'some new value' }; - const decoded = findListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.ts deleted file mode 100644 index 8b33ffc6a947..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.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", 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 * as t from 'io-ts'; - -import { StringToPositiveNumber } from '@kbn/securitysolution-io-ts-types'; -import { cursor } from '../../common/cursor'; -import { filter } from '../../common/filter'; -import { sort_field } from '../../common/sort_field'; -import { sort_order } from '../../common/sort_order'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; - -export const findListSchema = t.exact( - t.partial({ - cursor, // defaults to undefined if not set during decode - filter, // defaults to undefined if not set during decode - page: StringToPositiveNumber, // defaults to undefined if not set during decode - per_page: StringToPositiveNumber, // defaults to undefined if not set during decode - sort_field, // defaults to undefined if not set during decode - sort_order, // defaults to undefined if not set during decode - }) -); - -export type FindListSchema = RequiredKeepUndefined<t.TypeOf<typeof findListSchema>>; -export type FindListSchemaEncoded = t.OutputOf<typeof findListSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/get_exception_filter_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/get_exception_filter_schema/index.ts deleted file mode 100644 index 58c93b697a85..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/get_exception_filter_schema/index.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 * as t from 'io-ts'; -import { namespaceType } from '../../common/default_namespace'; -import { exceptionListItemSchema } from '../../response'; -import { createExceptionListItemSchema } from '../create_exception_list_item_schema'; - -const exceptionListId = t.type({ - exception_list_id: t.string, - namespace_type: namespaceType, -}); - -export const exceptionListIds = t.type({ - exception_list_ids: t.array(exceptionListId), - type: t.literal('exception_list_ids'), -}); - -export const exceptions = t.type({ - exceptions: t.array(t.union([exceptionListItemSchema, createExceptionListItemSchema])), - type: t.literal('exception_items'), -}); - -const optionalExceptionParams = t.exact( - t.partial({ alias: t.string, chunk_size: t.number, exclude_exceptions: t.boolean }) -); - -export const getExceptionFilterSchema = t.intersection([ - t.union([exceptions, exceptionListIds]), - optionalExceptionParams, -]); - -export type GetExceptionFilterSchema = t.TypeOf<typeof getExceptionFilterSchema>; -export type ExceptionListId = t.TypeOf<typeof exceptionListId>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.mock.ts deleted file mode 100644 index e66b805de496..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.mock.ts +++ /dev/null @@ -1,36 +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 { ENTRIES } from '../../constants/index.mock'; -import { ImportExceptionListItemSchema, ImportExceptionListItemSchemaDecoded } from '.'; - -export const getImportExceptionsListItemSchemaMock = ( - itemId = 'item_id_1', - listId = 'detection_list_id' -): ImportExceptionListItemSchema => ({ - description: 'some description', - entries: ENTRIES, - item_id: itemId, - list_id: listId, - name: 'Query with a rule id', - type: 'simple', -}); - -export const getImportExceptionsListItemSchemaDecodedMock = ( - itemId = 'item_id_1', - listId = 'detection_list_id' -): ImportExceptionListItemSchemaDecoded => ({ - ...getImportExceptionsListItemSchemaMock(itemId, listId), - comments: [], - meta: undefined, - namespace_type: 'single', - os_types: [], - tags: [], - expire_time: undefined, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.test.ts deleted file mode 100644 index 58b9bce4c410..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.test.ts +++ /dev/null @@ -1,174 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { importExceptionListItemSchema, ImportExceptionListItemSchema } from '.'; -import { - getImportExceptionsListItemSchemaDecodedMock, - getImportExceptionsListItemSchemaMock, -} from './index.mock'; -import { getCommentsArrayMock } from '../../common/comment/index.mock'; - -describe('import_list_item_schema', () => { - test('it should validate a typical item request', () => { - const payload = getImportExceptionsListItemSchemaMock(); - const decoded = importExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getImportExceptionsListItemSchemaDecodedMock()); - }); - - test('it should validate a typical item request with comments', () => { - const payload = { - ...getImportExceptionsListItemSchemaMock(), - comments: getCommentsArrayMock(), - }; - const decoded = importExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual({ - ...getImportExceptionsListItemSchemaDecodedMock(), - comments: [ - { - comment: 'some old comment', - created_at: '2020-04-20T15:25:31.830Z', - created_by: 'some user', - id: 'uuid_here', - }, - { - comment: 'some old comment', - created_at: '2020-04-20T15:25:31.830Z', - created_by: 'some user', - id: 'uuid_here', - }, - ], - }); - }); - - test('it should NOT accept an undefined for "item_id"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = - getImportExceptionsListItemSchemaMock(); - delete payload.item_id; - const decoded = importExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "item_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "list_id"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = - getImportExceptionsListItemSchemaMock(); - delete payload.list_id; - const decoded = importExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "description"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = - getImportExceptionsListItemSchemaMock(); - delete payload.description; - const decoded = importExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "name"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = - getImportExceptionsListItemSchemaMock(); - delete payload.name; - const decoded = importExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "name"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "type"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = - getImportExceptionsListItemSchemaMock(); - delete payload.type; - const decoded = importExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "entries"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = - getImportExceptionsListItemSchemaMock(); - delete payload.entries; - const decoded = importExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should accept any partial fields', () => { - const payload: ImportExceptionListItemSchema = { - ...getImportExceptionsListItemSchemaMock(), - id: '123', - namespace_type: 'single', - comments: [], - os_types: [], - tags: ['123'], - created_at: '2018-08-24T17:49:30.145142000', - created_by: 'elastic', - updated_at: '2018-08-24T17:49:30.145142000', - updated_by: 'elastic', - tie_breaker_id: '123', - _version: '3', - meta: undefined, - }; - - const decoded = importExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ImportExceptionListItemSchema & { - extraKey?: string; - } = getImportExceptionsListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = importExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.ts deleted file mode 100644 index 9b0762005f3a..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.ts +++ /dev/null @@ -1,90 +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 * as t from 'io-ts'; - -import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; -import { Tags } from '../../common/tags'; -import { NamespaceType } from '../../common/default_namespace'; -import { name } from '../../common/name'; -import { description } from '../../common/description'; -import { namespace_type } from '../../common/namespace_type'; -import { tags } from '../../common/tags'; -import { meta } from '../../common/meta'; -import { list_id } from '../../common/list_id'; -import { item_id } from '../../common/item_id'; -import { id } from '../../common/id'; -import { created_at } from '../../common/created_at'; -import { created_by } from '../../common/created_by'; -import { updated_at } from '../../common/updated_at'; -import { updated_by } from '../../common/updated_by'; -import { _version } from '../../common/underscore_version'; -import { tie_breaker_id } from '../../common/tie_breaker_id'; -import { nonEmptyEntriesArray } from '../../common/non_empty_entries_array'; -import { exceptionListItemType } from '../../common/exception_list_item_type'; -import { ItemId } from '../../common/item_id'; -import { EntriesArray } from '../../common/entries'; -import { DefaultImportCommentsArray } from '../../common/default_import_comments_array'; -import { ExpireTimeOrUndefined, expireTimeOrUndefined, ImportCommentsArray } from '../../common'; - -/** - * Differences from this and the createExceptionsListItemSchema are - * - item_id is required - * - id is optional (but ignored in the import code - item_id is exclusively used for imports) - * - immutable is optional but if it is any value other than false it will be rejected - * - created_at is optional (but ignored in the import code) - * - updated_at is optional (but ignored in the import code) - * - created_by is optional (but ignored in the import code) - * - updated_by is optional (but ignored in the import code) - */ -export const importExceptionListItemSchema = t.intersection([ - t.exact( - t.type({ - description, - entries: nonEmptyEntriesArray, - item_id, - list_id, - name, - type: exceptionListItemType, - }) - ), - t.exact( - t.partial({ - id, // defaults to undefined if not set during decode - comments: DefaultImportCommentsArray, // defaults to empty array if not set during decode - created_at, // defaults undefined if not set during decode - updated_at, // defaults undefined if not set during decode - created_by, // defaults undefined if not set during decode - updated_by, // defaults undefined if not set during decode - _version, // defaults to undefined if not set during decode - tie_breaker_id, - meta, // defaults to undefined if not set during decode - namespace_type, // defaults to 'single' if not set during decode - os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode - tags, // defaults to empty array if not set during decode - expire_time: expireTimeOrUndefined, - }) - ), -]); - -export type ImportExceptionListItemSchema = t.OutputOf<typeof importExceptionListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type ImportExceptionListItemSchemaDecoded = Omit< - ImportExceptionListItemSchema, - 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' | 'expire_time' -> & { - comments: ImportCommentsArray; - tags: Tags; - item_id: ItemId; - entries: EntriesArray; - namespace_type: NamespaceType; - os_types: OsTypeArray; - expire_time: ExpireTimeOrUndefined; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.mock.ts deleted file mode 100644 index 5bf263beb372..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.mock.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", 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 { ImportExceptionListSchemaDecoded, ImportExceptionsListSchema } from '.'; - -export const getImportExceptionsListSchemaMock = ( - listId = 'detection_list_id' -): ImportExceptionsListSchema => ({ - description: 'some description', - list_id: listId, - name: 'Query with a rule id', - type: 'detection', -}); - -export const getImportExceptionsListSchemaDecodedMock = ( - listId = 'detection_list_id' -): ImportExceptionListSchemaDecoded => ({ - ...getImportExceptionsListSchemaMock(listId), - immutable: false, - meta: undefined, - namespace_type: 'single', - os_types: [], - tags: [], - version: 1, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.test.ts deleted file mode 100644 index 677e089fe1ef..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.test.ts +++ /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", 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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { importExceptionsListSchema, ImportExceptionsListSchema } from '.'; -import { - getImportExceptionsListSchemaMock, - getImportExceptionsListSchemaDecodedMock, -} from './index.mock'; - -describe('import_list_item_schema', () => { - test('it should validate a typical lists request', () => { - const payload = getImportExceptionsListSchemaMock(); - const decoded = importExceptionsListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getImportExceptionsListSchemaDecodedMock()); - }); - - test('it should NOT accept an undefined for "list_id"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsListSchemaMock>> = - getImportExceptionsListSchemaMock(); - delete payload.list_id; - const decoded = importExceptionsListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "description"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsListSchemaMock>> = - getImportExceptionsListSchemaMock(); - delete payload.description; - const decoded = importExceptionsListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "name"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsListSchemaMock>> = - getImportExceptionsListSchemaMock(); - delete payload.name; - const decoded = importExceptionsListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "name"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "type"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsListSchemaMock>> = - getImportExceptionsListSchemaMock(); - delete payload.type; - const decoded = importExceptionsListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept value of "true" for "immutable"', () => { - const payload: ImportExceptionsListSchema = { - ...getImportExceptionsListSchemaMock(), - immutable: true, - }; - - const decoded = importExceptionsListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "true" supplied to "immutable"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should accept any partial fields', () => { - const payload: ImportExceptionsListSchema = { - ...getImportExceptionsListSchemaMock(), - namespace_type: 'single', - immutable: false, - os_types: [], - tags: ['123'], - created_at: '2018-08-24T17:49:30.145142000', - created_by: 'elastic', - updated_at: '2018-08-24T17:49:30.145142000', - updated_by: 'elastic', - version: 3, - tie_breaker_id: '123', - _version: '3', - meta: undefined, - }; - - const decoded = importExceptionsListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ImportExceptionsListSchema & { - extraKey?: string; - } = getImportExceptionsListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = importExceptionsListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.ts deleted file mode 100644 index 9c0584aa267e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.ts +++ /dev/null @@ -1,88 +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 * as t from 'io-ts'; - -import { - DefaultVersionNumber, - DefaultVersionNumberDecoded, - OnlyFalseAllowed, -} from '@kbn/securitysolution-io-ts-types'; - -import { exceptionListType } from '../../common/exception_list'; -import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; -import { Tags } from '../../common/tags'; -import { ListId } from '../../common/list_id'; -import { NamespaceType } from '../../common/default_namespace'; -import { name } from '../../common/name'; -import { description } from '../../common/description'; -import { namespace_type } from '../../common/namespace_type'; -import { tags } from '../../common/tags'; -import { meta } from '../../common/meta'; -import { list_id } from '../../common/list_id'; -import { id } from '../../common/id'; -import { created_at } from '../../common/created_at'; -import { created_by } from '../../common/created_by'; -import { updated_at } from '../../common/updated_at'; -import { updated_by } from '../../common/updated_by'; -import { _version } from '../../common/underscore_version'; -import { tie_breaker_id } from '../../common/tie_breaker_id'; - -/** - * Differences from this and the createExceptionsSchema are - * - list_id is required - * - id is optional (but ignored in the import code - list_id is exclusively used for imports) - * - immutable is optional but if it is any value other than false it will be rejected - * - created_at is optional (but ignored in the import code) - * - updated_at is optional (but ignored in the import code) - * - created_by is optional (but ignored in the import code) - * - updated_by is optional (but ignored in the import code) - */ -export const importExceptionsListSchema = t.intersection([ - t.exact( - t.type({ - description, - name, - type: exceptionListType, - list_id, - }) - ), - t.exact( - t.partial({ - id, // defaults to undefined if not set during decode - immutable: OnlyFalseAllowed, - meta, // defaults to undefined if not set during decode - namespace_type, // defaults to 'single' if not set during decode - os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode - tags, // defaults to empty array if not set during decode - created_at, // defaults "undefined" if not set during decode - updated_at, // defaults "undefined" if not set during decode - created_by, // defaults "undefined" if not set during decode - updated_by, // defaults "undefined" if not set during decode - _version, // defaults to undefined if not set during decode - tie_breaker_id, - version: DefaultVersionNumber, // defaults to numerical 1 if not set during decode - }) - ), -]); - -export type ImportExceptionsListSchema = t.TypeOf<typeof importExceptionsListSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type ImportExceptionListSchemaDecoded = Omit< - ImportExceptionsListSchema, - 'tags' | 'list_id' | 'namespace_type' | 'os_types' | 'immutable' -> & { - immutable: false; - tags: Tags; - list_id: ListId; - namespace_type: NamespaceType; - os_types: OsTypeArray; - version: DefaultVersionNumberDecoded; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.mock.ts deleted file mode 100644 index 74a2122ff1dc..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.mock.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { LIST_ID, TYPE } from '../../constants/index.mock'; - -import { ImportListItemQuerySchema } from '.'; - -export const getImportListItemQuerySchemaMock = (): ImportListItemQuerySchema => ({ - deserializer: undefined, - list_id: LIST_ID, - serializer: undefined, - type: TYPE, - refresh: 'false', -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.test.ts deleted file mode 100644 index b0ec4f10af87..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.test.ts +++ /dev/null @@ -1,90 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { ImportListItemQuerySchema, importListItemQuerySchema } from '.'; -import { getImportListItemQuerySchemaMock } from './index.mock'; - -describe('import_list_item_schema', () => { - test('it should validate a typical lists request', () => { - const payload = getImportListItemQuerySchemaMock(); - const decoded = importListItemQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "list_id"', () => { - const payload = getImportListItemQuerySchemaMock(); - delete payload.list_id; - const decoded = importListItemQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "type"', () => { - const payload = getImportListItemQuerySchemaMock(); - delete payload.type; - const decoded = importListItemQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "type" and "list_id', () => { - const payload = getImportListItemQuerySchemaMock(); - delete payload.type; - delete payload.list_id; - const decoded = importListItemQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "serializer"', () => { - const payload = getImportListItemQuerySchemaMock(); - delete payload.serializer; - const decoded = importListItemQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "deserializer"', () => { - const payload = getImportListItemQuerySchemaMock(); - delete payload.deserializer; - const decoded = importListItemQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ImportListItemQuerySchema & { - extraKey?: string; - } = getImportListItemQuerySchemaMock(); - payload.extraKey = 'some new value'; - const decoded = importListItemQuerySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.ts deleted file mode 100644 index e34d94c49866..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.ts +++ /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", 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 * as t from 'io-ts'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { deserializer } from '../../common/deserializer'; -import { list_id } from '../../common/list_id'; -import { type } from '../../common/type'; -import { serializer } from '../../common/serializer'; -import { refreshWithWaitFor } from '../../common/refresh'; - -export const importListItemQuerySchema = t.exact( - t.partial({ deserializer, list_id, serializer, type, refresh: refreshWithWaitFor }) -); - -export type ImportListItemQuerySchema = RequiredKeepUndefined< - t.TypeOf<typeof importListItemQuerySchema> ->; -export type ImportListItemQuerySchemaEncoded = t.OutputOf<typeof importListItemQuerySchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.mock.ts deleted file mode 100644 index 15dd19d4bdef..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.mock.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { ImportListItemSchema } from '.'; - -export const getImportListItemSchemaMock = (): ImportListItemSchema => ({ - file: {}, -}); - -/** - * This is useful for end to end tests, it will return a buffer given a string array - * of things to import. - * @param input Array of strings of things to import - */ -export const getImportListItemAsBuffer = (input: string[]): Buffer => { - return Buffer.from(input.join('\r\n')); -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.test.ts deleted file mode 100644 index c4841f394f8d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.test.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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { ImportListItemSchema, importListItemSchema } from '.'; -import { getImportListItemSchemaMock } from './index.mock'; - -describe('import_list_item_schema', () => { - test('it should validate a typical lists request', () => { - const payload = getImportListItemSchemaMock(); - const decoded = importListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for a file', () => { - const payload = getImportListItemSchemaMock(); - // @ts-expect-error - delete payload.file; - const decoded = importListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "file"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ImportListItemSchema & { - extraKey?: string; - } = getImportListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = importListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.ts deleted file mode 100644 index 45dbf838e587..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.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 * as t from 'io-ts'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { file } from '../../common/file'; - -export const importListItemSchema = t.exact( - t.type({ - file, - }) -); - -export type ImportListItemSchema = RequiredKeepUndefined<t.TypeOf<typeof importListItemSchema>>; -export type ImportListItemSchemaEncoded = t.OutputOf<typeof importListItemSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/index.ts deleted file mode 100644 index 3d21a5ffdcb2..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/index.ts +++ /dev/null @@ -1,49 +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". - */ - -export * from './create_endpoint_list_item_schema'; -export * from './create_exception_list_item_schema'; -export * from './create_exception_list_schema'; -export * from './create_rule_exception_item_schema'; -export * from './create_list_item_schema'; -export * from './create_list_schema'; -export * from './delete_endpoint_list_item_schema'; -export * from './delete_exception_list_schema'; -export * from './delete_exception_list_item_schema'; -export * from './delete_list_item_schema'; -export * from './delete_list_schema'; -export * from './duplicate_exception_list_query_schema'; -export * from './export_exception_list_query_schema'; -export * from './export_list_item_query_schema'; -export * from './find_endpoint_list_item_schema'; -export * from './find_exception_list_schema'; -export * from './find_exception_list_item_schema'; -export * from './find_list_item_schema'; -export * from './find_list_schema'; -export * from './get_exception_filter_schema'; -export * from './import_list_item_query_schema'; -export * from './import_exception_list_schema'; -export * from './import_exception_item_schema'; -export * from './import_list_item_schema'; -export * from './patch_list_item_schema'; -export * from './patch_list_schema'; -export * from './read_endpoint_list_item_schema'; -export * from './read_exception_list_item_schema'; -export * from './read_exception_list_schema'; -export * from './read_list_item_schema'; -export * from './read_list_schema'; -export * from './summary_exception_list_schema'; -export * from './update_endpoint_list_item_schema'; -export * from './update_exception_list_item_schema'; -export * from './update_exception_list_schema'; -export * from './update_list_item_schema'; -export * from './update_list_schema'; - -// Internal routes -export * from './internal/create_exception_list_schema'; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.test.ts deleted file mode 100644 index 4a4a090723c3..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.test.ts +++ /dev/null @@ -1,70 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; - -import { ExceptionListTypeEnum } from '../../../common/exception_list'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { internalCreateExceptionListSchema } from '.'; -import { getCreateExceptionListSchemaMock } from '../../create_exception_list_schema/index.mock'; - -describe('create_exception_list_schema', () => { - test('it should accept artifact list_id', () => { - const payload = { - ...getCreateExceptionListSchemaMock(), - list_id: ExceptionListTypeEnum.ENDPOINT_BLOCKLISTS, - }; - const decoded = internalCreateExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - test('it should fail when invalid list_id', () => { - const payload = { - ...getCreateExceptionListSchemaMock(), - list_id: ExceptionListTypeEnum.DETECTION, - }; - const decoded = internalCreateExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "detection" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - test('it should accept artifact type', () => { - const payload = { - ...getCreateExceptionListSchemaMock(), - list_id: ExceptionListTypeEnum.ENDPOINT_BLOCKLISTS, - type: ExceptionListTypeEnum.ENDPOINT_BLOCKLISTS, - }; - const decoded = internalCreateExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - test('it should fail when invalid type', () => { - const payload = { - ...getCreateExceptionListSchemaMock(), - list_id: ExceptionListTypeEnum.ENDPOINT_BLOCKLISTS, - type: ExceptionListTypeEnum.DETECTION, - }; - const decoded = internalCreateExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "detection" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.ts deleted file mode 100644 index 3aa11ba50050..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.ts +++ /dev/null @@ -1,48 +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 { ENDPOINT_ARTIFACT_LIST_IDS } from '@kbn/securitysolution-list-constants'; -import * as t from 'io-ts'; - -import { - createExceptionListSchema, - CreateExceptionListSchemaDecoded, -} from '../../create_exception_list_schema'; - -export const internalCreateExceptionListSchema = t.intersection([ - t.exact( - t.type({ - type: t.keyof({ - endpoint: null, - endpoint_events: null, - endpoint_host_isolation_exceptions: null, - endpoint_blocklists: null, - }), - }) - ), - t.exact( - t.partial({ - list_id: t.keyof( - ENDPOINT_ARTIFACT_LIST_IDS.reduce<Record<string, null>>((mapOfListIds, listId) => { - mapOfListIds[listId] = null; - - return mapOfListIds; - }, {}) - ), - }) - ), - createExceptionListSchema, -]); - -export type InternalCreateExceptionListSchema = t.OutputOf< - typeof internalCreateExceptionListSchema ->; - -// This type is used after a decode since some things are defaults after a decode. -export type InternalCreateExceptionListSchemaDecoded = CreateExceptionListSchemaDecoded; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.mock.ts deleted file mode 100644 index ff156386baa9..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.mock.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", 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 { LIST_ITEM_ID, META, VALUE } from '../../constants/index.mock'; - -import { PatchListItemSchema } from '.'; - -export const getPathListItemSchemaMock = (): PatchListItemSchema => ({ - id: LIST_ITEM_ID, - meta: META, - value: VALUE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.test.ts deleted file mode 100644 index fddeeaf0055c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getPathListItemSchemaMock } from './index.mock'; -import { PatchListItemSchema, patchListItemSchema } from '.'; - -describe('patch_list_item_schema', () => { - test('it should validate a typical list item request', () => { - const payload = getPathListItemSchemaMock(); - const decoded = patchListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "id"', () => { - const payload = getPathListItemSchemaMock(); - // @ts-expect-error - delete payload.id; - const decoded = patchListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "meta"', () => { - const payload = getPathListItemSchemaMock(); - delete payload.meta; - const decoded = patchListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "value"', () => { - const payload = getPathListItemSchemaMock(); - delete payload.value; - const decoded = patchListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "meta" and "value"', () => { - const payload = getPathListItemSchemaMock(); - delete payload.meta; - delete payload.value; - const decoded = patchListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: PatchListItemSchema & { extraKey?: string } = getPathListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = patchListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.ts deleted file mode 100644 index 96b50702d99b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.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", 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 * as t from 'io-ts'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { _version } from '../../common/underscore_version'; -import { id } from '../../common/id'; -import { meta } from '../../common/meta'; -import { value } from '../../common/value'; -import { refresh } from '../../common/refresh'; - -export const patchListItemSchema = t.intersection([ - t.exact( - t.type({ - id, - }) - ), - t.exact(t.partial({ _version, meta, value, refresh })), -]); - -export type PatchListItemSchema = t.OutputOf<typeof patchListItemSchema>; -export type PatchListItemSchemaDecoded = RequiredKeepUndefined< - t.TypeOf<typeof patchListItemSchema> ->; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.mock.ts deleted file mode 100644 index 818dd4e79e98..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.mock.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { DESCRIPTION, LIST_ITEM_ID, META, NAME } from '../../constants/index.mock'; - -import { PatchListSchema } from '.'; - -export const getPathListSchemaMock = (): PatchListSchema => ({ - description: DESCRIPTION, - id: LIST_ITEM_ID, - meta: META, - name: NAME, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.test.ts deleted file mode 100644 index b79bfc8fddd6..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.test.ts +++ /dev/null @@ -1,131 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getPathListSchemaMock } from './index.mock'; -import { PatchListSchema, patchListSchema } from '.'; - -describe('patch_list_schema', () => { - test('it should validate a typical list item request', () => { - const payload = getPathListSchemaMock(); - const decoded = patchListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "id"', () => { - const payload = getPathListSchemaMock(); - // @ts-expect-error - delete payload.id; - const decoded = patchListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "meta"', () => { - const payload = getPathListSchemaMock(); - delete payload.meta; - const decoded = patchListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "name"', () => { - const payload = getPathListSchemaMock(); - delete payload.name; - const decoded = patchListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "description"', () => { - const payload = getPathListSchemaMock(); - delete payload.description; - const decoded = patchListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "description", "meta", "name', () => { - const payload = getPathListSchemaMock(); - delete payload.description; - delete payload.name; - delete payload.meta; - const decoded = patchListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "description", "meta"', () => { - const payload = getPathListSchemaMock(); - delete payload.description; - delete payload.meta; - const decoded = patchListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "description", "name"', () => { - const payload = getPathListSchemaMock(); - delete payload.description; - delete payload.name; - const decoded = patchListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "meta", "name"', () => { - const payload = getPathListSchemaMock(); - delete payload.meta; - delete payload.name; - const decoded = patchListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: PatchListSchema & { extraKey?: string } = getPathListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = patchListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.ts deleted file mode 100644 index e5c477c84424..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.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", 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 * as t from 'io-ts'; -import { version } from '@kbn/securitysolution-io-ts-types'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; -import { _version } from '../../common/underscore_version'; -import { meta } from '../../common/meta'; -import { name } from '../../common/name'; -import { description } from '../../common/description'; - -export const patchListSchema = t.intersection([ - t.exact( - t.type({ - id, - }) - ), - t.exact( - t.partial({ - _version, // is undefined if not set during decode - description, // is undefined if not set during decode - meta, // is undefined if not set during decode - name, // is undefined if not set during decode - version, // is undefined if not set during decode - }) - ), -]); - -export type PatchListSchema = t.OutputOf<typeof patchListSchema>; -export type PatchListSchemaDecoded = RequiredKeepUndefined<t.TypeOf<typeof patchListSchema>>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.mock.ts deleted file mode 100644 index d7850f0cca32..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.mock.ts +++ /dev/null @@ -1,17 +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 { ID, ITEM_ID } from '../../constants/index.mock'; - -import { ReadEndpointListItemSchema } from '.'; - -export const getReadEndpointListItemSchemaMock = (): ReadEndpointListItemSchema => ({ - id: ID, - item_id: ITEM_ID, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.test.ts deleted file mode 100644 index d9de257a3130..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.test.ts +++ /dev/null @@ -1,87 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getReadEndpointListItemSchemaMock } from './index.mock'; -import { ReadEndpointListItemSchema, readEndpointListItemSchema } from '.'; - -describe('read_endpoint_list_item_schema', () => { - test('it should validate a typical list request', () => { - const payload = getReadEndpointListItemSchemaMock(); - const decoded = readEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id"', () => { - const payload = getReadEndpointListItemSchemaMock(); - delete payload.id; - const decoded = readEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "item_id"', () => { - const payload = getReadEndpointListItemSchemaMock(); - delete payload.item_id; - const decoded = readEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept "namespace_type" since endpoint list items do not need it', () => { - const payload: ReadEndpointListItemSchema & { - namespace_type: string; - } = { ...getReadEndpointListItemSchemaMock(), namespace_type: 'single' }; - // @ts-expect-error - delete payload.namespace_type; - const decoded = readEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getReadEndpointListItemSchemaMock()); - }); - - test('it should accept an undefined for "id", "item_id"', () => { - const payload = getReadEndpointListItemSchemaMock(); - delete payload.id; - delete payload.item_id; - const decoded = readEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ReadEndpointListItemSchema & { - extraKey?: string; - } = getReadEndpointListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = readEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.ts deleted file mode 100644 index dbf3a59d79b3..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; -import { item_id } from '../../common/item_id'; - -export const readEndpointListItemSchema = t.exact( - t.partial({ - id, - item_id, - }) -); - -export type ReadEndpointListItemSchema = t.OutputOf<typeof readEndpointListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type ReadEndpointListItemSchemaDecoded = RequiredKeepUndefined< - t.TypeOf<typeof readEndpointListItemSchema> ->; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.mock.ts deleted file mode 100644 index 00e72dc68f5f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.mock.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", 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 { ID, ITEM_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; - -import { ReadExceptionListItemSchema } from '.'; - -export const getReadExceptionListItemSchemaMock = (): ReadExceptionListItemSchema => ({ - id: ID, - item_id: ITEM_ID, - namespace_type: NAMESPACE_TYPE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.test.ts deleted file mode 100644 index e1c461551030..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.test.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", 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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getReadExceptionListItemSchemaMock } from './index.mock'; -import { ReadExceptionListItemSchema, readExceptionListItemSchema } from '.'; - -describe('read_exception_list_item_schema', () => { - test('it should validate a typical exception list request', () => { - const payload = getReadExceptionListItemSchemaMock(); - const decoded = readExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id"', () => { - const payload = getReadExceptionListItemSchemaMock(); - delete payload.id; - const decoded = readExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "item_id"', () => { - const payload = getReadExceptionListItemSchemaMock(); - delete payload.item_id; - const decoded = readExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "namespace_type" but default to "single"', () => { - const payload = getReadExceptionListItemSchemaMock(); - delete payload.namespace_type; - const decoded = readExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getReadExceptionListItemSchemaMock()); - }); - - test('it should accept an undefined for "id", "item_id", "namespace_type" but default "namespace_type" to "single"', () => { - const payload = getReadExceptionListItemSchemaMock(); - delete payload.id; - delete payload.namespace_type; - delete payload.item_id; - const output = getReadExceptionListItemSchemaMock(); - delete output.id; - delete output.item_id; - const decoded = readExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(output); - }); - - test('it should accept an undefined for "id", "item_id"', () => { - const payload = getReadExceptionListItemSchemaMock(); - delete payload.id; - delete payload.item_id; - const decoded = readExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id", "namespace_type" but default "namespace_type" to "single"', () => { - const payload = getReadExceptionListItemSchemaMock(); - delete payload.id; - delete payload.namespace_type; - const output = getReadExceptionListItemSchemaMock(); - delete output.id; - const decoded = readExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(output); - }); - - test('it should accept an undefined for "item_id", "namespace_type" but default "namespace_type" to "single"', () => { - const payload = getReadExceptionListItemSchemaMock(); - delete payload.namespace_type; - delete payload.item_id; - const output = getReadExceptionListItemSchemaMock(); - delete output.item_id; - const decoded = readExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(output); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ReadExceptionListItemSchema & { - extraKey?: string; - } = getReadExceptionListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = readExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.ts deleted file mode 100644 index 6f54ede5ff51..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.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", 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 * as t from 'io-ts'; - -import { NamespaceType } from '../../common/default_namespace'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; -import { item_id } from '../../common/item_id'; -import { namespace_type } from '../../common/namespace_type'; - -export const readExceptionListItemSchema = t.exact( - t.partial({ - id, - item_id, - namespace_type, // defaults to 'single' if not set during decode - }) -); - -export type ReadExceptionListItemSchema = t.OutputOf<typeof readExceptionListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type ReadExceptionListItemSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof readExceptionListItemSchema>>, - 'namespace_type' -> & { - namespace_type: NamespaceType; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.mock.ts deleted file mode 100644 index 47fcba01fd95..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.mock.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", 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 { ID, LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; - -import { ReadExceptionListSchema } from '.'; - -export const getReadExceptionListSchemaMock = (): ReadExceptionListSchema => ({ - id: ID, - list_id: LIST_ID, - namespace_type: NAMESPACE_TYPE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.test.ts deleted file mode 100644 index 2c5a93021b7c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.test.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", 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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getReadExceptionListSchemaMock } from './index.mock'; -import { ReadExceptionListSchema, readExceptionListSchema } from '.'; - -describe('read_exception_list_schema', () => { - test('it should validate a typical exception list request', () => { - const payload = getReadExceptionListSchemaMock(); - const decoded = readExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id"', () => { - const payload = getReadExceptionListSchemaMock(); - delete payload.id; - const decoded = readExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "list_id"', () => { - const payload = getReadExceptionListSchemaMock(); - delete payload.list_id; - const decoded = readExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "namespace_type" but default to "single"', () => { - const payload = getReadExceptionListSchemaMock(); - delete payload.namespace_type; - const decoded = readExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getReadExceptionListSchemaMock()); - }); - - test('it should accept an undefined for "id", "list_id", "namespace_type" but default "namespace_type" to "single"', () => { - const payload = getReadExceptionListSchemaMock(); - delete payload.id; - delete payload.namespace_type; - delete payload.list_id; - const output = getReadExceptionListSchemaMock(); - delete output.id; - delete output.list_id; - const decoded = readExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(output); - }); - - test('it should accept an undefined for "id", "list_id"', () => { - const payload = getReadExceptionListSchemaMock(); - delete payload.id; - delete payload.list_id; - const decoded = readExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id", "namespace_type" but default "namespace_type" to "single"', () => { - const payload = getReadExceptionListSchemaMock(); - delete payload.id; - delete payload.namespace_type; - const output = getReadExceptionListSchemaMock(); - delete output.id; - const decoded = readExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(output); - }); - - test('it should accept an undefined for "list_id", "namespace_type" but default "namespace_type" to "single"', () => { - const payload = getReadExceptionListSchemaMock(); - delete payload.namespace_type; - delete payload.list_id; - const output = getReadExceptionListSchemaMock(); - delete output.list_id; - const decoded = readExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(output); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ReadExceptionListSchema & { - extraKey?: string; - } = getReadExceptionListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = readExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.ts deleted file mode 100644 index fb2e66d2f1de..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.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", 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 * as t from 'io-ts'; - -import { NamespaceType } from '../../common/default_namespace'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; -import { list_id } from '../../common/list_id'; -import { namespace_type } from '../../common/namespace_type'; - -export const readExceptionListSchema = t.exact( - t.partial({ - id, - list_id, - namespace_type, // defaults to 'single' if not set during decode - }) -); - -export type ReadExceptionListSchema = t.OutputOf<typeof readExceptionListSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type ReadExceptionListSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof readExceptionListSchema>>, - 'namespace_type' -> & { - namespace_type: NamespaceType; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.mock.ts deleted file mode 100644 index d78bb6f44bc7..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.mock.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", 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 { LIST_ID, LIST_ITEM_ID, VALUE } from '../../constants/index.mock'; - -import { ReadListItemSchema } from '.'; - -export const getReadListItemSchemaMock = (): ReadListItemSchema => ({ - id: LIST_ITEM_ID, - list_id: LIST_ID, - value: VALUE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.test.ts deleted file mode 100644 index 547537afc673..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.test.ts +++ /dev/null @@ -1,119 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getReadListItemSchemaMock } from './index.mock'; -import { ReadListItemSchema, readListItemSchema } from '.'; - -describe('read_list_item_schema', () => { - test('it should validate a typical list item request', () => { - const payload = getReadListItemSchemaMock(); - const decoded = readListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id"', () => { - const payload = getReadListItemSchemaMock(); - delete payload.id; - const decoded = readListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "list_id"', () => { - const payload = getReadListItemSchemaMock(); - delete payload.list_id; - const decoded = readListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "value"', () => { - const payload = getReadListItemSchemaMock(); - delete payload.value; - const decoded = readListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id", "list_id", "value"', () => { - const payload = getReadListItemSchemaMock(); - delete payload.id; - delete payload.value; - delete payload.list_id; - const decoded = readListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id", "list_id"', () => { - const payload = getReadListItemSchemaMock(); - delete payload.id; - delete payload.list_id; - const decoded = readListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id", "value"', () => { - const payload = getReadListItemSchemaMock(); - delete payload.id; - delete payload.value; - const decoded = readListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "list_id", "value"', () => { - const payload = getReadListItemSchemaMock(); - delete payload.value; - delete payload.list_id; - const decoded = readListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ReadListItemSchema & { extraKey?: string } = getReadListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = readListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.ts deleted file mode 100644 index 3b45ebeea6ba..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; -import { list_id } from '../../common/list_id'; -import { value } from '../../common/value'; - -export const readListItemSchema = t.exact(t.partial({ id, list_id, value })); - -export type ReadListItemSchema = t.OutputOf<typeof readListItemSchema>; -export type ReadListItemSchemaDecoded = RequiredKeepUndefined<t.TypeOf<typeof readListItemSchema>>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.mock.ts deleted file mode 100644 index 5eecbd49e472..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.mock.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", 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 { LIST_ID } from '../../constants/index.mock'; - -import { ReadListSchema } from '.'; - -export const getReadListSchemaMock = (): ReadListSchema => ({ - id: LIST_ID, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.test.ts deleted file mode 100644 index d26f8206c498..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.test.ts +++ /dev/null @@ -1,49 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getReadListSchemaMock } from './index.mock'; -import { ReadListSchema, readListSchema } from '.'; - -describe('read_list_schema', () => { - test('it should validate a typical list item request', () => { - const payload = getReadListSchemaMock(); - const decoded = readListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "id"', () => { - const payload = getReadListSchemaMock(); - // @ts-expect-error - delete payload.id; - const decoded = readListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ReadListSchema & { extraKey?: string } = getReadListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = readListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.ts deleted file mode 100644 index b9c008287139..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -import { id } from '../../common/id'; - -export const readListSchema = t.exact( - t.type({ - id, - }) -); - -export type ReadListSchema = t.OutputOf<typeof readListSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.mock.ts deleted file mode 100644 index 61e8dfbae15f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.mock.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { FILTER, ID, LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; - -import { SummaryExceptionListSchema } from '.'; - -export const getSummaryExceptionListSchemaMock = (): SummaryExceptionListSchema => ({ - filter: FILTER, - id: ID, - list_id: LIST_ID, - namespace_type: NAMESPACE_TYPE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts deleted file mode 100644 index 06db32fa2df3..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts +++ /dev/null @@ -1,138 +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 { left } from 'fp-ts/lib/Either'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getSummaryExceptionListSchemaMock } from './index.mock'; -import { SummaryExceptionListSchema, summaryExceptionListSchema } from '.'; - -describe('summary_exception_list_schema', () => { - test('it should validate a typical exception list request', () => { - const payload = getSummaryExceptionListSchemaMock(); - const decoded = summaryExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = foldLeftRight(checked); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "filter"', () => { - const payload = getSummaryExceptionListSchemaMock(); - delete payload.filter; - const decoded = summaryExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = foldLeftRight(checked); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id"', () => { - const payload = getSummaryExceptionListSchemaMock(); - delete payload.id; - const decoded = summaryExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = foldLeftRight(checked); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "list_id"', () => { - const payload = getSummaryExceptionListSchemaMock(); - delete payload.list_id; - const decoded = summaryExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = foldLeftRight(checked); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "namespace_type" but default to "single"', () => { - const payload = getSummaryExceptionListSchemaMock(); - delete payload.namespace_type; - const decoded = summaryExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = foldLeftRight(checked); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(getSummaryExceptionListSchemaMock()); - }); - - test('it should accept an undefined for "id", "list_id", "namespace_type" but default "namespace_type" to "single"', () => { - const payload = getSummaryExceptionListSchemaMock(); - delete payload.id; - delete payload.namespace_type; - delete payload.list_id; - const output = getSummaryExceptionListSchemaMock(); - delete output.id; - delete output.list_id; - const decoded = summaryExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = foldLeftRight(checked); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(output); - }); - - test('it should accept an undefined for "id", "list_id"', () => { - const payload = getSummaryExceptionListSchemaMock(); - delete payload.id; - delete payload.list_id; - const decoded = summaryExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = foldLeftRight(checked); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "id", "namespace_type" but default "namespace_type" to "single"', () => { - const payload = getSummaryExceptionListSchemaMock(); - delete payload.id; - delete payload.namespace_type; - const output = getSummaryExceptionListSchemaMock(); - delete output.id; - const decoded = summaryExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = foldLeftRight(checked); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(output); - }); - - test('it should accept an undefined for "list_id", "namespace_type" but default "namespace_type" to "single"', () => { - const payload = getSummaryExceptionListSchemaMock(); - delete payload.namespace_type; - delete payload.list_id; - const output = getSummaryExceptionListSchemaMock(); - delete output.list_id; - const decoded = summaryExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = foldLeftRight(checked); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(output); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: SummaryExceptionListSchema & { - extraKey?: string; - } = getSummaryExceptionListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = summaryExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = foldLeftRight(checked); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.ts deleted file mode 100644 index 2815dfa05b27..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.ts +++ /dev/null @@ -1,37 +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 * as t from 'io-ts'; - -import { NamespaceType } from '../../common/default_namespace'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; -import { filter, Filter } from '../../common/filter'; -import { list_id } from '../../common/list_id'; -import { namespace_type } from '../../common/namespace_type'; - -export const summaryExceptionListSchema = t.exact( - t.partial({ - filter, - id, - list_id, - namespace_type, // defaults to 'single' if not set during decode - }) -); - -export type SummaryExceptionListSchema = t.OutputOf<typeof summaryExceptionListSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type SummaryExceptionListSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof summaryExceptionListSchema>>, - 'namespace_type' -> & { - namespace_type: NamespaceType; - filter: Filter; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.mock.ts deleted file mode 100644 index 8b34549a8c90..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.mock.ts +++ /dev/null @@ -1,37 +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 { - COMMENTS, - DESCRIPTION, - ENTRIES, - ID, - ITEM_TYPE, - LIST_ITEM_ID, - META, - NAME, - OS_TYPES, - TAGS, -} from '../../constants/index.mock'; - -import { UpdateEndpointListItemSchema } from '.'; - -export const getUpdateEndpointListItemSchemaMock = (): UpdateEndpointListItemSchema => ({ - _version: undefined, - comments: COMMENTS, - description: DESCRIPTION, - entries: ENTRIES, - id: ID, - item_id: LIST_ITEM_ID, - meta: META, - name: NAME, - os_types: OS_TYPES, - tags: TAGS, - type: ITEM_TYPE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.test.ts deleted file mode 100644 index f595ecd1ebbe..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.test.ts +++ /dev/null @@ -1,140 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { UpdateEndpointListItemSchema, updateEndpointListItemSchema } from '.'; -import { getUpdateEndpointListItemSchemaMock } from './index.mock'; - -describe('update_endpoint_list_item_schema', () => { - test('it should validate a typical list item request', () => { - const payload = getUpdateEndpointListItemSchemaMock(); - const decoded = updateEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not accept an undefined for "description"', () => { - const payload = getUpdateEndpointListItemSchemaMock(); - // @ts-expect-error - delete payload.description; - const decoded = updateEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not accept an undefined for "name"', () => { - const payload = getUpdateEndpointListItemSchemaMock(); - // @ts-expect-error - delete payload.name; - const decoded = updateEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "name"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not accept an undefined for "type"', () => { - const payload = getUpdateEndpointListItemSchemaMock(); - // @ts-expect-error - delete payload.type; - const decoded = updateEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not accept a value for "list_id"', () => { - const payload: UpdateEndpointListItemSchema & { - list_id?: string; - } = getUpdateEndpointListItemSchemaMock(); - payload.list_id = 'some new list_id'; - const decoded = updateEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "list_id"']); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "meta" but strip it out', () => { - const payload = getUpdateEndpointListItemSchemaMock(); - const outputPayload = getUpdateEndpointListItemSchemaMock(); - delete payload.meta; - const decoded = updateEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete outputPayload.meta; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should accept an undefined for "comments" but return an array', () => { - const inputPayload = getUpdateEndpointListItemSchemaMock(); - const outputPayload = getUpdateEndpointListItemSchemaMock(); - delete inputPayload.comments; - outputPayload.comments = []; - const decoded = updateEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should NOT accept an undefined for "entries"', () => { - const inputPayload = getUpdateEndpointListItemSchemaMock(); - const outputPayload = getUpdateEndpointListItemSchemaMock(); - // @ts-expect-error - delete inputPayload.entries; - outputPayload.entries = []; - const decoded = updateEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "tags" but return an array', () => { - const inputPayload = getUpdateEndpointListItemSchemaMock(); - const outputPayload = getUpdateEndpointListItemSchemaMock(); - delete inputPayload.tags; - outputPayload.tags = []; - const decoded = updateEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: UpdateEndpointListItemSchema & { - extraKey?: string; - } = getUpdateEndpointListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = updateEndpointListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.ts deleted file mode 100644 index 7851d80ef2ef..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.ts +++ /dev/null @@ -1,59 +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 * as t from 'io-ts'; - -import { DefaultUpdateCommentsArray } from '../../common/default_update_comments_array'; -import { exceptionListItemType } from '../../common/exception_list_item_type'; -import { nonEmptyEntriesArray } from '../../common/non_empty_entries_array'; -import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; -import { description } from '../../common/description'; -import { name } from '../../common/name'; -import { _version } from '../../common/underscore_version'; -import { id } from '../../common/id'; -import { meta } from '../../common/meta'; -import { Tags, tags } from '../../common/tags'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { UpdateCommentsArray } from '../../common/update_comment'; -import { EntriesArray } from '../../common/entries'; - -export const updateEndpointListItemSchema = t.intersection([ - t.exact( - t.type({ - description, - entries: nonEmptyEntriesArray, - name, - type: exceptionListItemType, - }) - ), - t.exact( - t.partial({ - _version, // defaults to undefined if not set during decode - comments: DefaultUpdateCommentsArray, // defaults to empty array if not set during decode - id, // defaults to undefined if not set during decode - item_id: t.union([t.string, t.undefined]), - meta, // defaults to undefined if not set during decode - os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode - tags, // defaults to empty array if not set during decode - }) - ), -]); - -export type UpdateEndpointListItemSchema = t.OutputOf<typeof updateEndpointListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type UpdateEndpointListItemSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof updateEndpointListItemSchema>>, - 'tags' | 'entries' | 'comments' -> & { - comments: UpdateCommentsArray; - tags: Tags; - entries: EntriesArray; - os_types: OsTypeArray; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.mock.ts deleted file mode 100644 index fce5d2f7e18b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.mock.ts +++ /dev/null @@ -1,53 +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 { - COMMENTS, - DESCRIPTION, - ENTRIES, - ID, - ITEM_ID, - ITEM_TYPE, - LIST_ITEM_ID, - META, - NAME, - NAMESPACE_TYPE, - OS_TYPES, - TAGS, -} from '../../constants/index.mock'; - -import { UpdateExceptionListItemSchema } from '.'; - -export const getUpdateExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ - _version: undefined, - comments: COMMENTS, - description: DESCRIPTION, - entries: ENTRIES, - id: ID, - item_id: LIST_ITEM_ID, - meta: META, - name: NAME, - namespace_type: NAMESPACE_TYPE, - os_types: ['linux'], - tags: TAGS, - type: ITEM_TYPE, -}); - -/** - * Useful for end to end tests and other mechanisms which want to fill in the values - * after doing a get of the structure. - */ -export const getUpdateMinimalExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ - description: DESCRIPTION, - entries: ENTRIES, - item_id: ITEM_ID, - name: NAME, - os_types: OS_TYPES, - type: ITEM_TYPE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.test.ts deleted file mode 100644 index 84e9b801c171..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.test.ts +++ /dev/null @@ -1,162 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { UpdateExceptionListItemSchema, updateExceptionListItemSchema } from '.'; -import { getUpdateExceptionListItemSchemaMock } from './index.mock'; - -describe('update_exception_list_item_schema', () => { - test('it should validate a typical exception list item request', () => { - const payload = getUpdateExceptionListItemSchemaMock(); - const decoded = updateExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not accept an undefined for "description"', () => { - const payload = getUpdateExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.description; - const decoded = updateExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not accept an undefined for "name"', () => { - const payload = getUpdateExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.name; - const decoded = updateExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "name"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not accept an undefined for "type"', () => { - const payload = getUpdateExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.type; - const decoded = updateExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not accept a value for "list_id"', () => { - const payload: UpdateExceptionListItemSchema & { - list_id?: string; - } = getUpdateExceptionListItemSchemaMock(); - payload.list_id = 'some new list_id'; - const decoded = updateExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "list_id"']); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "meta" but strip it out', () => { - const payload = getUpdateExceptionListItemSchemaMock(); - const outputPayload = getUpdateExceptionListItemSchemaMock(); - delete payload.meta; - const decoded = updateExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete outputPayload.meta; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should accept an undefined for "comments" but return an array', () => { - const inputPayload = getUpdateExceptionListItemSchemaMock(); - const outputPayload = getUpdateExceptionListItemSchemaMock(); - delete inputPayload.comments; - outputPayload.comments = []; - const decoded = updateExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should NOT accept an undefined for "entries"', () => { - const inputPayload = getUpdateExceptionListItemSchemaMock(); - const outputPayload = getUpdateExceptionListItemSchemaMock(); - // @ts-expect-error - delete inputPayload.entries; - outputPayload.entries = []; - const decoded = updateExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "namespace_type" but return enum "single"', () => { - const inputPayload = getUpdateExceptionListItemSchemaMock(); - const outputPayload = getUpdateExceptionListItemSchemaMock(); - delete inputPayload.namespace_type; - outputPayload.namespace_type = 'single'; - const decoded = updateExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should accept an undefined for "tags" but return an array', () => { - const inputPayload = getUpdateExceptionListItemSchemaMock(); - const outputPayload = getUpdateExceptionListItemSchemaMock(); - delete inputPayload.tags; - outputPayload.tags = []; - const decoded = updateExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should accept an undefined for "item_id" and generate a correct body not counting the uuid', () => { - const inputPayload = getUpdateExceptionListItemSchemaMock(); - delete inputPayload.item_id; - const decoded = updateExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as UpdateExceptionListItemSchema).item_id; - expect(message.schema).toEqual(inputPayload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: UpdateExceptionListItemSchema & { - extraKey?: string; - } = getUpdateExceptionListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = updateExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.ts deleted file mode 100644 index b0190d8fa388..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.ts +++ /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", 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 * as t from 'io-ts'; -import { ExceptionListItemEntryArray } from '@kbn/securitysolution-exceptions-common/api'; -import { NamespaceType } from '../../common/default_namespace'; -import { DefaultUpdateCommentsArray } from '../../common/default_update_comments_array'; -import { exceptionListItemType } from '../../common/exception_list_item_type'; -import { nonEmptyEntriesArray } from '../../common/non_empty_entries_array'; -import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { Tags, tags } from '../../common/tags'; -import { UpdateCommentsArray } from '../../common/update_comment'; -import { description } from '../../common/description'; -import { name } from '../../common/name'; -import { _version } from '../../common/underscore_version'; -import { id } from '../../common/id'; -import { item_id } from '../../common/item_id'; -import { meta } from '../../common/meta'; -import { namespace_type } from '../../common/namespace_type'; -import { ExpireTimeOrUndefined, expireTimeOrUndefined } from '../../common'; - -export const updateExceptionListItemSchema = t.intersection([ - t.exact( - t.type({ - description, - entries: nonEmptyEntriesArray, - name, - type: exceptionListItemType, - }) - ), - t.exact( - t.partial({ - _version, // defaults to undefined if not set during decode - comments: DefaultUpdateCommentsArray, // defaults to empty array if not set during decode - expire_time: expireTimeOrUndefined, - id, // defaults to undefined if not set during decode - item_id, - meta, // defaults to undefined if not set during decode - namespace_type, // defaults to 'single' if not set during decode - os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode - tags, // defaults to empty array if not set during decode - }) - ), -]); - -export type UpdateExceptionListItemSchema = t.OutputOf<typeof updateExceptionListItemSchema>; - -// This type is used after a decode since some things are defaults after a decode. -export type UpdateExceptionListItemSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof updateExceptionListItemSchema>>, - 'tags' | 'entries' | 'namespace_type' | 'comments' | 'os_types' | 'expire_time' -> & { - comments: UpdateCommentsArray; - tags: Tags; - entries: ExceptionListItemEntryArray; - namespace_type: NamespaceType; - os_types: OsTypeArray; - expire_time: ExpireTimeOrUndefined; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.mock.ts deleted file mode 100644 index ff98cc71f03f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.mock.ts +++ /dev/null @@ -1,36 +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 { DESCRIPTION, ID, LIST_ID, META, NAME, NAMESPACE_TYPE } from '../../constants/index.mock'; - -import { UpdateExceptionListSchema } from '.'; - -export const getUpdateExceptionListSchemaMock = (): UpdateExceptionListSchema => ({ - _version: undefined, - description: DESCRIPTION, - id: ID, - list_id: LIST_ID, - meta: META, - name: NAME, - namespace_type: NAMESPACE_TYPE, - os_types: [], - tags: ['malware'], - type: 'endpoint', -}); - -/** - * Useful for end to end tests and other mechanisms which want to fill in the values - * after doing a get of the structure. - */ -export const getUpdateMinimalExceptionListSchemaMock = (): UpdateExceptionListSchema => ({ - description: DESCRIPTION, - list_id: LIST_ID, - name: NAME, - type: 'endpoint', -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.test.ts deleted file mode 100644 index 4821251f36d1..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.test.ts +++ /dev/null @@ -1,123 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { UpdateExceptionListSchema, updateExceptionListSchema } from '.'; -import { getUpdateExceptionListSchemaMock } from './index.mock'; - -describe('update_exception_list_schema', () => { - test('it should validate a typical exception list request', () => { - const payload = getUpdateExceptionListSchemaMock(); - const decoded = updateExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not accept an undefined for "description"', () => { - const payload = getUpdateExceptionListSchemaMock(); - // @ts-expect-error - delete payload.description; - const decoded = updateExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not accept an undefined for "name"', () => { - const payload = getUpdateExceptionListSchemaMock(); - // @ts-expect-error - delete payload.name; - const decoded = updateExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "name"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not accept an undefined for "type"', () => { - const payload = getUpdateExceptionListSchemaMock(); - // @ts-expect-error - delete payload.type; - const decoded = updateExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "meta" but strip it out', () => { - const payload = getUpdateExceptionListSchemaMock(); - const outputPayload = getUpdateExceptionListSchemaMock(); - delete payload.meta; - const decoded = updateExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete outputPayload.meta; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should accept an undefined for "namespace_type" but return enum "single"', () => { - const inputPayload = getUpdateExceptionListSchemaMock(); - const outputPayload = getUpdateExceptionListSchemaMock(); - delete inputPayload.namespace_type; - outputPayload.namespace_type = 'single'; - const decoded = updateExceptionListSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should accept an undefined for "tags" but return an array', () => { - const inputPayload = getUpdateExceptionListSchemaMock(); - const outputPayload = getUpdateExceptionListSchemaMock(); - delete inputPayload.tags; - outputPayload.tags = []; - const decoded = updateExceptionListSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should accept an undefined for "list_id" and generate a correct body not counting the uuid', () => { - const inputPayload = getUpdateExceptionListSchemaMock(); - delete inputPayload.list_id; - const decoded = updateExceptionListSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as UpdateExceptionListSchema).list_id; - expect(message.schema).toEqual(inputPayload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: UpdateExceptionListSchema & { - extraKey?: string; - } = getUpdateExceptionListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = updateExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.ts deleted file mode 100644 index 62add37568ba..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.ts +++ /dev/null @@ -1,58 +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 * as t from 'io-ts'; - -import { version } from '@kbn/securitysolution-io-ts-types'; -import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { Tags, tags } from '../../common/tags'; -import { NamespaceType } from '../../common/default_namespace'; -import { description } from '../../common/description'; -import { name } from '../../common/name'; -import { _version } from '../../common/underscore_version'; -import { exceptionListType } from '../../common/exception_list'; -import { id } from '../../common/id'; -import { list_id } from '../../common/list_id'; -import { meta } from '../../common/meta'; -import { namespace_type } from '../../common/namespace_type'; - -export const updateExceptionListSchema = t.intersection([ - t.exact( - t.type({ - description, - name, - type: exceptionListType, - }) - ), - t.exact( - t.partial({ - _version, // defaults to undefined if not set during decode - id, // defaults to undefined if not set during decode - list_id, // defaults to undefined if not set during decode - meta, // defaults to undefined if not set during decode - namespace_type, // defaults to 'single' if not set during decode - os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode - tags, // defaults to empty array if not set during decode - version, // defaults to undefined if not set during decode - }) - ), -]); - -export type UpdateExceptionListSchema = t.OutputOf<typeof updateExceptionListSchema>; - -// This type is used after a decode since the arrays turn into defaults of empty arrays. -export type UpdateExceptionListSchemaDecoded = Omit< - RequiredKeepUndefined<t.TypeOf<typeof updateExceptionListSchema>>, - 'tags | namespace_type' | 'os_types' -> & { - tags: Tags; - namespace_type: NamespaceType; - os_types: OsTypeArray; -}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.mock.ts deleted file mode 100644 index 9c3b2dbbd7d3..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.mock.ts +++ /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", 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 { ID, LIST_ITEM_ID, META, VALUE } from '../../constants/index.mock'; - -import { UpdateListItemSchema } from '.'; - -export const getUpdateListItemSchemaMock = (): UpdateListItemSchema => ({ - id: ID, - meta: META, - value: VALUE, -}); - -/** - * Useful for end to end testing - */ -export const getUpdateMinimalListItemSchemaMock = (): UpdateListItemSchema => ({ - id: LIST_ITEM_ID, - value: VALUE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.test.ts deleted file mode 100644 index 6c7637cbde05..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { UpdateListItemSchema, updateListItemSchema } from '.'; -import { getUpdateListItemSchemaMock } from './index.mock'; - -describe('update_list_item_schema', () => { - test('it should validate a typical list item request', () => { - const payload = getUpdateListItemSchemaMock(); - const decoded = updateListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "meta" but strip it out', () => { - const payload = getUpdateListItemSchemaMock(); - const outputPayload = getUpdateListItemSchemaMock(); - delete payload.meta; - const decoded = updateListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete outputPayload.meta; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: UpdateListItemSchema & { - extraKey?: string; - } = getUpdateListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = updateListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.ts deleted file mode 100644 index 613dde36ef0f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.ts +++ /dev/null @@ -1,36 +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 * as t from 'io-ts'; - -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { _version } from '../../common/underscore_version'; -import { id } from '../../common/id'; -import { value } from '../../common/value'; -import { meta } from '../../common/meta'; - -export const updateListItemSchema = t.intersection([ - t.exact( - t.type({ - id, - value, - }) - ), - t.exact( - t.partial({ - _version, // defaults to undefined if not set during decode - meta, // defaults to undefined if not set during decode - }) - ), -]); - -export type UpdateListItemSchema = t.OutputOf<typeof updateListItemSchema>; -export type UpdateListItemSchemaDecoded = RequiredKeepUndefined< - t.TypeOf<typeof updateListItemSchema> ->; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.mock.ts deleted file mode 100644 index 5f1fc8fe926d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.mock.ts +++ /dev/null @@ -1,30 +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 { DESCRIPTION, LIST_ID, META, NAME, _VERSION } from '../../constants/index.mock'; - -import { UpdateListSchema } from '.'; - -export const getUpdateListSchemaMock = (): UpdateListSchema => ({ - _version: _VERSION, - description: DESCRIPTION, - id: LIST_ID, - meta: META, - name: NAME, -}); - -/** - * Useful for end to end tests and other mechanisms which want to fill in the values - * after doing a get of the structure. - */ -export const getUpdateMinimalListSchemaMock = (): UpdateListSchema => ({ - description: DESCRIPTION, - id: LIST_ID, - name: NAME, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.test.ts deleted file mode 100644 index cac97d7c32cf..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { UpdateListSchema, updateListSchema } from '.'; -import { getUpdateListSchemaMock } from './index.mock'; - -describe('update_list_schema', () => { - test('it should validate a typical list request', () => { - const payload = getUpdateListSchemaMock(); - const decoded = updateListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "meta" but strip it out', () => { - const payload = getUpdateListSchemaMock(); - const outputPayload = getUpdateListSchemaMock(); - delete payload.meta; - const decoded = updateListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - delete outputPayload.meta; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: UpdateListSchema & { - extraKey?: string; - } = getUpdateListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = updateListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.ts deleted file mode 100644 index 41ddb81c36e3..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.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", 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 * as t from 'io-ts'; - -import { version } from '@kbn/securitysolution-io-ts-types'; -import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; -import { id } from '../../common/id'; -import { name } from '../../common/name'; -import { description } from '../../common/description'; -import { _version } from '../../common/underscore_version'; -import { meta } from '../../common/meta'; - -export const updateListSchema = t.intersection([ - t.exact( - t.type({ - description, - id, - name, - }) - ), - t.exact( - t.partial({ - _version, // defaults to undefined if not set during decode - meta, // defaults to undefined if not set during decode - version, // defaults to undefined if not set during decode - }) - ), -]); - -export type UpdateListSchema = t.OutputOf<typeof updateListSchema>; -export type UpdateListSchemaDecoded = RequiredKeepUndefined<t.TypeOf<typeof updateListSchema>>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.mock.ts deleted file mode 100644 index c532dd740197..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.mock.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", 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 { AcknowledgeSchema } from '.'; - -export const getAcknowledgeSchemaResponseMock = (): AcknowledgeSchema => ({ - acknowledged: true, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.test.ts deleted file mode 100644 index 2d3086b02315..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getAcknowledgeSchemaResponseMock } from './index.mock'; -import { AcknowledgeSchema, acknowledgeSchema } from '.'; - -describe('acknowledge_schema', () => { - test('it should validate a typical response', () => { - const payload = getAcknowledgeSchemaResponseMock(); - const decoded = acknowledgeSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - test('it should NOT accept an undefined for "ok"', () => { - const payload = getAcknowledgeSchemaResponseMock(); - // @ts-expect-error - delete payload.acknowledged; - const decoded = acknowledgeSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "acknowledged"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: AcknowledgeSchema & { extraKey?: string } = getAcknowledgeSchemaResponseMock(); - payload.extraKey = 'some new value'; - const decoded = acknowledgeSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.ts deleted file mode 100644 index d506f038534d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.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", 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 * as t from 'io-ts'; - -export const acknowledgeSchema = t.exact(t.type({ acknowledged: t.boolean })); - -export type AcknowledgeSchema = t.TypeOf<typeof acknowledgeSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.test.ts deleted file mode 100644 index 52c72cf0e8cb..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.test.ts +++ /dev/null @@ -1,75 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { CreateEndpointListSchema, createEndpointListSchema } from '.'; -import { getExceptionListSchemaMock } from '../exception_list_schema/index.mock'; - -describe('create_endpoint_list_schema', () => { - test('it should validate a typical endpoint list response', () => { - const payload = getExceptionListSchemaMock(); - const decoded = createEndpointListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an empty object when an endpoint list already exists', () => { - const payload: CreateEndpointListSchema = {}; - const decoded = createEndpointListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "list_id"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.list_id; - const decoded = createEndpointListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'invalid keys "_version,created_at,created_by,description,id,immutable,meta,{},name,namespace_type,os_types,["linux"],tags,["user added string for a tag","malware"],tie_breaker_id,type,updated_at,updated_by,version"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT allow missing fields', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.list_id; - const decoded = createEndpointListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors)).length).toEqual(1); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: CreateEndpointListSchema & { - extraKey?: string; - } = getExceptionListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = createEndpointListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.ts deleted file mode 100644 index cdcdc8e67e07..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.ts +++ /dev/null @@ -1,15 +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 * as t from 'io-ts'; -import { exceptionListSchema } from '../exception_list_schema'; - -export const createEndpointListSchema = t.union([exceptionListSchema, t.exact(t.type({}))]); - -export type CreateEndpointListSchema = t.TypeOf<typeof createEndpointListSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.mock.ts deleted file mode 100644 index 2c0449ea900e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.mock.ts +++ /dev/null @@ -1,72 +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 { - COMMENTS, - DATE_NOW, - DESCRIPTION, - ELASTIC_USER, - ENTRIES, - ITEM_ID, - ITEM_TYPE, - LIST_ID, - META, - NAME, - NAMESPACE_TYPE, - OS_TYPES, - TIE_BREAKER, - USER, -} from '../../constants/index.mock'; - -import { ExceptionListItemSchema } from '.'; - -export const getExceptionListItemSchemaMock = ( - overrides?: Partial<ExceptionListItemSchema> -): ExceptionListItemSchema => ({ - _version: undefined, - comments: COMMENTS, - created_at: DATE_NOW, - created_by: USER, - description: DESCRIPTION, - entries: ENTRIES, - expire_time: undefined, - id: '1', - item_id: 'endpoint_list_item', - list_id: 'endpoint_list_id', - meta: META, - name: NAME, - namespace_type: NAMESPACE_TYPE, - os_types: ['linux'], - tags: ['user added string for a tag', 'malware'], - tie_breaker_id: TIE_BREAKER, - type: ITEM_TYPE, - updated_at: DATE_NOW, - updated_by: USER, - ...(overrides || {}), -}); - -/** - * This is useful for end to end tests where we remove the auto generated parts for comparisons - * such as created_at, updated_at, and id. - */ -export const getExceptionListItemResponseMockWithoutAutoGeneratedValues = - (): Partial<ExceptionListItemSchema> => ({ - comments: [], - created_by: ELASTIC_USER, - description: DESCRIPTION, - entries: ENTRIES, - item_id: ITEM_ID, - list_id: LIST_ID, - name: NAME, - namespace_type: 'single', - os_types: OS_TYPES, - tags: [], - type: ITEM_TYPE, - updated_by: ELASTIC_USER, - }); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.test.ts deleted file mode 100644 index 9c98d97a596a..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.test.ts +++ /dev/null @@ -1,242 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getExceptionListItemSchemaMock } from './index.mock'; -import { ExceptionListItemSchema, exceptionListItemSchema } from '.'; - -describe('exception_list_item_schema', () => { - test('it should validate a typical exception list item response', () => { - const payload = getExceptionListItemSchemaMock(); - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "id"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.id; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "list_id"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.list_id; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "item_id"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.item_id; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "item_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "comments"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.comments; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "comments"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "entries"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.entries; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "name"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.name; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "name"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "namespace_type" and return "single" as a default value for "namespace_type"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.namespace_type; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect((message.schema as ExceptionListItemSchema).namespace_type).toEqual('single'); - }); - - test('it should NOT accept an undefined for "description"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.description; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "meta"', () => { - const payload = getExceptionListItemSchemaMock(); - delete payload.meta; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "created_at"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.created_at; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "created_at"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "created_by"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.created_by; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "created_by"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "tie_breaker_id"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.tie_breaker_id; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "tie_breaker_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "type"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.type; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "updated_at"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.updated_at; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "updated_at"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "updated_by"', () => { - const payload = getExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.updated_by; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "updated_by"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ExceptionListItemSchema & { - extraKey?: string; - } = getExceptionListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = exceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.ts deleted file mode 100644 index 5408aa581bdf..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.ts +++ /dev/null @@ -1,56 +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 * as t from 'io-ts'; - -import { namespace_type } from '../../common/namespace_type'; -import { metaOrUndefined } from '../../common/meta'; -import { name } from '../../common/name'; -import { created_at } from '../../common/created_at'; -import { created_by } from '../../common/created_by'; -import { id } from '../../common/id'; -import { tie_breaker_id } from '../../common/tie_breaker_id'; -import { updated_at } from '../../common/updated_at'; -import { updated_by } from '../../common/updated_by'; -import { list_id } from '../../common/list_id'; -import { description } from '../../common/description'; -import { osTypeArray } from '../../common/os_type'; -import { tags } from '../../common/tags'; -import { _versionOrUndefined } from '../../common/underscore_version'; -import { commentsArray } from '../../common/comment'; -import { entriesArray } from '../../common/entries'; -import { item_id } from '../../common/item_id'; -import { exceptionListItemType } from '../../common/exception_list_item_type'; -import { expireTimeOrUndefined } from '../../common/expire_time'; - -export const exceptionListItemSchema = t.exact( - t.type({ - _version: _versionOrUndefined, - comments: commentsArray, - created_at, - created_by, - description, - entries: entriesArray, - expire_time: expireTimeOrUndefined, - id, - item_id, - list_id, - meta: metaOrUndefined, - name, - namespace_type, - os_types: osTypeArray, - tags, - tie_breaker_id, - type: exceptionListItemType, - updated_at, - updated_by, - }) -); - -export type ExceptionListItemSchema = t.TypeOf<typeof exceptionListItemSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.mock.ts deleted file mode 100644 index de2efb85c337..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.mock.ts +++ /dev/null @@ -1,79 +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 { - DATE_NOW, - DESCRIPTION, - ELASTIC_USER, - ENDPOINT_TYPE, - IMMUTABLE, - LIST_ID, - META, - NAME, - TIE_BREAKER, - USER, - VERSION, - _VERSION, -} from '../../constants/index.mock'; -import { - ENDPOINT_LIST_ID, - ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION, - ENDPOINT_TRUSTED_APPS_LIST_ID, - ENDPOINT_TRUSTED_APPS_LIST_NAME, -} from '@kbn/securitysolution-list-constants'; - -import { ExceptionListSchema } from '.'; - -export const getExceptionListSchemaMock = (): ExceptionListSchema => ({ - _version: _VERSION, - created_at: DATE_NOW, - created_by: USER, - description: DESCRIPTION, - id: '1', - immutable: IMMUTABLE, - list_id: ENDPOINT_LIST_ID, - meta: META, - name: 'Sample Endpoint Exception List', - namespace_type: 'agnostic', - os_types: ['linux'], - tags: ['user added string for a tag', 'malware'], - tie_breaker_id: TIE_BREAKER, - type: ENDPOINT_TYPE, - updated_at: DATE_NOW, - updated_by: 'user_name', - version: VERSION, -}); - -export const getTrustedAppsListSchemaMock = (): ExceptionListSchema => { - return { - ...getExceptionListSchemaMock(), - description: ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION, - list_id: ENDPOINT_TRUSTED_APPS_LIST_ID, - name: ENDPOINT_TRUSTED_APPS_LIST_NAME, - }; -}; - -/** - * This is useful for end to end tests where we remove the auto generated parts for comparisons - * such as created_at, updated_at, and id. - */ -export const getExceptionResponseMockWithoutAutoGeneratedValues = - (): Partial<ExceptionListSchema> => ({ - created_by: ELASTIC_USER, - description: DESCRIPTION, - immutable: IMMUTABLE, - list_id: LIST_ID, - name: NAME, - namespace_type: 'single', - os_types: [], - tags: [], - type: ENDPOINT_TYPE, - updated_by: ELASTIC_USER, - version: VERSION, - }); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.test.ts deleted file mode 100644 index e8dc043bd6b0..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.test.ts +++ /dev/null @@ -1,200 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getExceptionListSchemaMock } from './index.mock'; -import { ExceptionListSchema, exceptionListSchema } from '.'; - -describe('exception_list_schema', () => { - test('it should validate a typical exception list response', () => { - const payload = getExceptionListSchemaMock(); - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "id"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.id; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "list_id"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.list_id; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "name"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.name; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "name"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "namespace_type" and make "namespace_type" that of "single"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.namespace_type; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect((message.schema as ExceptionListSchema).namespace_type).toEqual('single'); - }); - - test('it should NOT accept an undefined for "description"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.description; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "meta"', () => { - const payload = getExceptionListSchemaMock(); - delete payload.meta; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "created_at"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.created_at; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "created_at"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "created_by"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.created_by; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "created_by"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "tie_breaker_id"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.tie_breaker_id; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "tie_breaker_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "type"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.type; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "updated_at"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.updated_at; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "updated_at"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "updated_by"', () => { - const payload = getExceptionListSchemaMock(); - // @ts-expect-error - delete payload.updated_by; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "updated_by"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ExceptionListSchema & { - extraKey?: string; - } = getExceptionListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = exceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.ts deleted file mode 100644 index 0667153c2777..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.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 * as t from 'io-ts'; - -import { version } from '@kbn/securitysolution-io-ts-types'; -import { _versionOrUndefined } from '../../common/underscore_version'; -import { namespace_type } from '../../common/namespace_type'; -import { metaOrUndefined } from '../../common/meta'; -import { name } from '../../common/name'; -import { created_at } from '../../common/created_at'; -import { created_by } from '../../common/created_by'; -import { id } from '../../common/id'; -import { tie_breaker_id } from '../../common/tie_breaker_id'; -import { immutable } from '../../common/immutable'; -import { updated_at } from '../../common/updated_at'; -import { updated_by } from '../../common/updated_by'; -import { list_id } from '../../common/list_id'; -import { description } from '../../common/description'; -import { osTypeArray } from '../../common/os_type'; -import { exceptionListType } from '../../common/exception_list'; -import { tags } from '../../common/tags'; - -export const exceptionListSchema = t.exact( - t.type({ - _version: _versionOrUndefined, - created_at, - created_by, - description, - id, - immutable, - list_id, - meta: metaOrUndefined, - name, - namespace_type, - os_types: osTypeArray, - tags, - tie_breaker_id, - type: exceptionListType, - updated_at, - updated_by, - version, - }) -); - -export type ExceptionListSchema = t.TypeOf<typeof exceptionListSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.mock.ts deleted file mode 100644 index 1decbde1374b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.mock.ts +++ /dev/null @@ -1,17 +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 { ExceptionListSummarySchema } from '.'; - -export const getListSummaryResponseMock = (): ExceptionListSummarySchema => ({ - windows: 0, - linux: 1, - macos: 2, - total: 3, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.test.ts deleted file mode 100644 index ad1d8b737e46..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.test.ts +++ /dev/null @@ -1,95 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getListSummaryResponseMock } from './index.mock'; -import { ExceptionListSummarySchema, exceptionListSummarySchema } from '.'; - -describe('list_summary_schema', () => { - test('it should validate a typical list summary response', () => { - const payload = getListSummaryResponseMock(); - const decoded = exceptionListSummarySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "windows"', () => { - const payload = getListSummaryResponseMock(); - // @ts-expect-error - delete payload.windows; - const decoded = exceptionListSummarySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "windows"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "linux"', () => { - const payload = getListSummaryResponseMock(); - // @ts-expect-error - delete payload.linux; - const decoded = exceptionListSummarySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "linux"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "macos"', () => { - const payload = getListSummaryResponseMock(); - // @ts-expect-error - delete payload.macos; - const decoded = exceptionListSummarySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "macos"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "total"', () => { - const payload = getListSummaryResponseMock(); - // @ts-expect-error - delete payload.total; - const decoded = exceptionListSummarySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "total"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ExceptionListSummarySchema & { - extraKey?: string; - } = getListSummaryResponseMock(); - payload.extraKey = 'some new value'; - const decoded = exceptionListSummarySchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts deleted file mode 100644 index a14eaba10b78..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.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 { PositiveInteger } from '@kbn/securitysolution-io-ts-types'; -import * as t from 'io-ts'; - -export const exceptionListSummarySchema = t.exact( - t.type({ - windows: PositiveInteger, - linux: PositiveInteger, - macos: PositiveInteger, - total: PositiveInteger, - }) -); - -export type ExceptionListSummarySchema = t.TypeOf<typeof exceptionListSummarySchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.mock.ts deleted file mode 100644 index 7f6ec6e8bd5c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.mock.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", 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 { FoundAllListItemsSchema } from '.'; -import { getListItemResponseMock } from '../list_item_schema/index.mock'; - -export const getFoundAllListItemsSchemaMock = (): FoundAllListItemsSchema => ({ - data: [getListItemResponseMock()], - total: 1, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.ts deleted file mode 100644 index f8e6dc43647f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.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 * as t from 'io-ts'; - -import { listItemSchema } from '../list_item_schema'; -import { total } from '../../common/total'; - -export const foundAllListItemsSchema = t.exact( - t.type({ - data: t.array(listItemSchema), - total, - }) -); - -export type FoundAllListItemsSchema = t.TypeOf<typeof foundAllListItemsSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.mock.ts deleted file mode 100644 index 8a7abc59a9cd..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.mock.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", 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 { FoundExceptionListItemSchema } from '.'; -import { getExceptionListItemSchemaMock } from '../exception_list_item_schema/index.mock'; - -export const getFoundExceptionListItemSchemaMock = (): FoundExceptionListItemSchema => ({ - data: [getExceptionListItemSchemaMock()], - page: 1, - per_page: 1, - total: 1, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.test.ts deleted file mode 100644 index eeaeee26fac4..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.test.ts +++ /dev/null @@ -1,154 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getFoundExceptionListItemSchemaMock } from './index.mock'; -import { FoundExceptionListItemSchema, foundExceptionListItemSchema } from '.'; -import { ExceptionListItemSchema } from '../exception_list_item_schema'; -import { getExceptionListItemSchemaMock } from '../exception_list_item_schema/index.mock'; - -describe('found_exception_list_item_schema', () => { - test('it should validate a typical exception list response', () => { - const payload = getFoundExceptionListItemSchemaMock(); - const decoded = foundExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept a malformed exception list item within "data"', () => { - const item: Omit<ExceptionListItemSchema, 'entries'> & { - entries?: string; - } = { ...getExceptionListItemSchemaMock(), entries: 'I should be an array' }; - const payload: Omit<FoundExceptionListItemSchema, 'data'> & { - data?: Array< - Omit<ExceptionListItemSchema, 'entries'> & { - entries?: string; - } - >; - } = { ...getFoundExceptionListItemSchemaMock(), data: [item] }; - const decoded = foundExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "I should be an array" supplied to "data,entries"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept a string for "page"', () => { - const payload: Omit<FoundExceptionListItemSchema, 'page'> & { - page?: string; - } = { ...getFoundExceptionListItemSchemaMock(), page: '1' }; - const decoded = foundExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "page"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept a string for "per_page"', () => { - const payload: Omit<FoundExceptionListItemSchema, 'per_page'> & { - per_page?: string; - } = { ...getFoundExceptionListItemSchemaMock(), per_page: '20' }; - const decoded = foundExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "20" supplied to "per_page"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept a string for "total"', () => { - const payload: Omit<FoundExceptionListItemSchema, 'total'> & { - total?: string; - } = { ...getFoundExceptionListItemSchemaMock(), total: '1' }; - const decoded = foundExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "total"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "page"', () => { - const payload = getFoundExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.page; - const decoded = foundExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "page"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "per_page"', () => { - const payload = getFoundExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.per_page; - const decoded = foundExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "per_page"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "total"', () => { - const payload = getFoundExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.total; - const decoded = foundExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "total"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "data"', () => { - const payload = getFoundExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.data; - const decoded = foundExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "data"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: FoundExceptionListItemSchema & { - extraKey?: string; - } = getFoundExceptionListItemSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = foundExceptionListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.ts deleted file mode 100644 index 6e34672f3349..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.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", 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 * as t from 'io-ts'; - -import { page } from '../../common/page'; -import { per_page } from '../../common/per_page'; -import { pitId } from '../../common/pit'; -import { total } from '../../common/total'; -import { exceptionListItemSchema } from '../exception_list_item_schema'; - -export const foundExceptionListItemSchema = t.intersection([ - t.exact( - t.type({ - data: t.array(exceptionListItemSchema), - page, - per_page, - total, - }) - ), - t.exact( - t.partial({ - pit: pitId, - }) - ), -]); - -export type FoundExceptionListItemSchema = t.TypeOf<typeof foundExceptionListItemSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.mock.ts deleted file mode 100644 index 5a4aa9fcbe57..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.mock.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", 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 { FoundExceptionListSchema } from '.'; -import { getExceptionListSchemaMock } from '../exception_list_schema/index.mock'; - -export const getFoundExceptionListSchemaMock = (): FoundExceptionListSchema => ({ - data: [getExceptionListSchemaMock()], - page: 1, - per_page: 1, - total: 1, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.test.ts deleted file mode 100644 index 07b302c80170..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.test.ts +++ /dev/null @@ -1,154 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getFoundExceptionListSchemaMock } from './index.mock'; -import { FoundExceptionListSchema, foundExceptionListSchema } from '.'; -import { getExceptionListSchemaMock } from '../exception_list_schema/index.mock'; -import { ExceptionListSchema } from '../exception_list_schema'; - -describe('exception_list_schema', () => { - test('it should validate a typical exception list response', () => { - const payload = getFoundExceptionListSchemaMock(); - const decoded = foundExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept a malformed exception list item within "data"', () => { - const item: Omit<ExceptionListSchema, 'entries'> & { - entries?: string[]; - } = { ...getExceptionListSchemaMock(), entries: ['I should not be here'] }; - const payload: Omit<FoundExceptionListSchema, 'data'> & { - data?: Array< - Omit<ExceptionListSchema, 'entries'> & { - entries?: string[]; - } - >; - } = { ...getFoundExceptionListSchemaMock(), data: [item] }; - const decoded = foundExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'invalid keys "entries,["I should not be here"]"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept a string for "page"', () => { - const payload: Omit<FoundExceptionListSchema, 'page'> & { - page?: string; - } = { ...getFoundExceptionListSchemaMock(), page: '1' }; - const decoded = foundExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "page"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept a string for "per_page"', () => { - const payload: Omit<FoundExceptionListSchema, 'per_page'> & { - per_page?: string; - } = { ...getFoundExceptionListSchemaMock(), per_page: '20' }; - const decoded = foundExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "20" supplied to "per_page"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept a string for "total"', () => { - const payload: Omit<FoundExceptionListSchema, 'total'> & { - total?: string; - } = { ...getFoundExceptionListSchemaMock(), total: '1' }; - const decoded = foundExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "total"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "page"', () => { - const payload = getFoundExceptionListSchemaMock(); - // @ts-expect-error - delete payload.page; - const decoded = foundExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "page"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "per_page"', () => { - const payload = getFoundExceptionListSchemaMock(); - // @ts-expect-error - delete payload.per_page; - const decoded = foundExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "per_page"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "total"', () => { - const payload = getFoundExceptionListSchemaMock(); - // @ts-expect-error - delete payload.total; - const decoded = foundExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "total"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "data"', () => { - const payload = getFoundExceptionListSchemaMock(); - // @ts-expect-error - delete payload.data; - const decoded = foundExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "data"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: FoundExceptionListSchema & { - extraKey?: string; - } = getFoundExceptionListSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = foundExceptionListSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.ts deleted file mode 100644 index de4a222b61e7..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.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", 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 * as t from 'io-ts'; - -import { page } from '../../common/page'; -import { per_page } from '../../common/per_page'; -import { pitId } from '../../common/pit'; -import { total } from '../../common/total'; - -import { exceptionListSchema } from '../exception_list_schema'; - -export const foundExceptionListSchema = t.intersection([ - t.exact( - t.type({ - data: t.array(exceptionListSchema), - page, - per_page, - total, - }) - ), - t.exact(t.partial({ pit: pitId })), -]); - -export type FoundExceptionListSchema = t.TypeOf<typeof foundExceptionListSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.mock.ts deleted file mode 100644 index 3f2083565743..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.mock.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { FoundListItemSchema } from '.'; -import { getListItemResponseMock } from '../list_item_schema/index.mock'; - -export const getFoundListItemSchemaMock = (): FoundListItemSchema => ({ - cursor: 'WzI1LFsiNmE3NmI2OWQtODBkZi00YWIyLThjM2UtODVmNDY2YjA2YTBlIl1d', - data: [getListItemResponseMock()], - page: 1, - per_page: 25, - total: 1, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.ts deleted file mode 100644 index 872a178b2f13..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -import { listItemSchema } from '../list_item_schema'; -import { cursor } from '../../common/cursor'; -import { page } from '../../common/page'; -import { per_page } from '../../common/per_page'; -import { total } from '../../common/total'; - -export const foundListItemSchema = t.exact( - t.type({ - cursor, - data: t.array(listItemSchema), - page, - per_page, - total, - }) -); - -export type FoundListItemSchema = t.TypeOf<typeof foundListItemSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.mock.ts deleted file mode 100644 index 232de4d47c15..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.mock.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { FoundListSchema } from '.'; -import { getListResponseMock } from '../list_schema/index.mock'; - -export const getFoundListSchemaMock = (): FoundListSchema => ({ - cursor: '123', - data: [getListResponseMock()], - page: 1, - per_page: 1, - total: 1, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.ts deleted file mode 100644 index 6b97b8c73e63..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -import { listSchema } from '../list_schema'; -import { cursor } from '../../common/cursor'; -import { page } from '../../common/page'; -import { per_page } from '../../common/per_page'; -import { total } from '../../common/total'; - -export const foundListSchema = t.exact( - t.type({ - cursor, - data: t.array(listSchema), - page, - per_page, - total, - }) -); - -export type FoundListSchema = t.TypeOf<typeof foundListSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.mock.ts deleted file mode 100644 index bd654ccde56e..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.mock.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", 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 { FoundListsBySizeSchema } from '.'; -import { getListResponseMock } from '../list_schema/index.mock'; - -export const getFoundListsBySizeSchemaMock = (): FoundListsBySizeSchema => ({ - smallLists: [getListResponseMock()], - largeLists: [getListResponseMock()], -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.ts deleted file mode 100644 index b4d41484bf26..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.ts +++ /dev/null @@ -1,21 +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 * as t from 'io-ts'; - -import { listSchema } from '../list_schema'; - -export const foundListsBySizeSchema = t.exact( - t.type({ - largeLists: t.array(listSchema), - smallLists: t.array(listSchema), - }) -); - -export type FoundListsBySizeSchema = t.TypeOf<typeof foundListsBySizeSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.mock.ts deleted file mode 100644 index b1502381013c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.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 { ImportExceptionsResponseSchema } from '.'; - -export const getImportExceptionsResponseSchemaMock = ( - success = 0, - lists = 0, - items = 0 -): ImportExceptionsResponseSchema => ({ - errors: [], - success: true, - success_count: success, - success_exception_lists: true, - success_count_exception_lists: lists, - success_exception_list_items: true, - success_count_exception_list_items: items, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.test.ts deleted file mode 100644 index 81dd87bf19a5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.test.ts +++ /dev/null @@ -1,130 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { importExceptionsResponseSchema, ImportExceptionsResponseSchema } from '.'; -import { getImportExceptionsResponseSchemaMock } from './index.mock'; - -describe('importExceptionsResponseSchema', () => { - test('it should validate a typical exceptions import response', () => { - const payload = getImportExceptionsResponseSchemaMock(); - const decoded = importExceptionsResponseSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "errors"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = - getImportExceptionsResponseSchemaMock(); - delete payload.errors; - const decoded = importExceptionsResponseSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "errors"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "success"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = - getImportExceptionsResponseSchemaMock(); - delete payload.success; - const decoded = importExceptionsResponseSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "success"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "success_count"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = - getImportExceptionsResponseSchemaMock(); - delete payload.success_count; - const decoded = importExceptionsResponseSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "success_count"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "success_exception_lists"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = - getImportExceptionsResponseSchemaMock(); - delete payload.success_exception_lists; - const decoded = importExceptionsResponseSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "success_exception_lists"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "success_count_exception_lists"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = - getImportExceptionsResponseSchemaMock(); - delete payload.success_count_exception_lists; - const decoded = importExceptionsResponseSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "success_count_exception_lists"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "success_exception_list_items"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = - getImportExceptionsResponseSchemaMock(); - delete payload.success_exception_list_items; - const decoded = importExceptionsResponseSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "success_exception_list_items"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "success_count_exception_list_items"', () => { - const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = - getImportExceptionsResponseSchemaMock(); - delete payload.success_count_exception_list_items; - const decoded = importExceptionsResponseSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "success_count_exception_list_items"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ImportExceptionsResponseSchema & { - extraKey?: string; - } = getImportExceptionsResponseSchemaMock(); - payload.extraKey = 'some new value'; - const decoded = importExceptionsResponseSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.ts deleted file mode 100644 index b976ab298e0c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.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 * as t from 'io-ts'; - -import { PositiveInteger } from '@kbn/securitysolution-io-ts-types'; - -import { id } from '../../common/id'; -import { list_id } from '../../common/list_id'; -import { item_id } from '../../common/item_id'; - -export const bulkErrorErrorSchema = t.exact( - t.type({ - status_code: t.number, - message: t.string, - }) -); - -export const bulkErrorSchema = t.intersection([ - t.exact( - t.type({ - error: bulkErrorErrorSchema, - }) - ), - t.partial({ - id, - list_id, - item_id, - }), -]); - -export type BulkErrorSchema = t.TypeOf<typeof bulkErrorSchema>; - -export const importExceptionsResponseSchema = t.exact( - t.type({ - errors: t.array(bulkErrorSchema), - success: t.boolean, - success_count: PositiveInteger, - success_exception_lists: t.boolean, - success_count_exception_lists: PositiveInteger, - success_exception_list_items: t.boolean, - success_count_exception_list_items: PositiveInteger, - }) -); - -export type ImportExceptionsResponseSchema = t.TypeOf<typeof importExceptionsResponseSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/index.ts deleted file mode 100644 index 49c42783473d..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/index.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". - */ - -export * from './acknowledge_schema'; -export * from './create_endpoint_list_schema'; -export * from './exception_list_schema'; -export * from './exception_list_item_schema'; -export * from './found_exception_list_item_schema'; -export * from './found_exception_list_schema'; -export * from './found_all_list_items_schema'; -export * from './found_lists_by_size_schema'; -export * from './found_list_item_schema'; -export * from './found_list_schema'; -export * from './import_exceptions_schema'; -export * from './list_item_schema'; -export * from './list_schema'; -export * from './exception_list_summary_schema'; -export * from './list_item_index_exist_schema'; -export * from './search_list_item_schema'; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.mock.ts deleted file mode 100644 index eff892b3f00b..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.mock.ts +++ /dev/null @@ -1,15 +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 { ListItemIndexExistSchema } from '.'; - -export const getListItemIndexExistSchemaResponseMock = (): ListItemIndexExistSchema => ({ - list_index: true, - list_item_index: true, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.test.ts deleted file mode 100644 index 2827cd362734..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.test.ts +++ /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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getListItemIndexExistSchemaResponseMock } from './index.mock'; -import { ListItemIndexExistSchema, listItemIndexExistSchema } from '.'; - -describe('list_item_index_exist_schema', () => { - test('it should validate a typical list item request', () => { - const payload = getListItemIndexExistSchemaResponseMock(); - const decoded = listItemIndexExistSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "list_index"', () => { - const payload = getListItemIndexExistSchemaResponseMock(); - // @ts-expect-error - delete payload.list_index; - const decoded = listItemIndexExistSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_index"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "list_item_index"', () => { - const payload = getListItemIndexExistSchemaResponseMock(); - // @ts-expect-error - delete payload.list_item_index; - const decoded = listItemIndexExistSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_item_index"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ListItemIndexExistSchema & { - extraKey?: string; - } = getListItemIndexExistSchemaResponseMock(); - payload.extraKey = 'some new value'; - const decoded = listItemIndexExistSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.ts deleted file mode 100644 index 494af4e3e759..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; - -export const listItemIndexExistSchema = t.exact( - t.type({ - list_index: t.boolean, - list_item_index: t.boolean, - }) -); - -export type ListItemIndexExistSchema = t.TypeOf<typeof listItemIndexExistSchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.mock.ts deleted file mode 100644 index 22c2e5ae07f6..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.mock.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { ListItemSchema } from '.'; -import { - DATE_NOW, - ELASTIC_USER, - LIST_ID, - LIST_ITEM_ID, - META, - TIE_BREAKER, - TYPE, - USER, - VALUE, -} from '../../constants/index.mock'; - -export const getListItemResponseMock = (): ListItemSchema => ({ - _version: undefined, - '@timestamp': DATE_NOW, - created_at: DATE_NOW, - created_by: USER, - deserializer: undefined, - id: LIST_ITEM_ID, - list_id: LIST_ID, - meta: META, - serializer: undefined, - tie_breaker_id: TIE_BREAKER, - type: TYPE, - updated_at: DATE_NOW, - updated_by: USER, - value: VALUE, -}); - -/** - * This is useful for end to end tests where we remove the auto generated parts for comparisons - * such as created_at, updated_at, and id. - */ -export const getListItemResponseMockWithoutAutoGeneratedValues = (): Partial<ListItemSchema> => ({ - created_by: ELASTIC_USER, - list_id: LIST_ID, - type: TYPE, - updated_by: ELASTIC_USER, - value: VALUE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.test.ts deleted file mode 100644 index 5b4238e136dc..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.test.ts +++ /dev/null @@ -1,194 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getListItemResponseMock } from './index.mock'; -import { ListItemSchema, listItemSchema } from '.'; - -describe('list_item_schema', () => { - test('it should validate a typical list item response', () => { - const payload = getListItemResponseMock(); - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "id"', () => { - const payload = getListItemResponseMock(); - // @ts-expect-error - delete payload.id; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "list_id"', () => { - const payload = getListItemResponseMock(); - // @ts-expect-error - delete payload.list_id; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "list_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "meta"', () => { - const payload = getListItemResponseMock(); - delete payload.meta; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "serializer"', () => { - const payload = getListItemResponseMock(); - delete payload.serializer; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "deserializer"', () => { - const payload = getListItemResponseMock(); - delete payload.deserializer; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "created_at"', () => { - const payload = getListItemResponseMock(); - // @ts-expect-error - delete payload.created_at; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "created_at"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "created_by"', () => { - const payload = getListItemResponseMock(); - // @ts-expect-error - delete payload.created_by; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "created_by"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "tie_breaker_id"', () => { - const payload = getListItemResponseMock(); - // @ts-expect-error - delete payload.tie_breaker_id; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "tie_breaker_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "type"', () => { - const payload = getListItemResponseMock(); - // @ts-expect-error - delete payload.type; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "updated_at"', () => { - const payload = getListItemResponseMock(); - // @ts-expect-error - delete payload.updated_at; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "updated_at"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "updated_by"', () => { - const payload = getListItemResponseMock(); - // @ts-expect-error - delete payload.updated_by; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "updated_by"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "value"', () => { - const payload = getListItemResponseMock(); - // @ts-expect-error - delete payload.value; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "value"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ListItemSchema & { extraKey?: string } = getListItemResponseMock(); - payload.extraKey = 'some new value'; - const decoded = listItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.ts deleted file mode 100644 index 869ca695fa42..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.ts +++ /dev/null @@ -1,49 +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 * as t from 'io-ts'; - -import { _versionOrUndefined } from '../../common/underscore_version'; -import { deserializerOrUndefined } from '../../common/deserializer'; -import { metaOrUndefined } from '../../common/meta'; -import { timestampOrUndefined } from '../../common/timestamp'; -import { serializerOrUndefined } from '../../common/serializer'; -import { created_at } from '../../common/created_at'; -import { created_by } from '../../common/created_by'; -import { id } from '../../common/id'; -import { tie_breaker_id } from '../../common/tie_breaker_id'; -import { type } from '../../common/type'; -import { updated_at } from '../../common/updated_at'; -import { updated_by } from '../../common/updated_by'; -import { list_id } from '../../common/list_id'; -import { value } from '../../common/value'; - -export const listItemSchema = t.exact( - t.type({ - _version: _versionOrUndefined, - '@timestamp': timestampOrUndefined, - created_at, - created_by, - deserializer: deserializerOrUndefined, - id, - list_id, - meta: metaOrUndefined, - serializer: serializerOrUndefined, - tie_breaker_id, - type, - updated_at, - updated_by, - value, - }) -); - -export type ListItemSchema = t.TypeOf<typeof listItemSchema>; - -export const listItemArraySchema = t.array(listItemSchema); -export type ListItemArraySchema = t.TypeOf<typeof listItemArraySchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.mock.ts deleted file mode 100644 index 7a0b9e01b87c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.mock.ts +++ /dev/null @@ -1,56 +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 { ListSchema } from '.'; -import { - DATE_NOW, - DESCRIPTION, - ELASTIC_USER, - IMMUTABLE, - LIST_ID, - META, - NAME, - TIE_BREAKER, - TYPE, - USER, - VERSION, -} from '../../constants/index.mock'; - -export const getListResponseMock = (): ListSchema => ({ - _version: undefined, - '@timestamp': DATE_NOW, - created_at: DATE_NOW, - created_by: USER, - description: DESCRIPTION, - deserializer: undefined, - id: LIST_ID, - immutable: IMMUTABLE, - meta: META, - name: NAME, - serializer: undefined, - tie_breaker_id: TIE_BREAKER, - type: TYPE, - updated_at: DATE_NOW, - updated_by: USER, - version: VERSION, -}); - -/** - * This is useful for end to end tests where we remove the auto generated parts for comparisons - * such as created_at, updated_at, and id. - */ -export const getListResponseMockWithoutAutoGeneratedValues = (): Partial<ListSchema> => ({ - created_by: ELASTIC_USER, - description: DESCRIPTION, - immutable: IMMUTABLE, - name: NAME, - type: TYPE, - updated_by: ELASTIC_USER, - version: VERSION, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.test.ts deleted file mode 100644 index 17f25de7d8ec..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.test.ts +++ /dev/null @@ -1,194 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getListResponseMock } from './index.mock'; -import { ListSchema, listSchema } from '.'; - -describe('list_schema', () => { - test('it should validate a typical list response', () => { - const payload = getListResponseMock(); - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "id"', () => { - const payload = getListResponseMock(); - // @ts-expect-error - delete payload.id; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); - expect(message.schema).toEqual({}); - }); - - test('it should accept an undefined for "meta"', () => { - const payload = getListResponseMock(); - delete payload.meta; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "serializer"', () => { - const payload = getListResponseMock(); - delete payload.serializer; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should accept an undefined for "deserializer"', () => { - const payload = getListResponseMock(); - delete payload.deserializer; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT accept an undefined for "created_at"', () => { - const payload = getListResponseMock(); - // @ts-expect-error - delete payload.created_at; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "created_at"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "created_by"', () => { - const payload = getListResponseMock(); - // @ts-expect-error - delete payload.created_by; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "created_by"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "tie_breaker_id"', () => { - const payload = getListResponseMock(); - // @ts-expect-error - delete payload.tie_breaker_id; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "tie_breaker_id"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "type"', () => { - const payload = getListResponseMock(); - // @ts-expect-error - delete payload.type; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "type"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "updated_at"', () => { - const payload = getListResponseMock(); - // @ts-expect-error - delete payload.updated_at; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "updated_at"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "updated_by"', () => { - const payload = getListResponseMock(); - // @ts-expect-error - delete payload.updated_by; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "updated_by"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "name"', () => { - const payload = getListResponseMock(); - // @ts-expect-error - delete payload.name; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "name"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should NOT accept an undefined for "description"', () => { - const payload = getListResponseMock(); - // @ts-expect-error - delete payload.description; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "description"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: ListSchema & { extraKey?: string } = getListResponseMock(); - payload.extraKey = 'some new value'; - const decoded = listSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.ts deleted file mode 100644 index 5029e55df324..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.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 * as t from 'io-ts'; -import { version } from '@kbn/securitysolution-io-ts-types'; -import { _versionOrUndefined } from '../../common/underscore_version'; -import { deserializerOrUndefined } from '../../common/deserializer'; -import { metaOrUndefined } from '../../common/meta'; -import { serializerOrUndefined } from '../../common/serializer'; -import { created_at } from '../../common/created_at'; -import { timestampOrUndefined } from '../../common/timestamp'; -import { created_by } from '../../common/created_by'; -import { description } from '../../common/description'; -import { id } from '../../common/id'; -import { immutable } from '../../common/immutable'; -import { name } from '../../common/name'; -import { tie_breaker_id } from '../../common/tie_breaker_id'; -import { type } from '../../common/type'; -import { updated_at } from '../../common/updated_at'; -import { updated_by } from '../../common/updated_by'; - -export const listSchema = t.exact( - t.type({ - _version: _versionOrUndefined, - '@timestamp': timestampOrUndefined, - created_at, - created_by, - description, - deserializer: deserializerOrUndefined, - id, - immutable, - meta: metaOrUndefined, - name, - serializer: serializerOrUndefined, - tie_breaker_id, - type, - updated_at, - updated_by, - version, - }) -); - -export type ListSchema = t.TypeOf<typeof listSchema>; - -export const listArraySchema = t.array(listSchema); -export type ListArraySchema = t.TypeOf<typeof listArraySchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.mock.ts deleted file mode 100644 index 1cab9a6b827c..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.mock.ts +++ /dev/null @@ -1,17 +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 { SearchListItemSchema } from '.'; -import { VALUE } from '../../constants/index.mock'; -import { getListItemResponseMock } from '../list_item_schema/index.mock'; - -export const getSearchListItemResponseMock = (): SearchListItemSchema => ({ - items: [getListItemResponseMock()], - value: VALUE, -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.test.ts deleted file mode 100644 index a6604f39b462..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.test.ts +++ /dev/null @@ -1,49 +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 { left } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { getSearchListItemResponseMock } from './index.mock'; -import { SearchListItemSchema, searchListItemSchema } from '.'; - -describe('search_list_item_schema', () => { - test('it should validate a typical search list item response', () => { - const payload = getSearchListItemResponseMock(); - const decoded = searchListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should NOT validate with an "undefined" for "items"', () => { - const { items, ...noItems } = getSearchListItemResponseMock(); - const decoded = searchListItemSchema.decode(noItems); - const checked = exactCheck(noItems, decoded); - const message = pipe(checked, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "items"', - ]); - expect(message.schema).toEqual({}); - }); - - test('it should not allow an extra key to be sent in', () => { - const payload: SearchListItemSchema & { extraKey?: string } = getSearchListItemResponseMock(); - payload.extraKey = 'some new value'; - const decoded = searchListItemSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); - expect(message.schema).toEqual({}); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.ts deleted file mode 100644 index c5a07308dd0f..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 * as t from 'io-ts'; -import { listItemArraySchema } from '../list_item_schema'; - -/** - * NOTE: Although this is defined within "response" this does not expose a REST API - * endpoint right now for this particular response. Instead this is only used internally - * for the plugins at this moment. If this changes, please remove this message. - */ -export const searchListItemSchema = t.exact( - t.type({ - items: listItemArraySchema, - value: t.unknown, - }) -); - -export type SearchListItemSchema = t.TypeOf<typeof searchListItemSchema>; - -export const searchListItemArraySchema = t.array(searchListItemSchema); -export type SearchListItemArraySchema = t.TypeOf<typeof searchListItemArraySchema>; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts deleted file mode 100644 index 9a8cd1e4fab5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts +++ /dev/null @@ -1,244 +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 { HttpStart } from '@kbn/core-http-browser'; -import type { NotificationsStart } from '@kbn/core-notifications-browser'; -import type { Filter } from '@kbn/es-query'; -import { NamespaceType } from '../common/default_namespace'; -import { ExceptionListType, ExceptionListTypeEnum } from '../common/exception_list'; -import { Page } from '../common/page'; -import { PerPage } from '../common/per_page'; -import { TotalOrUndefined } from '../common/total'; -import { CreateExceptionListItemSchema } from '../request/create_exception_list_item_schema'; -import { CreateExceptionListSchema } from '../request/create_exception_list_schema'; -import { ExceptionListId } from '../request/get_exception_filter_schema'; -import { UpdateExceptionListItemSchema } from '../request/update_exception_list_item_schema'; -import { UpdateExceptionListSchema } from '../request/update_exception_list_schema'; -import { ExceptionListItemSchema } from '../response/exception_list_item_schema'; -import { ExceptionListSchema } from '../response/exception_list_schema'; - -interface BaseParams { - http: HttpStart; - signal: AbortSignal; -} - -export interface DuplicateExceptionListProps extends BaseParams { - listId: string; - namespaceType: NamespaceType; - includeExpiredExceptions: boolean; -} - -export interface ApiListDuplicateProps - extends Omit<DuplicateExceptionListProps, 'http' | 'signal'> { - onError: (err: Error) => void; - onSuccess: (newList: ExceptionListSchema) => void; -} - -export interface ExceptionListFilter { - name?: string | null; - list_id?: string | null; - created_by?: string | null; - types?: ExceptionListTypeEnum[] | null; - tags?: string | null; -} - -export interface UseExceptionListsProps { - errorMessage: string; - filterOptions?: ExceptionListFilter; - http: HttpStart; - namespaceTypes: NamespaceType[]; - notifications: NotificationsStart; - initialPagination?: Pagination; - hideLists?: readonly string[]; - initialSort?: Sort; -} - -export interface UseExceptionListProps { - http: HttpStart; - lists: ExceptionListIdentifiers[]; - onError?: (arg: string[]) => void; - filterOptions: FilterExceptionsOptions[]; - pagination?: Pagination; - showDetectionsListsOnly: boolean; - showEndpointListsOnly: boolean; - matchFilters: boolean; - onSuccess?: (arg: UseExceptionListItemsSuccess) => void; - sort?: Sort; -} - -export interface FilterExceptionsOptions { - filter: string; - tags: string[]; -} - -export interface ApiCallMemoProps { - id: string; - namespaceType: NamespaceType; - onError: (arg: Error) => void; - onSuccess: () => void; -} - -// TODO: Switch to use ApiCallMemoProps -// after cleaning up exceptions/api file to -// remove unnecessary validation checks -export interface ApiListExportProps { - id: string; - includeExpiredExceptions: boolean; - listId: string; - namespaceType: NamespaceType; - onError: (err: Error) => void; - onSuccess: (blob: Blob) => void; -} - -export interface Sort { - field: string; - order: string; -} -export interface Pagination { - page: Page; - perPage: PerPage; - total: TotalOrUndefined; -} - -export interface UseExceptionListItemsSuccess { - exceptions: ExceptionListItemSchema[]; - pagination: Pagination; -} - -export interface ExceptionListIdentifiers { - id: string; - listId: string; - namespaceType: NamespaceType; - type: ExceptionListType; -} - -export interface ApiCallFindListsItemsMemoProps { - lists: ExceptionListIdentifiers[]; - pagination: Partial<Pagination>; - showDetectionsListsOnly: boolean; - showEndpointListsOnly: boolean; - filter?: string; - onError: (arg: string[]) => void; - onSuccess: (arg: UseExceptionListItemsSuccess) => void; -} - -export interface ApiCallGetExceptionFilterFromIdsMemoProps extends GetExceptionFilterOptionalProps { - exceptionListIds: ExceptionListId[]; - onError: (arg: string[]) => void; - onSuccess: (arg: Filter) => void; -} - -export interface ApiCallGetExceptionFilterFromExceptionsMemoProps - extends GetExceptionFilterOptionalProps { - exceptions: Array<ExceptionListItemSchema | CreateExceptionListItemSchema>; - onError: (arg: string[]) => void; - onSuccess: (arg: Filter) => void; -} - -export interface ExportExceptionListProps { - http: HttpStart; - id: string; - listId: string; - namespaceType: NamespaceType; - includeExpiredExceptions: boolean; - signal: AbortSignal; -} - -export interface AddEndpointExceptionListProps { - http: HttpStart; - signal: AbortSignal; -} - -export interface UpdateExceptionListItemProps { - http: HttpStart; - listItem: UpdateExceptionListItemSchema; - signal: AbortSignal; -} - -export interface UpdateExceptionListProps { - http: HttpStart; - list: UpdateExceptionListSchema; - signal: AbortSignal; -} - -export interface AddExceptionListItemProps { - http: HttpStart; - listItem: CreateExceptionListItemSchema; - signal: AbortSignal; -} - -export interface AddExceptionListProps { - http: HttpStart; - list: CreateExceptionListSchema; - signal: AbortSignal; -} - -export interface UseExceptionListsSuccess { - exceptions: ExceptionListSchema[]; - pagination: Pagination; -} - -export interface ApiCallFetchExceptionListsProps { - http: HttpStart; - namespaceTypes: string; - pagination: Partial<Pagination>; - sort?: Sort; - filters: string; - signal: AbortSignal; -} - -export interface ApiCallByIdProps { - http: HttpStart; - id: string; - namespaceType: NamespaceType; - signal: AbortSignal; -} - -export interface ApiCallByListIdProps { - http: HttpStart; - listIds: string[]; - namespaceTypes: NamespaceType[]; - pagination: Partial<Pagination>; - search?: string; - filter?: string; - signal: AbortSignal; -} - -export type AddExceptionList = UpdateExceptionListSchema | CreateExceptionListSchema; - -export interface PersistHookProps { - http: HttpStart; - onError: (arg: Error) => void; -} - -export interface ExceptionList extends ExceptionListSchema { - totalItems: number; -} - -export interface GetExceptionFilterOptionalProps { - signal?: AbortSignal; - chunkSize?: number; - alias?: string; - excludeExceptions?: boolean; -} - -export interface GetExceptionFilterFromExceptionListIdsProps - extends GetExceptionFilterOptionalProps { - http: HttpStart; - exceptionListIds: ExceptionListId[]; -} - -export interface GetExceptionFilterFromExceptionsProps extends GetExceptionFilterOptionalProps { - http: HttpStart; - exceptions: Array<ExceptionListItemSchema | CreateExceptionListItemSchema>; -} - -export interface ExceptionFilterResponse { - filter: Filter; -} diff --git a/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json b/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json deleted file mode 100644 index 706aa6e81fc5..000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": ["jest", "node"] - }, - "include": ["**/*.ts"], - "kbn_references": [ - "@kbn/securitysolution-io-ts-types", - "@kbn/securitysolution-io-ts-utils", - "@kbn/securitysolution-list-constants", - "@kbn/es-query", - "@kbn/core-http-browser", - "@kbn/core-notifications-browser", - "@kbn/securitysolution-exceptions-common" - ], - "exclude": ["target/**/*"] -} diff --git a/packages/kbn-securitysolution-io-ts-types/jest.config.js b/packages/kbn-securitysolution-io-ts-types/jest.config.js deleted file mode 100644 index c6efe620b09f..000000000000 --- a/packages/kbn-securitysolution-io-ts-types/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-io-ts-types'], -}; diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_uuid/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/default_uuid/index.test.ts deleted file mode 100644 index c429529167e5..000000000000 --- a/packages/kbn-securitysolution-io-ts-types/src/default_uuid/index.test.ts +++ /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", 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 { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { DefaultUuid } from '.'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -describe('default_uuid', () => { - test('it should validate a regular string', () => { - const payload = '1'; - const decoded = DefaultUuid.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('it should not validate a number', () => { - const payload = 5; - const decoded = DefaultUuid.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "DefaultUuid"']); - expect(message.schema).toEqual({}); - }); - - test('it should return a default of a uuid', () => { - const payload = null; - const decoded = DefaultUuid.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toMatch( - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i - ); - }); -}); diff --git a/packages/kbn-securitysolution-io-ts-types/tsconfig.json b/packages/kbn-securitysolution-io-ts-types/tsconfig.json deleted file mode 100644 index 25b82d38b7dc..000000000000 --- a/packages/kbn-securitysolution-io-ts-types/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts" - ], - "kbn_references": [ - "@kbn/securitysolution-io-ts-utils" - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-io-ts-utils/jest.config.js b/packages/kbn-securitysolution-io-ts-utils/jest.config.js deleted file mode 100644 index 457ac315951b..000000000000 --- a/packages/kbn-securitysolution-io-ts-utils/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-io-ts-utils'], -}; diff --git a/packages/kbn-securitysolution-io-ts-utils/tsconfig.json b/packages/kbn-securitysolution-io-ts-utils/tsconfig.json deleted file mode 100644 index 13f8244edd1a..000000000000 --- a/packages/kbn-securitysolution-io-ts-utils/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts" - ], - "kbn_references": [ - "@kbn/datemath" - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-list-api/index.ts b/packages/kbn-securitysolution-list-api/index.ts deleted file mode 100644 index fc434f01c7ec..000000000000 --- a/packages/kbn-securitysolution-list-api/index.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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './src/api'; -export * from './src/fp_utils'; -export * from './src/list_api'; -export * from './src/list_item_api'; -export * from './src/types'; diff --git a/packages/kbn-securitysolution-list-api/jest.config.js b/packages/kbn-securitysolution-list-api/jest.config.js deleted file mode 100644 index 434cef2b7ca3..000000000000 --- a/packages/kbn-securitysolution-list-api/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-list-api'], -}; diff --git a/packages/kbn-securitysolution-list-api/package.json b/packages/kbn-securitysolution-list-api/package.json deleted file mode 100644 index bcb7a79accdc..000000000000 --- a/packages/kbn-securitysolution-list-api/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "@kbn/securitysolution-list-api", - "version": "1.0.0", - "description": "security solution list REST API", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "private": true -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-list-api/src/api/index.ts b/packages/kbn-securitysolution-list-api/src/api/index.ts deleted file mode 100644 index 4bd188672e5a..000000000000 --- a/packages/kbn-securitysolution-list-api/src/api/index.ts +++ /dev/null @@ -1,666 +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 { chain, fromEither, tryCatch } from 'fp-ts/lib/TaskEither'; -import { flow } from 'fp-ts/lib/function'; -import { validateEither } from '@kbn/securitysolution-io-ts-utils'; -import { - CreateEndpointListSchema, - ExceptionListItemSchema, - ExceptionListSchema, - FoundExceptionListItemSchema, - FoundExceptionListSchema, - createEndpointListSchema, - exceptionListItemSchema, - exceptionListSchema, - foundExceptionListItemSchema, - foundExceptionListSchema, - AddEndpointExceptionListProps, - AddExceptionListItemProps, - AddExceptionListProps, - ApiCallByIdProps, - ApiCallByListIdProps, - ApiCallFetchExceptionListsProps, - ExportExceptionListProps, - UpdateExceptionListItemProps, - UpdateExceptionListProps, - GetExceptionFilterFromExceptionListIdsProps, - GetExceptionFilterFromExceptionsProps, - ExceptionFilterResponse, - DuplicateExceptionListProps, -} from '@kbn/securitysolution-io-ts-list-types'; - -import { - ENDPOINT_LIST_URL, - INTERNAL_EXCEPTION_FILTER, - EXCEPTION_LIST_ITEM_URL, - EXCEPTION_LIST_URL, -} from '@kbn/securitysolution-list-constants'; -import { toError, toPromise } from '../fp_utils'; - -const version = '2023-10-31'; - -/** - * Add new ExceptionList - * - * @param http Kibana http service - * @param list exception list to add - * @param signal to cancel request - * - * @throws An error if response is not OK - * - */ -const addExceptionList = async ({ - http, - list, - signal, -}: AddExceptionListProps): Promise<ExceptionListSchema> => - http.fetch<ExceptionListSchema>(EXCEPTION_LIST_URL, { - body: JSON.stringify(list), - method: 'POST', - signal, - version, - }); - -const addExceptionListWithValidation = async ({ - http, - list, - signal, -}: AddExceptionListProps): Promise<ExceptionListSchema> => - flow( - () => - tryCatch( - () => - addExceptionList({ - http, - list, - signal, - }), - toError - ), - chain((response) => fromEither(validateEither(exceptionListSchema, response))), - flow(toPromise) - )(); - -export { addExceptionListWithValidation as addExceptionList }; - -/** - * Add new ExceptionListItem - * - * @param http Kibana http service - * @param listItem exception list item to add - * @param signal to cancel request - * - * @throws An error if response is not OK - * - */ -const addExceptionListItem = async ({ - http, - listItem, - signal, -}: AddExceptionListItemProps): Promise<ExceptionListItemSchema> => - http.fetch<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, { - body: JSON.stringify(listItem), - method: 'POST', - signal, - version, - }); - -const addExceptionListItemWithValidation = async ({ - http, - listItem, - signal, -}: AddExceptionListItemProps): Promise<ExceptionListItemSchema> => - flow( - () => - tryCatch( - () => - addExceptionListItem({ - http, - listItem, - signal, - }), - toError - ), - chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), - flow(toPromise) - )(); - -export { addExceptionListItemWithValidation as addExceptionListItem }; - -/** - * Update existing ExceptionList - * - * @param http Kibana http service - * @param list exception list to add - * @param signal to cancel request - * - * @throws An error if response is not OK - * - */ -const updateExceptionList = async ({ - http, - list, - signal, -}: UpdateExceptionListProps): Promise<ExceptionListSchema> => - http.fetch<ExceptionListSchema>(EXCEPTION_LIST_URL, { - body: JSON.stringify(list), - method: 'PUT', - signal, - version, - }); - -const updateExceptionListWithValidation = async ({ - http, - list, - signal, -}: UpdateExceptionListProps): Promise<ExceptionListSchema> => - flow( - () => - tryCatch( - () => - updateExceptionList({ - http, - list, - signal, - }), - toError - ), - chain((response) => fromEither(validateEither(exceptionListSchema, response))), - flow(toPromise) - )(); - -export { updateExceptionListWithValidation as updateExceptionList }; - -/** - * Update existing ExceptionListItem - * - * @param http Kibana http service - * @param listItem exception list item to add - * @param signal to cancel request - * - * @throws An error if response is not OK - * - */ -const updateExceptionListItem = async ({ - http, - listItem, - signal, -}: UpdateExceptionListItemProps): Promise<ExceptionListItemSchema> => - http.fetch<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, { - body: JSON.stringify(listItem), - method: 'PUT', - signal, - version, - }); - -const updateExceptionListItemWithValidation = async ({ - http, - listItem, - signal, -}: UpdateExceptionListItemProps): Promise<ExceptionListItemSchema> => - flow( - () => - tryCatch( - () => - updateExceptionListItem({ - http, - listItem, - signal, - }), - toError - ), - chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), - flow(toPromise) - )(); - -export { updateExceptionListItemWithValidation as updateExceptionListItem }; - -/** - * Fetch all ExceptionLists (optionally by namespaceType) - * - * @param http Kibana http service - * @param namespaceTypes ExceptionList namespace_types of lists to find - * @param filters search bar filters - * @param pagination optional - * @param signal to cancel request - * - * @throws An error if request params or response is not OK - */ -const fetchExceptionLists = async ({ - http, - filters, - namespaceTypes, - pagination, - signal, - sort, -}: ApiCallFetchExceptionListsProps): Promise<FoundExceptionListSchema> => { - const query = { - filter: filters || undefined, - namespace_type: namespaceTypes, - page: pagination.page ? `${pagination.page}` : '1', - per_page: pagination.perPage ? `${pagination.perPage}` : '20', - sort_field: sort?.field ? sort?.field : 'exception-list.created_at', - sort_order: sort?.order ? sort?.order : 'desc', - }; - - return http.fetch<FoundExceptionListSchema>(`${EXCEPTION_LIST_URL}/_find`, { - method: 'GET', - query, - signal, - version, - }); -}; - -const fetchExceptionListsWithValidation = async ({ - filters, - http, - namespaceTypes, - pagination, - signal, - sort, -}: ApiCallFetchExceptionListsProps): Promise<FoundExceptionListSchema> => - flow( - () => - tryCatch( - () => - fetchExceptionLists({ - filters, - http, - namespaceTypes, - pagination, - signal, - sort, - }), - toError - ), - chain((response) => fromEither(validateEither(foundExceptionListSchema, response))), - flow(toPromise) - )(); - -export { fetchExceptionListsWithValidation as fetchExceptionLists }; - -/** - * Fetch an ExceptionList by providing a ExceptionList ID - * - * @param http Kibana http service - * @param id ExceptionList ID (not list_id) - * @param namespaceType ExceptionList namespace_type - * @param signal to cancel request - * - * @throws An error if response is not OK - */ -const fetchExceptionListById = async ({ - http, - id, - namespaceType, - signal, -}: ApiCallByIdProps): Promise<ExceptionListSchema> => - http.fetch<ExceptionListSchema>(EXCEPTION_LIST_URL, { - method: 'GET', - query: { id, namespace_type: namespaceType }, - signal, - version, - }); - -const fetchExceptionListByIdWithValidation = async ({ - http, - id, - namespaceType, - signal, -}: ApiCallByIdProps): Promise<ExceptionListSchema> => - flow( - () => - tryCatch( - () => - fetchExceptionListById({ - http, - id, - namespaceType, - signal, - }), - toError - ), - chain((response) => fromEither(validateEither(exceptionListSchema, response))), - flow(toPromise) - )(); - -export { fetchExceptionListByIdWithValidation as fetchExceptionListById }; - -/** - * Fetch an ExceptionList's ExceptionItems by providing a ExceptionList list_id - * - * @param http Kibana http service - * @param listIds ExceptionList list_ids (not ID) - * @param namespaceTypes ExceptionList namespace_types - * @param search optional - simple search string - * @param filter optional - * @param pagination optional - * @param signal to cancel request - * - * @throws An error if response is not OK - */ -const fetchExceptionListsItemsByListIds = async ({ - http, - listIds, - namespaceTypes, - filter, - pagination, - search, - signal, -}: ApiCallByListIdProps): Promise<FoundExceptionListItemSchema> => { - const query = { - list_id: listIds.join(','), - namespace_type: namespaceTypes.join(','), - page: pagination.page ? `${pagination.page}` : '1', - per_page: pagination.perPage ? `${pagination.perPage}` : '20', - search, - sort_field: 'exception-list.created_at', - sort_order: 'desc', - filter, - }; - - return http.fetch<FoundExceptionListItemSchema>(`${EXCEPTION_LIST_ITEM_URL}/_find`, { - method: 'GET', - query, - signal, - version, - }); -}; - -const fetchExceptionListsItemsByListIdsWithValidation = async ({ - filter, - http, - listIds, - namespaceTypes, - pagination, - search, - signal, -}: ApiCallByListIdProps): Promise<FoundExceptionListItemSchema> => - flow( - () => - tryCatch( - () => - fetchExceptionListsItemsByListIds({ - filter, - http, - listIds, - namespaceTypes, - pagination, - search, - signal, - }), - toError - ), - chain((response) => fromEither(validateEither(foundExceptionListItemSchema, response))), - flow(toPromise) - )(); - -export { fetchExceptionListsItemsByListIdsWithValidation as fetchExceptionListsItemsByListIds }; - -/** - * Fetch an ExceptionListItem by providing a ExceptionListItem ID - * - * @param http Kibana http service - * @param id ExceptionListItem ID (not item_id) - * @param namespaceType ExceptionList namespace_type - * @param signal to cancel request - * - * @throws An error if response is not OK - */ -const fetchExceptionListItemById = async ({ - http, - id, - namespaceType, - signal, -}: ApiCallByIdProps): Promise<ExceptionListItemSchema> => - http.fetch<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, { - method: 'GET', - query: { id, namespace_type: namespaceType }, - signal, - version, - }); - -const fetchExceptionListItemByIdWithValidation = async ({ - http, - id, - namespaceType, - signal, -}: ApiCallByIdProps): Promise<ExceptionListItemSchema> => - flow( - () => tryCatch(() => fetchExceptionListItemById({ http, id, namespaceType, signal }), toError), - chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), - flow(toPromise) - )(); - -export { fetchExceptionListItemByIdWithValidation as fetchExceptionListItemById }; - -/** - * Delete an ExceptionList by providing a ExceptionList ID - * - * @param http Kibana http service - * @param id ExceptionList ID (not list_id) - * @param namespaceType ExceptionList namespace_type - * @param signal to cancel request - * - * @throws An error if response is not OK - */ -const deleteExceptionListById = async ({ - http, - id, - namespaceType, - signal, -}: ApiCallByIdProps): Promise<ExceptionListSchema> => - http.fetch<ExceptionListSchema>(EXCEPTION_LIST_URL, { - method: 'DELETE', - query: { id, namespace_type: namespaceType }, - signal, - version, - }); - -const deleteExceptionListByIdWithValidation = async ({ - http, - id, - namespaceType, - signal, -}: ApiCallByIdProps): Promise<ExceptionListSchema> => - flow( - () => tryCatch(() => deleteExceptionListById({ http, id, namespaceType, signal }), toError), - chain((response) => fromEither(validateEither(exceptionListSchema, response))), - flow(toPromise) - )(); - -export { deleteExceptionListByIdWithValidation as deleteExceptionListById }; - -/** - * Delete an ExceptionListItem by providing a ExceptionListItem ID - * - * @param http Kibana http service - * @param id ExceptionListItem ID (not item_id) - * @param namespaceType ExceptionList namespace_type - * @param signal to cancel request - * - * @throws An error if response is not OK - */ -const deleteExceptionListItemById = async ({ - http, - id, - namespaceType, - signal, -}: ApiCallByIdProps): Promise<ExceptionListItemSchema> => - http.fetch<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, { - method: 'DELETE', - query: { id, namespace_type: namespaceType }, - signal, - version, - }); - -const deleteExceptionListItemByIdWithValidation = async ({ - http, - id, - namespaceType, - signal, -}: ApiCallByIdProps): Promise<ExceptionListItemSchema> => - flow( - () => tryCatch(() => deleteExceptionListItemById({ http, id, namespaceType, signal }), toError), - chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), - flow(toPromise) - )(); - -export { deleteExceptionListItemByIdWithValidation as deleteExceptionListItemById }; - -/** - * Add new Endpoint ExceptionList - * - * @param http Kibana http service - * @param signal to cancel request - * - * @throws An error if response is not OK - * - */ -const addEndpointExceptionList = async ({ - http, - signal, -}: AddEndpointExceptionListProps): Promise<CreateEndpointListSchema> => - http.fetch<ExceptionListItemSchema>(ENDPOINT_LIST_URL, { - method: 'POST', - signal, - version, - }); - -const addEndpointExceptionListWithValidation = async ({ - http, - signal, -}: AddEndpointExceptionListProps): Promise<CreateEndpointListSchema> => - flow( - () => tryCatch(() => addEndpointExceptionList({ http, signal }), toError), - chain((response) => fromEither(validateEither(createEndpointListSchema, response))), - flow(toPromise) - )(); - -export { addEndpointExceptionListWithValidation as addEndpointExceptionList }; - -/** - * Export an ExceptionList by providing a ExceptionList ID - * - * @param http Kibana http service - * @param id ExceptionList ID (not list_id) - * @param includeExpiredExceptions boolean for including expired exceptions - * @param listId ExceptionList LIST_ID (not id) - * @param namespaceType ExceptionList namespace_type - * @param signal to cancel request - * - * @throws An error if response is not OK - */ -export const exportExceptionList = async ({ - http, - id, - includeExpiredExceptions, - listId, - namespaceType, - signal, -}: ExportExceptionListProps): Promise<Blob> => - http.fetch<Blob>(`${EXCEPTION_LIST_URL}/_export`, { - method: 'POST', - query: { - id, - list_id: listId, - namespace_type: namespaceType, - include_expired_exceptions: includeExpiredExceptions, - }, - signal, - version, - }); - -/** - * Create a Filter query from an exception list id - * - * @param exceptionListId The id of the exception list from which create a Filter query - * @param signal AbortSignal for cancelling request - * - * @throws An error if response is not OK - */ -export const getExceptionFilterFromExceptionListIds = async ({ - alias, - chunkSize, - exceptionListIds, - excludeExceptions, - http, - signal, -}: GetExceptionFilterFromExceptionListIdsProps): Promise<ExceptionFilterResponse> => - http.fetch(INTERNAL_EXCEPTION_FILTER, { - method: 'POST', - version: '1', - body: JSON.stringify({ - exception_list_ids: exceptionListIds, - type: 'exception_list_ids', - alias, - exclude_exceptions: excludeExceptions, - chunk_size: chunkSize, - }), - signal, - }); - -/** - * Create a Filter query from a list of exceptions - * - * @param exceptions Exception items to be made into a `Filter` query - * @param signal AbortSignal for cancelling request - * - * @throws An error if response is not OK - */ -export const getExceptionFilterFromExceptions = async ({ - exceptions, - alias, - excludeExceptions, - http, - chunkSize, - signal, -}: GetExceptionFilterFromExceptionsProps): Promise<ExceptionFilterResponse> => - http.fetch(INTERNAL_EXCEPTION_FILTER, { - method: 'POST', - version: '1', - body: JSON.stringify({ - exceptions, - type: 'exception_items', - alias, - exclude_exceptions: excludeExceptions, - chunk_size: chunkSize, - }), - signal, - }); - -/** - * Duplicate an ExceptionList and its items by providing a ExceptionList list_id - * - * @param http Kibana http service - * @param includeExpiredExceptions boolean for including exception items with expired TTL - * @param listId ExceptionList LIST_ID (not id) - * @param namespaceType ExceptionList namespace_type - * @param signal to cancel request - * - * @throws An error if response is not OK - */ -export const duplicateExceptionList = async ({ - http, - includeExpiredExceptions, - listId, - namespaceType, - signal, -}: DuplicateExceptionListProps): Promise<ExceptionListSchema> => - http.fetch<ExceptionListSchema>(`${EXCEPTION_LIST_URL}/_duplicate`, { - method: 'POST', - query: { - list_id: listId, - namespace_type: namespaceType, - include_expired_exceptions: includeExpiredExceptions, - }, - signal, - version, - }); diff --git a/packages/kbn-securitysolution-list-api/src/fp_utils/index.test.ts b/packages/kbn-securitysolution-list-api/src/fp_utils/index.test.ts deleted file mode 100644 index 98872f45cb77..000000000000 --- a/packages/kbn-securitysolution-list-api/src/fp_utils/index.test.ts +++ /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", 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 { tryCatch } from 'fp-ts/lib/TaskEither'; - -import { toPromise } from '.'; - -describe('toPromise', () => { - it('rejects with left if TaskEither is left', async () => { - const task = tryCatch(() => Promise.reject(new Error('whoops')), String); - - await expect(toPromise(task)).rejects.toEqual('Error: whoops'); - }); - - it('resolves with right if TaskEither is right', async () => { - const task = tryCatch(() => Promise.resolve('success'), String); - - await expect(toPromise(task)).resolves.toEqual('success'); - }); -}); diff --git a/packages/kbn-securitysolution-list-api/src/fp_utils/index.ts b/packages/kbn-securitysolution-list-api/src/fp_utils/index.ts deleted file mode 100644 index 5614a3e7cb8d..000000000000 --- a/packages/kbn-securitysolution-list-api/src/fp_utils/index.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 { pipe } from 'fp-ts/lib/pipeable'; -import { TaskEither } from 'fp-ts/lib/TaskEither'; -import { fold } from 'fp-ts/lib/Either'; - -// TODO: This is copied in a few other spots and probably should live within its own kbn package -// rather than living here. A package such as kbn-security-solution-fp-utils -export const toPromise = async <E, A>(taskEither: TaskEither<E, A>): Promise<A> => - pipe( - await taskEither(), - fold( - (e) => Promise.reject(e), - (a) => Promise.resolve(a) - ) - ); - -export const toError = (e: unknown): Error => (e instanceof Error ? e : new Error(String(e))); diff --git a/packages/kbn-securitysolution-list-api/src/list_api/index.test.ts b/packages/kbn-securitysolution-list-api/src/list_api/index.test.ts deleted file mode 100644 index 6b8698a707aa..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_api/index.test.ts +++ /dev/null @@ -1,445 +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 { createListIndex, deleteList, exportList, findLists, importList, readListIndex } from '.'; -import { - ApiPayload, - DeleteListParams, - ExportListParams, - FindListsParams, - ImportListParams, -} from '../types'; - -import { HttpFetchOptions } from '@kbn/core-http-browser'; -import { httpServiceMock } from '@kbn/core-http-browser-mocks'; - -import { getFoundListSchemaMock } from './mocks/response/found_list_schema.mock'; -import { getListResponseMock } from './mocks/response/list_schema.mock'; -import { getListItemIndexExistSchemaResponseMock } from './mocks/response/list_item_index_exist_schema.mock'; -import { getAcknowledgeSchemaResponseMock } from './mocks/response/acknowledge_schema.mock'; - -describe('Value Lists API', () => { - let httpMock: ReturnType<typeof httpServiceMock.createStartContract>; - - beforeEach(() => { - httpMock = httpServiceMock.createStartContract(); - }); - describe('deleteList', () => { - beforeEach(() => { - httpMock.fetch.mockResolvedValue(getListResponseMock()); - }); - - it('DELETEs specifying the id as a query parameter', async () => { - const abortCtrl = new AbortController(); - const payload: ApiPayload<DeleteListParams> = { - deleteReferences: false, - id: 'list-id', - ignoreReferences: true, - }; - await deleteList({ - http: httpMock, - ...payload, - signal: abortCtrl.signal, - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists', - expect.objectContaining({ - method: 'DELETE', - query: { deleteReferences: false, id: 'list-id', ignoreReferences: true }, - }) - ); - }); - - it('rejects with an error if request payload is invalid (and does not make API call)', async () => { - const abortCtrl = new AbortController(); - const payload: Omit<ApiPayload<DeleteListParams>, 'id'> & { - id: number; - } = { id: 23 }; - - await expect( - deleteList({ - http: httpMock, - ...(payload as unknown as ApiPayload<DeleteListParams>), - signal: abortCtrl.signal, - }) - ).rejects.toEqual(new Error('Invalid value "23" supplied to "id"')); - expect(httpMock.fetch).not.toHaveBeenCalled(); - }); - - it('rejects with an error if response payload is invalid', async () => { - const abortCtrl = new AbortController(); - const payload: ApiPayload<DeleteListParams> = { id: 'list-id' }; - const badResponse = { ...getListResponseMock(), id: undefined }; - httpMock.fetch.mockResolvedValue(badResponse); - - await expect( - deleteList({ - http: httpMock, - ...payload, - signal: abortCtrl.signal, - }) - ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); - }); - }); - describe('findLists', () => { - beforeEach(() => { - httpMock.fetch.mockResolvedValue(getFoundListSchemaMock()); - }); - - it('GETs from the lists endpoint', async () => { - const abortCtrl = new AbortController(); - await findLists({ - http: httpMock, - pageIndex: 1, - pageSize: 10, - signal: abortCtrl.signal, - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/_find', - expect.objectContaining({ - method: 'GET', - }) - ); - }); - - it('sends pagination as query parameters', async () => { - const abortCtrl = new AbortController(); - await findLists({ - cursor: 'cursor', - http: httpMock, - pageIndex: 1, - pageSize: 10, - signal: abortCtrl.signal, - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/_find', - expect.objectContaining({ - query: { - cursor: 'cursor', - page: 1, - per_page: 10, - }, - }) - ); - }); - - it('sends sort_field and sort_order as query parameters', async () => { - const abortCtrl = new AbortController(); - await findLists({ - cursor: 'cursor', - http: httpMock, - pageIndex: 1, - pageSize: 10, - signal: abortCtrl.signal, - sortField: 'created_at', - sortOrder: 'desc', - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/_find', - expect.objectContaining({ - query: { - cursor: 'cursor', - page: 1, - per_page: 10, - sort_field: 'created_at', - sort_order: 'desc', - }, - }) - ); - }); - - it('rejects with an error if request payload is invalid (and does not make API call)', async () => { - const abortCtrl = new AbortController(); - const payload: ApiPayload<FindListsParams> = { - pageIndex: 10, - pageSize: 0, - }; - - await expect( - findLists({ - http: httpMock, - ...payload, - signal: abortCtrl.signal, - }) - ).rejects.toEqual(new Error('Invalid value "0" supplied to "per_page"')); - expect(httpMock.fetch).not.toHaveBeenCalled(); - }); - - it('rejects with an error if response payload is invalid', async () => { - const abortCtrl = new AbortController(); - const payload: ApiPayload<FindListsParams> = { - pageIndex: 1, - pageSize: 10, - }; - const badResponse = { ...getFoundListSchemaMock(), cursor: undefined }; - httpMock.fetch.mockResolvedValue(badResponse); - - await expect( - findLists({ - http: httpMock, - ...payload, - signal: abortCtrl.signal, - }) - ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "cursor"')); - }); - }); - describe('importList', () => { - beforeEach(() => { - httpMock.fetch.mockResolvedValue(getListResponseMock()); - }); - - it('POSTs the file', async () => { - const abortCtrl = new AbortController(); - const file = new File([], 'name'); - - await importList({ - file, - http: httpMock, - listId: 'my_list', - signal: abortCtrl.signal, - type: 'keyword', - }); - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/items/_import', - expect.objectContaining({ - method: 'POST', - }) - ); - - // httpmock's fetch signature is inferred incorrectly - const [[, { body }]] = httpMock.fetch.mock.calls as unknown as Array< - [unknown, HttpFetchOptions] - >; - const actualFile = (body as FormData).get('file'); - expect(actualFile).toEqual(file); - }); - - it('sends type and id as query parameters', async () => { - const abortCtrl = new AbortController(); - const file = new File([], 'name'); - - await importList({ - file, - http: httpMock, - listId: 'my_list', - signal: abortCtrl.signal, - type: 'keyword', - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/items/_import', - expect.objectContaining({ - query: { list_id: 'my_list', type: 'keyword' }, - }) - ); - }); - - it('rejects with an error if request body is invalid (and does not make API call)', async () => { - const abortCtrl = new AbortController(); - const payload: ApiPayload<ImportListParams> = { - file: undefined as unknown as File, - listId: 'list-id', - type: 'ip', - }; - - await expect( - importList({ - http: httpMock, - ...payload, - signal: abortCtrl.signal, - }) - ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "file"')); - expect(httpMock.fetch).not.toHaveBeenCalled(); - }); - - it('rejects with an error if request params are invalid (and does not make API call)', async () => { - const abortCtrl = new AbortController(); - const file = new File([], 'name'); - const payload: ApiPayload<ImportListParams> = { - file, - listId: 'list-id', - type: 'other' as 'ip', - }; - - await expect( - importList({ - http: httpMock, - ...payload, - signal: abortCtrl.signal, - }) - ).rejects.toEqual(new Error('Invalid value "other" supplied to "type"')); - expect(httpMock.fetch).not.toHaveBeenCalled(); - }); - - it('rejects with an error if response payload is invalid', async () => { - const abortCtrl = new AbortController(); - const file = new File([], 'name'); - const payload: ApiPayload<ImportListParams> = { - file, - listId: 'list-id', - type: 'ip', - }; - const badResponse = { ...getListResponseMock(), id: undefined }; - httpMock.fetch.mockResolvedValue(badResponse); - - await expect( - importList({ - http: httpMock, - ...payload, - signal: abortCtrl.signal, - }) - ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); - }); - }); - describe('exportList', () => { - beforeEach(() => { - httpMock.fetch.mockResolvedValue({}); - }); - - it('POSTs to the export endpoint', async () => { - const abortCtrl = new AbortController(); - - await exportList({ - http: httpMock, - listId: 'my_list', - signal: abortCtrl.signal, - }); - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/items/_export', - expect.objectContaining({ - method: 'POST', - }) - ); - }); - - it('sends type and id as query parameters', async () => { - const abortCtrl = new AbortController(); - - await exportList({ - http: httpMock, - listId: 'my_list', - signal: abortCtrl.signal, - }); - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/items/_export', - expect.objectContaining({ - query: { list_id: 'my_list' }, - }) - ); - }); - - it('rejects with an error if request params are invalid (and does not make API call)', async () => { - const abortCtrl = new AbortController(); - const payload: ApiPayload<ExportListParams> = { - listId: 23 as unknown as string, - }; - - await expect( - exportList({ - http: httpMock, - ...payload, - signal: abortCtrl.signal, - }) - ).rejects.toEqual(new Error('Invalid value "23" supplied to "list_id"')); - expect(httpMock.fetch).not.toHaveBeenCalled(); - }); - }); - - describe('readListIndex', () => { - beforeEach(() => { - httpMock.fetch.mockResolvedValue(getListItemIndexExistSchemaResponseMock()); - }); - - it('GETs the list index', async () => { - const abortCtrl = new AbortController(); - await readListIndex({ - http: httpMock, - signal: abortCtrl.signal, - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/index', - expect.objectContaining({ - method: 'GET', - }) - ); - }); - - it('returns the response when valid', async () => { - const abortCtrl = new AbortController(); - const result = await readListIndex({ - http: httpMock, - signal: abortCtrl.signal, - }); - - expect(result).toEqual(getListItemIndexExistSchemaResponseMock()); - }); - - it('rejects with an error if response payload is invalid', async () => { - const abortCtrl = new AbortController(); - const badResponse = { ...getListItemIndexExistSchemaResponseMock(), list_index: undefined }; - httpMock.fetch.mockResolvedValue(badResponse); - - await expect( - readListIndex({ - http: httpMock, - signal: abortCtrl.signal, - }) - ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "list_index"')); - }); - }); - - describe('createListIndex', () => { - beforeEach(() => { - httpMock.fetch.mockResolvedValue(getAcknowledgeSchemaResponseMock()); - }); - - it('GETs the list index', async () => { - const abortCtrl = new AbortController(); - await createListIndex({ - http: httpMock, - signal: abortCtrl.signal, - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/index', - expect.objectContaining({ - method: 'POST', - }) - ); - }); - - it('returns the response when valid', async () => { - const abortCtrl = new AbortController(); - const result = await createListIndex({ - http: httpMock, - signal: abortCtrl.signal, - }); - - expect(result).toEqual(getAcknowledgeSchemaResponseMock()); - }); - - it('rejects with an error if response payload is invalid', async () => { - const abortCtrl = new AbortController(); - const badResponse = { acknowledged: undefined }; - httpMock.fetch.mockResolvedValue(badResponse); - - await expect( - createListIndex({ - http: httpMock, - signal: abortCtrl.signal, - }) - ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "acknowledged"')); - }); - }); -}); diff --git a/packages/kbn-securitysolution-list-api/src/list_api/index.ts b/packages/kbn-securitysolution-list-api/src/list_api/index.ts deleted file mode 100644 index 3087ed5333cc..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_api/index.ts +++ /dev/null @@ -1,344 +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 { chain, fromEither, map, tryCatch } from 'fp-ts/lib/TaskEither'; -import { flow } from 'fp-ts/lib/function'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { validateEither } from '@kbn/securitysolution-io-ts-utils'; -import { - AcknowledgeSchema, - DeleteListSchemaEncoded, - ExportListItemQuerySchemaEncoded, - FindListSchemaEncoded, - FoundListSchema, - ImportListItemQuerySchemaEncoded, - ImportListItemSchemaEncoded, - ListItemIndexExistSchema, - ListSchema, - ReadListSchema, - acknowledgeSchema, - deleteListSchema, - readListSchema, - exportListItemQuerySchema, - findListSchema, - foundListSchema, - importListItemQuerySchema, - importListItemSchema, - listItemIndexExistSchema, - listSchema, - foundListsBySizeSchema, - FoundListsBySizeSchema, -} from '@kbn/securitysolution-io-ts-list-types'; -import { - LIST_INDEX, - LIST_ITEM_URL, - LIST_PRIVILEGES_URL, - LIST_URL, - INTERNAL_FIND_LISTS_BY_SIZE, -} from '@kbn/securitysolution-list-constants'; -import { toError, toPromise } from '../fp_utils'; - -import { - ApiParams, - DeleteListParams, - ExportListParams, - FindListsParams, - ImportListParams, - GetListByIdParams, -} from '../types'; - -export type { - ApiParams, - DeleteListParams, - ExportListParams, - FindListsParams, - ImportListParams, -} from '../types'; - -const version = '2023-10-31'; - -const findLists = async ({ - http, - cursor, - page, - // eslint-disable-next-line @typescript-eslint/naming-convention - per_page, - signal, - // eslint-disable-next-line @typescript-eslint/naming-convention - sort_field, - // eslint-disable-next-line @typescript-eslint/naming-convention - sort_order, -}: ApiParams & FindListSchemaEncoded): Promise<FoundListSchema> => { - return http.fetch(`${LIST_URL}/_find`, { - method: 'GET', - query: { - cursor, - page, - per_page, - sort_field, - sort_order, - }, - signal, - version, - }); -}; - -const findListsWithValidation = async ({ - cursor, - http, - pageIndex, - pageSize, - signal, - sortField, - sortOrder, -}: FindListsParams): Promise<FoundListSchema> => - pipe( - { - cursor: cursor != null ? cursor.toString() : undefined, - page: pageIndex != null ? pageIndex.toString() : undefined, - per_page: pageSize != null ? pageSize.toString() : undefined, - sort_field: sortField != null ? sortField.toString() : undefined, - sort_order: sortOrder, - }, - (payload) => fromEither(validateEither(findListSchema, payload)), - chain((payload) => tryCatch(() => findLists({ http, signal, ...payload }), toError)), - chain((response) => fromEither(validateEither(foundListSchema, response))), - flow(toPromise) - ); - -export { findListsWithValidation as findLists }; - -const findListsBySize = async ({ - http, - cursor, - page, - // eslint-disable-next-line @typescript-eslint/naming-convention - per_page, - signal, -}: ApiParams & FindListSchemaEncoded): Promise<FoundListsBySizeSchema> => { - return http.fetch(`${INTERNAL_FIND_LISTS_BY_SIZE}`, { - method: 'GET', - version: '1', - query: { - cursor, - page, - per_page, - }, - signal, - }); -}; - -const findListsBySizeWithValidation = async ({ - cursor, - http, - pageIndex, - pageSize, - signal, -}: FindListsParams): Promise<FoundListsBySizeSchema> => - pipe( - { - cursor: cursor != null ? cursor.toString() : undefined, - page: pageIndex != null ? pageIndex.toString() : undefined, - per_page: pageSize != null ? pageSize.toString() : undefined, - }, - (payload) => fromEither(validateEither(findListSchema, payload)), - chain((payload) => tryCatch(() => findListsBySize({ http, signal, ...payload }), toError)), - chain((response) => fromEither(validateEither(foundListsBySizeSchema, response))), - flow(toPromise) - ); - -export { findListsBySizeWithValidation as findListsBySize }; - -const importList = async ({ - file, - http, - // eslint-disable-next-line @typescript-eslint/naming-convention - list_id, - type, - signal, - refresh, -}: ApiParams & - ImportListItemSchemaEncoded & - ImportListItemQuerySchemaEncoded): Promise<ListSchema> => { - const formData = new FormData(); - formData.append('file', file as Blob); - - return http.fetch<ListSchema>(`${LIST_ITEM_URL}/_import`, { - body: formData, - headers: { 'Content-Type': undefined }, - method: 'POST', - query: { list_id, type, refresh }, - signal, - version, - }); -}; - -const importListWithValidation = async ({ - file, - http, - listId, - type, - signal, - refresh, -}: ImportListParams): Promise<ListSchema> => - pipe( - { - list_id: listId, - type, - refresh, - }, - (query) => fromEither(validateEither(importListItemQuerySchema, query)), - chain((query) => - pipe( - fromEither(validateEither(importListItemSchema, { file })), - map((body) => ({ ...body, ...query })) - ) - ), - chain((payload) => tryCatch(() => importList({ http, signal, ...payload }), toError)), - chain((response) => fromEither(validateEither(listSchema, response))), - toPromise - ); - -export { importListWithValidation as importList }; - -const deleteList = async ({ - deleteReferences = false, - http, - id, - ignoreReferences = false, - signal, -}: ApiParams & DeleteListSchemaEncoded): Promise<ListSchema> => - http.fetch<ListSchema>(LIST_URL, { - method: 'DELETE', - query: { deleteReferences, id, ignoreReferences }, - signal, - version, - }); - -const deleteListWithValidation = async ({ - deleteReferences, - http, - id, - ignoreReferences, - signal, -}: DeleteListParams): Promise<ListSchema> => - pipe( - { deleteReferences, id, ignoreReferences }, - (payload) => fromEither(validateEither(deleteListSchema, payload)), - chain((payload) => tryCatch(() => deleteList({ http, signal, ...payload }), toError)), - chain((response) => fromEither(validateEither(listSchema, response))), - flow(toPromise) - ); - -export { deleteListWithValidation as deleteList }; - -const exportList = async ({ - http, - // eslint-disable-next-line @typescript-eslint/naming-convention - list_id, - signal, -}: ApiParams & ExportListItemQuerySchemaEncoded): Promise<Blob> => - http.fetch<Blob>(`${LIST_ITEM_URL}/_export`, { - method: 'POST', - query: { list_id }, - signal, - version, - }); - -const exportListWithValidation = async ({ - http, - listId, - signal, -}: ExportListParams): Promise<Blob> => - pipe( - { list_id: listId }, - (payload) => fromEither(validateEither(exportListItemQuerySchema, payload)), - chain((payload) => tryCatch(() => exportList({ http, signal, ...payload }), toError)), - flow(toPromise) - ); - -export { exportListWithValidation as exportList }; - -const readListIndex = async ({ http, signal }: ApiParams): Promise<ListItemIndexExistSchema> => - http.fetch<ListItemIndexExistSchema>(LIST_INDEX, { - method: 'GET', - signal, - version, - }); - -const readListIndexWithValidation = async ({ - http, - signal, -}: ApiParams): Promise<ListItemIndexExistSchema> => - flow( - () => tryCatch(() => readListIndex({ http, signal }), toError), - chain((response) => fromEither(validateEither(listItemIndexExistSchema, response))), - flow(toPromise) - )(); - -export { readListIndexWithValidation as readListIndex }; - -// TODO add types and validation -export const readListPrivileges = async ({ http, signal }: ApiParams): Promise<unknown> => - http.fetch<unknown>(LIST_PRIVILEGES_URL, { - method: 'GET', - signal, - version, - }); - -const createListIndex = async ({ http, signal }: ApiParams): Promise<AcknowledgeSchema> => - http.fetch<AcknowledgeSchema>(LIST_INDEX, { - method: 'POST', - signal, - version, - }); - -const createListIndexWithValidation = async ({ - http, - signal, -}: ApiParams): Promise<AcknowledgeSchema> => - flow( - () => tryCatch(() => createListIndex({ http, signal }), toError), - chain((response) => fromEither(validateEither(acknowledgeSchema, response))), - flow(toPromise) - )(); - -export { createListIndexWithValidation as createListIndex }; - -const getListById = async ({ - http, - signal, - id, -}: ApiParams & ReadListSchema): Promise<ListSchema> => { - return http.fetch(`${LIST_URL}`, { - method: 'GET', - query: { - id, - }, - signal, - version, - }); -}; - -const getListByIdWithValidation = async ({ - http, - signal, - id, -}: GetListByIdParams): Promise<ListSchema> => - pipe( - { - id, - }, - (payload) => fromEither(validateEither(readListSchema, payload)), - chain((payload) => tryCatch(() => getListById({ http, signal, ...payload }), toError)), - chain((response) => fromEither(validateEither(listSchema, response))), - flow(toPromise) - ); - -export { getListByIdWithValidation as getListById }; diff --git a/packages/kbn-securitysolution-list-api/src/list_api/mocks/constants.mock.ts b/packages/kbn-securitysolution-list-api/src/list_api/mocks/constants.mock.ts deleted file mode 100644 index 3043cc70642c..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_api/mocks/constants.mock.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". - */ - -export const DATE_NOW = '2020-04-20T15:25:31.830Z'; -export const USER = 'some user'; -export const ELASTIC_USER = 'elastic'; -export const NAME = 'some name'; -export const DESCRIPTION = 'some description'; -export const LIST_ID = 'some-list-id'; -export const TIE_BREAKER = '6a76b69d-80df-4ab2-8c3e-85f466b06a0e'; - -export const META = {}; -export const TYPE = 'ip'; - -export const VERSION = 1; -export const IMMUTABLE = false; diff --git a/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/acknowledge_schema.mock.ts b/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/acknowledge_schema.mock.ts deleted file mode 100644 index 788fab714e2d..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/acknowledge_schema.mock.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", 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 { AcknowledgeSchema } from '@kbn/securitysolution-io-ts-list-types'; - -export const getAcknowledgeSchemaResponseMock = (): AcknowledgeSchema => ({ - acknowledged: true, -}); diff --git a/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/found_list_schema.mock.ts b/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/found_list_schema.mock.ts deleted file mode 100644 index 436ddcd7a83d..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/found_list_schema.mock.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { FoundListSchema } from '@kbn/securitysolution-io-ts-list-types'; - -import { getListResponseMock } from './list_schema.mock'; - -export const getFoundListSchemaMock = (): FoundListSchema => ({ - cursor: '123', - data: [getListResponseMock()], - page: 1, - per_page: 1, - total: 1, -}); diff --git a/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_item_index_exist_schema.mock.ts b/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_item_index_exist_schema.mock.ts deleted file mode 100644 index 27a54a5fd96f..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_item_index_exist_schema.mock.ts +++ /dev/null @@ -1,15 +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 { ListItemIndexExistSchema } from '@kbn/securitysolution-io-ts-list-types'; - -export const getListItemIndexExistSchemaResponseMock = (): ListItemIndexExistSchema => ({ - list_index: true, - list_item_index: true, -}); diff --git a/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_schema.mock.ts b/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_schema.mock.ts deleted file mode 100644 index 79ed25641977..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_schema.mock.ts +++ /dev/null @@ -1,57 +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 { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; - -import { - DATE_NOW, - DESCRIPTION, - ELASTIC_USER, - IMMUTABLE, - LIST_ID, - META, - NAME, - TIE_BREAKER, - TYPE, - USER, - VERSION, -} from '../constants.mock'; - -export const getListResponseMock = (): ListSchema => ({ - '@timestamp': DATE_NOW, - _version: undefined, - created_at: DATE_NOW, - created_by: USER, - description: DESCRIPTION, - deserializer: undefined, - id: LIST_ID, - immutable: IMMUTABLE, - meta: META, - name: NAME, - serializer: undefined, - tie_breaker_id: TIE_BREAKER, - type: TYPE, - updated_at: DATE_NOW, - updated_by: USER, - version: VERSION, -}); - -/** - * This is useful for end to end tests where we remove the auto generated parts for comparisons - * such as created_at, updated_at, and id. - */ -export const getListResponseMockWithoutAutoGeneratedValues = (): Partial<ListSchema> => ({ - created_by: ELASTIC_USER, - description: DESCRIPTION, - immutable: IMMUTABLE, - name: NAME, - type: TYPE, - updated_by: ELASTIC_USER, - version: VERSION, -}); diff --git a/packages/kbn-securitysolution-list-api/src/list_item_api/index.test.ts b/packages/kbn-securitysolution-list-api/src/list_item_api/index.test.ts deleted file mode 100644 index 95e37ec43410..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_item_api/index.test.ts +++ /dev/null @@ -1,173 +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 { createListItem, deleteListItem, findListItems, patchListItem } from '.'; -import { httpServiceMock } from '@kbn/core-http-browser-mocks'; -import { - getFoundListSchemaMock, - getCreateListItemResponseMock, - getUpdatedListItemResponseMock, - getDeletedListItemResponseMock, -} from './mocks/response'; - -describe('Value list item API', () => { - let httpMock: ReturnType<typeof httpServiceMock.createStartContract>; - beforeEach(() => { - httpMock = httpServiceMock.createStartContract(); - }); - - describe('findListItems', () => { - beforeEach(() => { - httpMock.fetch.mockResolvedValue(getFoundListSchemaMock()); - }); - - it('GETs from the lists endpoint with query params', async () => { - const abortCtrl = new AbortController(); - await findListItems({ - http: httpMock, - pageIndex: 1, - pageSize: 10, - signal: abortCtrl.signal, - filter: '*:*', - listId: 'list_id', - sortField: 'updated_at', - sortOrder: 'asc', - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/items/_find', - expect.objectContaining({ - method: 'GET', - query: { - cursor: undefined, - filter: '*:*', - list_id: 'list_id', - page: 1, - per_page: 10, - sort_field: 'updated_at', - sort_order: 'asc', - }, - }) - ); - }); - }); - - describe('createListItem', () => { - beforeEach(() => { - httpMock.fetch.mockResolvedValue(getCreateListItemResponseMock()); - }); - - it('POSTs to the lists endpoint with the list item', async () => { - const abortCtrl = new AbortController(); - await createListItem({ - http: httpMock, - signal: abortCtrl.signal, - value: '123', - listId: 'list_id', - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/items', - expect.objectContaining({ - method: 'POST', - body: JSON.stringify({ - value: '123', - list_id: 'list_id', - }), - }) - ); - }); - - it('returns the created list item', async () => { - const abortCtrl = new AbortController(); - const result = await createListItem({ - http: httpMock, - signal: abortCtrl.signal, - value: '123', - listId: 'list_id', - }); - - expect(result).toEqual(getCreateListItemResponseMock()); - }); - }); - - describe('patchListItem', () => { - beforeEach(() => { - httpMock.fetch.mockResolvedValue(getUpdatedListItemResponseMock()); - }); - - it('PATCH to the lists endpoint with the list item', async () => { - const abortCtrl = new AbortController(); - await patchListItem({ - http: httpMock, - signal: abortCtrl.signal, - id: 'item_id', - value: '123', - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/items', - expect.objectContaining({ - method: 'PATCH', - body: JSON.stringify({ - id: 'item_id', - value: '123', - }), - }) - ); - }); - - it('returns the updated list item', async () => { - const abortCtrl = new AbortController(); - const result = await patchListItem({ - http: httpMock, - signal: abortCtrl.signal, - id: 'item_id', - value: '123', - }); - - expect(result).toEqual(getUpdatedListItemResponseMock()); - }); - }); - - describe('deleteListItem', () => { - beforeEach(() => { - httpMock.fetch.mockResolvedValue(getCreateListItemResponseMock()); - }); - - it('DELETE to the lists endpoint with the list item', async () => { - const abortCtrl = new AbortController(); - await deleteListItem({ - http: httpMock, - signal: abortCtrl.signal, - id: 'item_id', - refresh: 'true', - }); - - expect(httpMock.fetch).toHaveBeenCalledWith( - '/api/lists/items', - expect.objectContaining({ - method: 'DELETE', - query: { id: 'item_id', refresh: 'true' }, - }) - ); - }); - - it('returns the deleted list item', async () => { - const abortCtrl = new AbortController(); - const result = await deleteListItem({ - http: httpMock, - signal: abortCtrl.signal, - id: 'item_id', - }); - - expect(result).toEqual(getDeletedListItemResponseMock()); - }); - }); -}); diff --git a/packages/kbn-securitysolution-list-api/src/list_item_api/index.ts b/packages/kbn-securitysolution-list-api/src/list_item_api/index.ts deleted file mode 100644 index 8c6cdc358f70..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_item_api/index.ts +++ /dev/null @@ -1,227 +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 { - FindListItemSchema, - ListItemSchema, - deleteListItemSchema, - patchListItemSchema, - createListItemSchema, - findListItemSchema, - foundListItemSchema, - listItemSchema, - FoundListItemSchema, - DeleteListItemSchema, - PatchListItemSchema, - CreateListItemSchema, -} from '@kbn/securitysolution-io-ts-list-types'; -import { chain, fromEither, tryCatch } from 'fp-ts/lib/TaskEither'; -import { flow } from 'fp-ts/lib/function'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { validateEither } from '@kbn/securitysolution-io-ts-utils'; - -import { LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; -import { - ApiParams, - FindListItemsParams, - DeleteListItemParams, - PatchListItemParams, - CreateListItemParams, -} from '../types'; -import { toError, toPromise } from '../fp_utils'; - -const version = '2023-10-31'; - -/** - * Fetch list items - */ -const findListItems = async ({ - http, - cursor, - page, - // eslint-disable-next-line @typescript-eslint/naming-convention - list_id, - // eslint-disable-next-line @typescript-eslint/naming-convention - per_page, - signal, - // eslint-disable-next-line @typescript-eslint/naming-convention - sort_field, - // eslint-disable-next-line @typescript-eslint/naming-convention - sort_order, - filter, -}: ApiParams & FindListItemSchema): Promise<FoundListItemSchema> => { - return http.fetch(`${LIST_ITEM_URL}/_find`, { - method: 'GET', - query: { - cursor, - page, - per_page, - sort_field, - sort_order, - list_id, - filter, - }, - signal, - version, - }); -}; - -const findListItemsWithValidation = async ({ - cursor, - http, - pageIndex, - pageSize, - signal, - sortField, - sortOrder, - filter, - listId, -}: FindListItemsParams): Promise<FoundListItemSchema> => - pipe( - { - cursor: cursor != null ? cursor.toString() : undefined, - page: pageIndex != null ? pageIndex.toString() : undefined, - per_page: pageSize != null ? pageSize.toString() : undefined, - sort_field: sortField != null ? sortField.toString() : undefined, - filter: filter != null ? filter.toString() : undefined, - sort_order: sortOrder, - list_id: listId, - }, - (payload) => fromEither(validateEither(findListItemSchema, payload)), - chain((payload) => tryCatch(() => findListItems({ http, signal, ...payload }), toError)), - chain((response) => fromEither(validateEither(foundListItemSchema, response))), - flow(toPromise) - ); - -export { findListItemsWithValidation as findListItems }; - -const deleteListItem = async ({ - http, - id, - signal, - refresh, -}: ApiParams & DeleteListItemSchema): Promise<ListItemSchema> => - http.fetch<ListItemSchema>(LIST_ITEM_URL, { - method: 'DELETE', - query: { id, refresh }, - signal, - version, - }); - -const deleteListItemWithValidation = async ({ - http, - id, - signal, - refresh, -}: DeleteListItemParams): Promise<ListItemSchema> => - pipe( - { id, refresh }, - (payload) => fromEither(validateEither(deleteListItemSchema, payload)), - chain((payload) => - tryCatch( - () => - deleteListItem({ - http, - signal, - ...payload, - value: undefined, - list_id: undefined, - }), - toError - ) - ), - chain((response) => fromEither(validateEither(listItemSchema, response))), - flow(toPromise) - ); - -export { deleteListItemWithValidation as deleteListItem }; - -const patchListItem = async ({ - http, - id, - signal, - value, - _version, -}: ApiParams & PatchListItemSchema): Promise<ListItemSchema> => - http.fetch<ListItemSchema>(LIST_ITEM_URL, { - method: 'PATCH', - body: JSON.stringify({ id, value, _version }), - signal, - version, - }); - -const patchListItemWithValidation = async ({ - http, - id, - signal, - value, - refresh, - _version, -}: PatchListItemParams): Promise<ListItemSchema> => - pipe( - { id, value, _version, refresh }, - (payload) => fromEither(validateEither(patchListItemSchema, payload)), - chain((payload) => - tryCatch( - () => - patchListItem({ - http, - signal, - ...payload, - }), - toError - ) - ), - chain((response) => fromEither(validateEither(listItemSchema, response))), - flow(toPromise) - ); - -export { patchListItemWithValidation as patchListItem }; - -const createListItem = async ({ - http, - signal, - value, - // eslint-disable-next-line @typescript-eslint/naming-convention - list_id, - refresh, -}: ApiParams & CreateListItemSchema): Promise<ListItemSchema> => - http.fetch<ListItemSchema>(LIST_ITEM_URL, { - method: 'POST', - body: JSON.stringify({ value, list_id, refresh }), - signal, - version, - }); - -const createListItemWithValidation = async ({ - http, - signal, - value, - refresh, - listId, -}: CreateListItemParams): Promise<ListItemSchema> => - pipe( - { list_id: listId, value, refresh }, - (payload) => fromEither(validateEither(createListItemSchema, payload)), - chain((payload) => - tryCatch( - () => - createListItem({ - http, - signal, - ...payload, - }), - toError - ) - ), - chain((response) => fromEither(validateEither(listItemSchema, response))), - flow(toPromise) - ); - -export { createListItemWithValidation as createListItem }; diff --git a/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/index.ts b/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/index.ts deleted file mode 100644 index 5ef4e52adbe8..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/index.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 type { FoundListItemSchema, ListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; - -import { getListItemResponseMock } from './list_item_schema.mock'; - -export const getFoundListSchemaMock = (): FoundListItemSchema => ({ - cursor: '123', - data: [getListItemResponseMock()], - page: 1, - per_page: 1, - total: 1, -}); - -export const getCreateListItemResponseMock = (): ListItemSchema => getListItemResponseMock(); -export const getUpdatedListItemResponseMock = (): ListItemSchema => getListItemResponseMock(); -export const getDeletedListItemResponseMock = (): ListItemSchema => getListItemResponseMock(); diff --git a/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/list_item_schema.mock.ts b/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/list_item_schema.mock.ts deleted file mode 100644 index eb67b9777354..000000000000 --- a/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/list_item_schema.mock.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { ListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; - -export const getListItemResponseMock = (): ListItemSchema => ({ - _version: '1', - '@timestamp': '2020-08-11T11:22:13.670Z', - created_at: '2020-08-11T11:22:13.670Z', - created_by: 'elastic', - deserializer: 'some deserializer', - id: 'bpdB3XMBx7pemMHopQ6M', - list_id: 'list_id', - meta: {}, - serializer: 'some serializer', - tie_breaker_id: '17d3befb-dc22-4b3c-a286-b5504c4fbeeb', - type: 'keyword', - updated_at: '2020-08-11T11:22:13.670Z', - updated_by: 'elastic', - value: 'some keyword', -}); diff --git a/packages/kbn-securitysolution-list-api/src/types.ts b/packages/kbn-securitysolution-list-api/src/types.ts deleted file mode 100644 index c78071bb5824..000000000000 --- a/packages/kbn-securitysolution-list-api/src/types.ts +++ /dev/null @@ -1,86 +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 { - SortFieldOrUndefined, - SortOrderOrUndefined, - Type, - Refresh, - RefreshWithWaitFor, -} from '@kbn/securitysolution-io-ts-list-types'; - -// TODO: Replace these with kbn packaged versions once we have those available to us -// These originally came from this location below before moving them to this hacked "any" types: -// import { HttpStart, NotificationsStart } from '../../../../../src/core/public'; -interface HttpStart { - fetch: <T>(...args: any) => any; -} - -export interface ApiParams { - http: HttpStart; - signal: AbortSignal; -} -export type ApiPayload<T extends ApiParams> = Omit<T, 'http' | 'signal'>; - -export interface FindListsParams extends ApiParams { - cursor?: string | undefined; - pageSize: number | undefined; - pageIndex: number | undefined; - sortOrder?: SortOrderOrUndefined; - sortField?: SortFieldOrUndefined; -} - -export interface FindListItemsParams extends ApiParams { - cursor?: string | undefined; - pageSize: number | undefined; - pageIndex: number | undefined; - sortOrder?: SortOrderOrUndefined; - sortField?: SortFieldOrUndefined; - filter: string | undefined; - listId: string; -} - -export interface ImportListParams extends ApiParams { - file: File; - listId: string | undefined; - type: Type | undefined; - refresh?: RefreshWithWaitFor; -} - -export interface DeleteListParams extends ApiParams { - deleteReferences?: boolean; - id: string; - ignoreReferences?: boolean; -} - -export interface DeleteListItemParams extends ApiParams { - refresh?: Refresh; - id: string; -} - -export interface PatchListItemParams extends ApiParams { - refresh?: Refresh; - id: string; - value: string; - _version?: string; -} - -export interface CreateListItemParams extends ApiParams { - refresh?: RefreshWithWaitFor; - value: string; - listId: string; -} - -export interface ExportListParams extends ApiParams { - listId: string; -} - -export interface GetListByIdParams extends ApiParams { - id: string; -} diff --git a/packages/kbn-securitysolution-list-api/tsconfig.json b/packages/kbn-securitysolution-list-api/tsconfig.json deleted file mode 100644 index 2c2ca29316fc..000000000000 --- a/packages/kbn-securitysolution-list-api/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts" - ], - "kbn_references": [ - "@kbn/securitysolution-io-ts-list-types", - "@kbn/securitysolution-io-ts-utils", - "@kbn/securitysolution-list-constants", - "@kbn/core-http-browser", - "@kbn/core-http-browser-mocks", - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-list-constants/index.ts b/packages/kbn-securitysolution-list-constants/index.ts deleted file mode 100644 index 6f1e8d01b830..000000000000 --- a/packages/kbn-securitysolution-list-constants/index.ts +++ /dev/null @@ -1,148 +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 { deepFreeze } from '@kbn/std'; - -/** - * Value list routes - */ -export const LIST_URL = '/api/lists'; -export const LIST_INDEX = `${LIST_URL}/index`; -export const LIST_ITEM_URL = `${LIST_URL}/items`; -export const LIST_PRIVILEGES_URL = `${LIST_URL}/privileges`; - -/** - * Internal value list routes - */ -export const INTERNAL_LIST_URL = '/internal/lists'; -export const INTERNAL_FIND_LISTS_BY_SIZE = `${INTERNAL_LIST_URL}/_find_lists_by_size` as const; -export const INTERNAL_EXCEPTION_FILTER = `${INTERNAL_LIST_URL}/_create_filter` as const; - -/** - * Exception list routes - */ -export const EXCEPTION_LIST_URL = '/api/exception_lists'; -export const EXCEPTION_LIST_ITEM_URL = '/api/exception_lists/items'; - -/** - * Internal exception list routes - */ -export const INTERNAL_EXCEPTION_LIST_URL = `/internal${EXCEPTION_LIST_URL}`; -export const INTERNAL_EXCEPTIONS_LIST_ENSURE_CREATED_URL = `${INTERNAL_EXCEPTION_LIST_URL}/_create`; - -/** - * Exception list spaces - */ -export const EXCEPTION_LIST_NAMESPACE_AGNOSTIC = 'exception-list-agnostic'; -export const EXCEPTION_LIST_NAMESPACE = 'exception-list'; - -/** - * Specific routes for the single global space agnostic endpoint list - */ -export const ENDPOINT_LIST_URL = '/api/endpoint_list'; - -/** - * Specific routes for the single global space agnostic endpoint list. These are convenience - * routes where they are going to try and create the global space agnostic endpoint list if it - * does not exist yet or if it was deleted at some point and re-create it before adding items to - * the list - */ -export const ENDPOINT_LIST_ITEM_URL = '/api/endpoint_list/items'; - -/** - * This ID is used for _both_ the Saved Object ID and for the list_id - * for the single global space agnostic endpoint list - */ -export const ENDPOINT_LIST_ID = 'endpoint_list'; - -/** The name of the single global space agnostic endpoint list */ -export const ENDPOINT_LIST_NAME = 'Endpoint Security Exception List'; - -/** The description of the single global space agnostic endpoint list */ -export const ENDPOINT_LIST_DESCRIPTION = 'Endpoint Security Exception List'; - -export const MAX_EXCEPTION_LIST_SIZE = 10000; - -export const MAXIMUM_SMALL_VALUE_LIST_SIZE = 65536; - -export const MAXIMUM_SMALL_IP_RANGE_VALUE_LIST_DASH_SIZE = 200; - -/** - * List definitions for Endpoint Artifact - */ -export const ENDPOINT_ARTIFACT_LISTS = deepFreeze({ - trustedApps: { - id: 'endpoint_trusted_apps', - name: 'Endpoint Security Trusted Apps List', - description: 'Endpoint Security Trusted Apps List', - }, - eventFilters: { - id: 'endpoint_event_filters', - name: 'Endpoint Security Event Filters List', - description: 'Endpoint Security Event Filters List', - }, - hostIsolationExceptions: { - id: 'endpoint_host_isolation_exceptions', - name: 'Endpoint Security Host isolation exceptions List', - description: 'Endpoint Security Host isolation exceptions List', - }, - blocklists: { - id: 'endpoint_blocklists', - name: 'Endpoint Security Blocklists List', - description: 'Endpoint Security Blocklists List', - }, -}); - -/** - * The IDs of all Endpoint artifact lists - */ -export const ENDPOINT_ARTIFACT_LIST_IDS = Object.freeze( - Object.values(ENDPOINT_ARTIFACT_LISTS).map(({ id }) => id) -); - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_TRUSTED_APPS_LIST_ID = ENDPOINT_ARTIFACT_LISTS.trustedApps.id; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_TRUSTED_APPS_LIST_NAME = ENDPOINT_ARTIFACT_LISTS.trustedApps.name; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION = - ENDPOINT_ARTIFACT_LISTS.trustedApps.description; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_EVENT_FILTERS_LIST_ID = ENDPOINT_ARTIFACT_LISTS.eventFilters.id; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_EVENT_FILTERS_LIST_NAME = ENDPOINT_ARTIFACT_LISTS.eventFilters.name; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION = - ENDPOINT_ARTIFACT_LISTS.eventFilters.description; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID = - ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.id; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_NAME = - ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.name; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_DESCRIPTION = - ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.description; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_BLOCKLISTS_LIST_ID = ENDPOINT_ARTIFACT_LISTS.blocklists.id; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_BLOCKLISTS_LIST_NAME = ENDPOINT_ARTIFACT_LISTS.blocklists.name; - -/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ -export const ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION = ENDPOINT_ARTIFACT_LISTS.blocklists.description; diff --git a/packages/kbn-securitysolution-list-constants/package.json b/packages/kbn-securitysolution-list-constants/package.json deleted file mode 100644 index 1b03f95f0f15..000000000000 --- a/packages/kbn-securitysolution-list-constants/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "@kbn/securitysolution-list-constants", - "version": "1.0.0", - "description": "security solution list constants to use across plugins such lists, security_solution, cases, etc...", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "private": true -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-list-constants/tsconfig.json b/packages/kbn-securitysolution-list-constants/tsconfig.json deleted file mode 100644 index 0cd8de173ed4..000000000000 --- a/packages/kbn-securitysolution-list-constants/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts" - ], - "kbn_references": [ - "@kbn/std" - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-list-hooks/index.ts b/packages/kbn-securitysolution-list-hooks/index.ts deleted file mode 100644 index 5ec2cb85d8c2..000000000000 --- a/packages/kbn-securitysolution-list-hooks/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './src/transforms'; -export * from './src/use_api'; -export * from './src/use_create_list_index'; -export * from './src/use_cursor'; -export * from './src/use_delete_list'; -export * from './src/use_exception_lists'; -export * from './src/use_export_list'; -export * from './src/use_find_lists'; -export * from './src/use_find_lists_by_size'; -export * from './src/use_import_list'; -export * from './src/use_persist_exception_item'; -export * from './src/use_persist_exception_list'; -export * from './src/use_read_list_index'; -export * from './src/use_read_list_privileges'; -export * from './src/use_find_list_items'; -export * from './src/use_create_list_item'; -export * from './src/use_delete_list_item'; -export * from './src/use_patch_list_item'; -export * from './src/use_get_list_by_id'; diff --git a/packages/kbn-securitysolution-list-hooks/jest.config.js b/packages/kbn-securitysolution-list-hooks/jest.config.js deleted file mode 100644 index f5fadd8bf399..000000000000 --- a/packages/kbn-securitysolution-list-hooks/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-list-hooks'], -}; diff --git a/packages/kbn-securitysolution-list-hooks/package.json b/packages/kbn-securitysolution-list-hooks/package.json deleted file mode 100644 index 3d9f2019d3f2..000000000000 --- a/packages/kbn-securitysolution-list-hooks/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@kbn/securitysolution-list-hooks", - "version": "1.0.0", - "description": "Security solution list ReactJS hooks", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "private": true, - "sideEffects": false -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-list-hooks/src/constants.ts b/packages/kbn-securitysolution-list-hooks/src/constants.ts deleted file mode 100644 index 333fb40ca4ca..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/constants.ts +++ /dev/null @@ -1,10 +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". - */ - -export const READ_INDEX_QUERY_KEY = ['detectionEngine', 'listIndex']; diff --git a/packages/kbn-securitysolution-list-hooks/src/index.ts b/packages/kbn-securitysolution-list-hooks/src/index.ts deleted file mode 100644 index 852d138b8931..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './transforms'; -export * from './use_api'; -export * from './use_create_list_index'; -export * from './use_cursor'; -export * from './use_delete_list'; -export * from './use_exception_lists'; -export * from './use_export_list'; -export * from './use_find_lists'; -export * from './use_import_list'; -export * from './use_persist_exception_item'; -export * from './use_persist_exception_list'; -export * from './use_read_list_index'; -export * from './use_read_list_privileges'; -export * from './use_find_list_items'; -export * from './use_create_list_item'; -export * from './use_delete_list_item'; -export * from './use_patch_list_item'; -export * from './use_get_list_by_id'; diff --git a/packages/kbn-securitysolution-list-hooks/src/mocks/constants.mock.ts b/packages/kbn-securitysolution-list-hooks/src/mocks/constants.mock.ts deleted file mode 100644 index 67dcac046c8d..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/mocks/constants.mock.ts +++ /dev/null @@ -1,101 +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 { - CommentsArray, - EntriesArray, - Entry, - EntryMatch, - EntryNested, - OsTypeArray, -} from '@kbn/securitysolution-io-ts-list-types'; - -export const DATE_NOW = '2020-04-20T15:25:31.830Z'; -export const USER = 'some user'; -export const ELASTIC_USER = 'elastic'; -export const NAME = 'some name'; -export const DESCRIPTION = 'some description'; -export const LIST_ID = 'some-list-id'; -export const TIE_BREAKER = '6a76b69d-80df-4ab2-8c3e-85f466b06a0e'; - -export const META = {}; -export const TYPE = 'ip'; - -export const VERSION = 1; -export const IMMUTABLE = false; -// Exception List specific -export const ID = 'uuid_here'; -export const NAMESPACE_TYPE = 'single'; -export const OS_TYPES: OsTypeArray = ['windows']; -export const TAGS = []; -export const UPDATED_COMMENTS = [ - { - comment: 'old comment', - id: 'old_created_id', - }, - { - comment: 'new comment', - id: 'new_id', - }, -]; -export const COMMENTS = []; -export const ENTRIES: EntriesArray = [ - { - entries: [{ field: 'nested.field', operator: 'included', type: 'match', value: 'some value' }], - field: 'some.parentField', - type: 'nested', - }, - { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, -]; -export const ITEM_ID = 'some-list-item-id'; -export const ITEM_TYPE = 'simple'; -export const LIST_ITEM_ID = 'some-list-item-id'; -// ENTRIES_WITH_IDS should only be used to mock out functionality of a collection of transforms -// that are UI specific and useful for UI concerns that are inserted between the -// API and the actual user interface. In some ways these might be viewed as -// technical debt or to compensate for the differences and preferences -// of how ReactJS might prefer data vs. how we want to model data. -export const ENTRIES_WITH_IDS: EntriesArray = [ - { - entries: [ - { - field: 'nested.field', - id: '123', - operator: 'included', - type: 'match', - value: 'some value', - } as EntryMatch & { id: string }, - ], - field: 'some.parentField', - id: '123', - type: 'nested', - } as EntryNested & { id: string }, - { - field: 'some.not.nested.field', - id: '123', - operator: 'included', - type: 'match', - value: 'some value', - } as Entry & { id: string }, -]; - -export const COMMENTS_WITH_CREATEDAT_CREATEDBY: CommentsArray = [ - { - comment: 'old comment', - id: 'old_created_id', - created_at: '2022-01-08T15:25:31.830Z', - created_by: 'elastic', - }, - { - comment: 'new comment', - id: 'new_id', - created_at: '2022-05-14T15:25:31.830Z', - created_by: 'elastic', - }, -]; diff --git a/packages/kbn-securitysolution-list-hooks/src/mocks/request/update_exception_list_item_schema.mock.ts b/packages/kbn-securitysolution-list-hooks/src/mocks/request/update_exception_list_item_schema.mock.ts deleted file mode 100644 index 916ce0c81dac..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/mocks/request/update_exception_list_item_schema.mock.ts +++ /dev/null @@ -1,53 +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 { UpdateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; - -import { - DESCRIPTION, - ENTRIES, - ID, - ITEM_ID, - ITEM_TYPE, - LIST_ITEM_ID, - META, - NAME, - NAMESPACE_TYPE, - OS_TYPES, - TAGS, - UPDATED_COMMENTS, -} from '../constants.mock'; - -export const getUpdateExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ - _version: undefined, - comments: UPDATED_COMMENTS, - description: DESCRIPTION, - entries: ENTRIES, - id: ID, - item_id: LIST_ITEM_ID, - meta: META, - name: NAME, - namespace_type: NAMESPACE_TYPE, - os_types: ['linux'], - tags: TAGS, - type: ITEM_TYPE, -}); - -/** - * Useful for end to end tests and other mechanisms which want to fill in the values - * after doing a get of the structure. - */ -export const getUpdateMinimalExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ - description: DESCRIPTION, - entries: ENTRIES, - item_id: ITEM_ID, - name: NAME, - os_types: OS_TYPES, - type: ITEM_TYPE, -}); diff --git a/packages/kbn-securitysolution-list-hooks/src/mocks/response/exception_list_item_schema.mock.ts b/packages/kbn-securitysolution-list-hooks/src/mocks/response/exception_list_item_schema.mock.ts deleted file mode 100644 index 0c5e732a1f30..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/mocks/response/exception_list_item_schema.mock.ts +++ /dev/null @@ -1,72 +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 { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; - -import { - COMMENTS, - DATE_NOW, - DESCRIPTION, - ELASTIC_USER, - ENTRIES, - ITEM_ID, - ITEM_TYPE, - LIST_ID, - META, - NAME, - NAMESPACE_TYPE, - OS_TYPES, - TIE_BREAKER, - USER, -} from '../constants.mock'; - -export const getExceptionListItemSchemaMock = ( - overrides?: Partial<ExceptionListItemSchema> -): ExceptionListItemSchema => ({ - _version: undefined, - comments: COMMENTS, - created_at: DATE_NOW, - created_by: USER, - description: DESCRIPTION, - entries: ENTRIES, - expire_time: undefined, - id: '1', - item_id: 'endpoint_list_item', - list_id: 'endpoint_list_id', - meta: META, - name: NAME, - namespace_type: NAMESPACE_TYPE, - os_types: [], - tags: ['user added string for a tag', 'malware'], - tie_breaker_id: TIE_BREAKER, - type: ITEM_TYPE, - updated_at: DATE_NOW, - updated_by: USER, - ...(overrides || {}), -}); - -/** - * This is useful for end to end tests where we remove the auto generated parts for comparisons - * such as created_at, updated_at, and id. - */ -export const getExceptionListItemResponseMockWithoutAutoGeneratedValues = - (): Partial<ExceptionListItemSchema> => ({ - comments: [], - created_by: ELASTIC_USER, - description: DESCRIPTION, - entries: ENTRIES, - item_id: ITEM_ID, - list_id: LIST_ID, - name: NAME, - namespace_type: 'single', - os_types: OS_TYPES, - tags: [], - type: ITEM_TYPE, - updated_by: ELASTIC_USER, - }); diff --git a/packages/kbn-securitysolution-list-hooks/src/mocks/response/found_list_schema.mock.ts b/packages/kbn-securitysolution-list-hooks/src/mocks/response/found_list_schema.mock.ts deleted file mode 100644 index 436ddcd7a83d..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/mocks/response/found_list_schema.mock.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { FoundListSchema } from '@kbn/securitysolution-io-ts-list-types'; - -import { getListResponseMock } from './list_schema.mock'; - -export const getFoundListSchemaMock = (): FoundListSchema => ({ - cursor: '123', - data: [getListResponseMock()], - page: 1, - per_page: 1, - total: 1, -}); diff --git a/packages/kbn-securitysolution-list-hooks/src/mocks/response/list_schema.mock.ts b/packages/kbn-securitysolution-list-hooks/src/mocks/response/list_schema.mock.ts deleted file mode 100644 index 79ed25641977..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/mocks/response/list_schema.mock.ts +++ /dev/null @@ -1,57 +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 { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; - -import { - DATE_NOW, - DESCRIPTION, - ELASTIC_USER, - IMMUTABLE, - LIST_ID, - META, - NAME, - TIE_BREAKER, - TYPE, - USER, - VERSION, -} from '../constants.mock'; - -export const getListResponseMock = (): ListSchema => ({ - '@timestamp': DATE_NOW, - _version: undefined, - created_at: DATE_NOW, - created_by: USER, - description: DESCRIPTION, - deserializer: undefined, - id: LIST_ID, - immutable: IMMUTABLE, - meta: META, - name: NAME, - serializer: undefined, - tie_breaker_id: TIE_BREAKER, - type: TYPE, - updated_at: DATE_NOW, - updated_by: USER, - version: VERSION, -}); - -/** - * This is useful for end to end tests where we remove the auto generated parts for comparisons - * such as created_at, updated_at, and id. - */ -export const getListResponseMockWithoutAutoGeneratedValues = (): Partial<ListSchema> => ({ - created_by: ELASTIC_USER, - description: DESCRIPTION, - immutable: IMMUTABLE, - name: NAME, - type: TYPE, - updated_by: ELASTIC_USER, - version: VERSION, -}); diff --git a/packages/kbn-securitysolution-list-hooks/src/transforms/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/transforms/index.test.ts deleted file mode 100644 index bb0cb6e6e907..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/transforms/index.test.ts +++ /dev/null @@ -1,299 +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 { - CreateExceptionListItemSchema, - Entry, - EntryMatch, - EntryNested, - ExceptionListItemSchema, - UpdateExceptionListItemSchema, -} from '@kbn/securitysolution-io-ts-list-types'; -import { - addIdToExceptionItemEntries, - removeIdFromExceptionItemsEntries, - transformInput, - transformOutput, -} from '../..'; - -import { getCreateExceptionListItemSchemaMock } from '../mocks/request/create_exception_list_item_schema.mock'; -import { getUpdateExceptionListItemSchemaMock } from '../mocks/request/update_exception_list_item_schema.mock'; -import { getExceptionListItemSchemaMock } from '../mocks/response/exception_list_item_schema.mock'; -import { COMMENTS_WITH_CREATEDAT_CREATEDBY, ENTRIES_WITH_IDS } from '../mocks/constants.mock'; - -jest.mock('uuid', () => ({ - v4: jest.fn().mockReturnValue('123'), -})); - -describe('Exceptions transforms', () => { - describe('transformOutput', () => { - it('should return same output as input with stripped ids per entry - CreateExceptionListItemSchema', () => { - const mockCreateExceptionItem = { - ...getCreateExceptionListItemSchemaMock(), - entries: ENTRIES_WITH_IDS, - }; - const output = transformOutput(mockCreateExceptionItem); - const expectedOutput: CreateExceptionListItemSchema = getCreateExceptionListItemSchemaMock(); - - expect(output).toEqual(expectedOutput); - }); - - it('should return same output as input with stripped ids per entry - UpdateExceptionListItemSchema', () => { - const mockUpdateExceptionItem = { - ...getUpdateExceptionListItemSchemaMock(), - entries: ENTRIES_WITH_IDS, - }; - const output = transformOutput(mockUpdateExceptionItem); - const expectedOutput: UpdateExceptionListItemSchema = getUpdateExceptionListItemSchemaMock(); - - expect(output).toEqual(expectedOutput); - }); - it('should return output as input with stripped createdAt and createdBy per entry - UpdateExceptionListItemSchema', () => { - const mockUpdateExceptionItem = { - ...getUpdateExceptionListItemSchemaMock(), - entries: ENTRIES_WITH_IDS, - comments: COMMENTS_WITH_CREATEDAT_CREATEDBY, - }; - const output = transformOutput(mockUpdateExceptionItem); - const expectedOutput: UpdateExceptionListItemSchema = getUpdateExceptionListItemSchemaMock(); - - expect(output).toEqual(expectedOutput); - }); - }); - - describe('transformInput', () => { - it('should return same output as input with added ids per entry', () => { - const mockExceptionItem = getExceptionListItemSchemaMock(); - const output = transformInput(mockExceptionItem); - const expectedOutput: ExceptionListItemSchema = { - ...getExceptionListItemSchemaMock(), - entries: ENTRIES_WITH_IDS, - }; - - expect(output).toEqual(expectedOutput); - }); - }); - - describe('addIdToExceptionItemEntries', () => { - it('should return same output as input with added ids per entry', () => { - const mockExceptionItem: ExceptionListItemSchema = { - ...getExceptionListItemSchemaMock(), - entries: [ - { - field: 'some.not.nested.field', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - }; - const output = addIdToExceptionItemEntries(mockExceptionItem); - const expectedOutput: ExceptionListItemSchema = { - ...getExceptionListItemSchemaMock(), - entries: [ - { - field: 'some.not.nested.field', - id: '123', - operator: 'included', - type: 'match', - value: 'some value', - } as Entry & { id: string }, - ], - }; - - expect(output).toEqual(expectedOutput); - }); - - it('should return same output as input with added ids per nested entry', () => { - const mockExceptionItem: ExceptionListItemSchema = { - ...getExceptionListItemSchemaMock(), - entries: [ - { - entries: [ - { - field: 'nested.field', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - field: 'some.parentField', - type: 'nested', - }, - ], - }; - const output = addIdToExceptionItemEntries(mockExceptionItem); - const expectedOutput: ExceptionListItemSchema = { - ...getExceptionListItemSchemaMock(), - entries: [ - { - entries: [ - { - field: 'nested.field', - id: '123', - operator: 'included', - type: 'match', - value: 'some value', - } as EntryMatch & { id: string }, - ], - field: 'some.parentField', - id: '123', - type: 'nested', - } as EntryNested & { id: string }, - ], - }; - - expect(output).toEqual(expectedOutput); - }); - }); - - describe('removeIdFromExceptionItemsEntries', () => { - it('should return same output as input with stripped ids per entry - CreateExceptionListItemSchema', () => { - const mockCreateExceptionItem = { - ...getCreateExceptionListItemSchemaMock(), - entries: [ - { - field: 'some.not.nested.field', - id: '123', - operator: 'included', - type: 'match', - value: 'some value', - } as Entry & { id: string }, - ], - }; - const output = removeIdFromExceptionItemsEntries(mockCreateExceptionItem); - const expectedOutput: CreateExceptionListItemSchema = { - ...getCreateExceptionListItemSchemaMock(), - entries: [ - { - field: 'some.not.nested.field', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - }; - - expect(output).toEqual(expectedOutput); - }); - - it('should return same output as input with stripped ids per nested entry - CreateExceptionListItemSchema', () => { - const mockCreateExceptionItem = { - ...getCreateExceptionListItemSchemaMock(), - entries: [ - { - entries: [ - { - field: 'nested.field', - id: '123', - operator: 'included', - type: 'match', - value: 'some value', - } as EntryMatch & { id: string }, - ], - field: 'some.parentField', - id: '123', - type: 'nested', - } as EntryNested & { id: string }, - ], - }; - const output = removeIdFromExceptionItemsEntries(mockCreateExceptionItem); - const expectedOutput: CreateExceptionListItemSchema = { - ...getCreateExceptionListItemSchemaMock(), - entries: [ - { - entries: [ - { - field: 'nested.field', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - field: 'some.parentField', - type: 'nested', - }, - ], - }; - - expect(output).toEqual(expectedOutput); - }); - - it('should return same output as input with stripped ids per entry - UpdateExceptionListItemSchema', () => { - const mockUpdateExceptionItem = { - ...getUpdateExceptionListItemSchemaMock(), - entries: [ - { - field: 'some.not.nested.field', - id: '123', - operator: 'included', - type: 'match', - value: 'some value', - } as Entry & { id: string }, - ], - }; - const output = removeIdFromExceptionItemsEntries(mockUpdateExceptionItem); - const expectedOutput: UpdateExceptionListItemSchema = { - ...getUpdateExceptionListItemSchemaMock(), - entries: [ - { - field: 'some.not.nested.field', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - }; - - expect(output).toEqual(expectedOutput); - }); - - it('should return same output as input with stripped ids per nested entry - UpdateExceptionListItemSchema', () => { - const mockUpdateExceptionItem = { - ...getUpdateExceptionListItemSchemaMock(), - entries: [ - { - entries: [ - { - field: 'nested.field', - id: '123', - operator: 'included', - type: 'match', - value: 'some value', - } as EntryMatch & { id: string }, - ], - field: 'some.parentField', - id: '123', - type: 'nested', - } as EntryNested & { id: string }, - ], - }; - const output = removeIdFromExceptionItemsEntries(mockUpdateExceptionItem); - const expectedOutput: UpdateExceptionListItemSchema = { - ...getUpdateExceptionListItemSchemaMock(), - entries: [ - { - entries: [ - { - field: 'nested.field', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - field: 'some.parentField', - type: 'nested', - }, - ], - }; - - expect(output).toEqual(expectedOutput); - }); - }); -}); diff --git a/packages/kbn-securitysolution-list-hooks/src/transforms/index.ts b/packages/kbn-securitysolution-list-hooks/src/transforms/index.ts deleted file mode 100644 index c30ea9ad8679..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/transforms/index.ts +++ /dev/null @@ -1,138 +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 { flow } from 'fp-ts/lib/function'; -import { addIdToItem, removeIdFromItem } from '@kbn/securitysolution-utils'; -import type { - CreateExceptionListItemSchema, - EntriesArray, - Entry, - ExceptionListItemSchema, - UpdateExceptionListItemSchema, -} from '@kbn/securitysolution-io-ts-list-types'; - -// These are a collection of transforms that are UI specific and useful for UI concerns -// that are inserted between the API and the actual user interface. In some ways these -// might be viewed as technical debt or to compensate for the differences and preferences -// of how ReactJS might prefer data vs. how we want to model data. Each function should have -// a description giving context around the transform. - -/** - * Transforms the output of exception items to compensate for technical debt or UI concerns such as - * ReactJS preferences for having ids within arrays if the data is not modeled that way. - * - * If you add a new transform of the output called "myNewTransform" do it - * in the form of: - * flow(removeIdFromExceptionItemsEntries, myNewTransform)(exceptionItem) - * - * @param exceptionItem The exceptionItem to transform the output of - * @returns The exceptionItem transformed from the output - */ -export const transformOutput = ( - exceptionItem: UpdateExceptionListItemSchema | ExceptionListItemSchema -): UpdateExceptionListItemSchema | ExceptionListItemSchema => - flow( - removeCreatedAtCreatedByFromCommentsOnUpdate, - removeIdFromExceptionItemsEntries - )(exceptionItem); - -export const transformNewItemOutput = ( - exceptionItem: CreateExceptionListItemSchema -): CreateExceptionListItemSchema => flow(removeIdFromExceptionItemsEntries)(exceptionItem); - -/** - * Transforms the output of rules to compensate for technical debt or UI concerns such as - * ReactJS preferences for having ids within arrays if the data is not modeled that way. - * - * If you add a new transform of the input called "myNewTransform" do it - * in the form of: - * flow(addIdToExceptionItemEntries, myNewTransform)(exceptionItem) - * - * @param exceptionItem The exceptionItem to transform the output of - * @returns The exceptionItem transformed from the output - */ -export const transformInput = (exceptionItem: ExceptionListItemSchema): ExceptionListItemSchema => - flow(addIdToExceptionItemEntries)(exceptionItem); - -/** - * This adds an id to the incoming exception item entries as ReactJS prefers to have - * an id added to them for use as a stable id. Later if we decide to change the data - * model to have id's within the array then this code should be removed. If not, then - * this code should stay as an adapter for ReactJS. - * - * This does break the type system slightly as we are lying a bit to the type system as we return - * the same exceptionItem as we have previously but are augmenting the arrays with an id which TypeScript - * doesn't mind us doing here. However, downstream you will notice that you have an id when the type - * does not indicate it. In that case use (ExceptionItem & { id: string }) temporarily if you're using the id. If you're not, - * you can ignore the id and just use the normal TypeScript with ReactJS. - * - * @param exceptionItem The exceptionItem to add an id to the threat matches. - * @returns exceptionItem The exceptionItem but with id added to the exception item entries - */ -export const addIdToExceptionItemEntries = ( - exceptionItem: ExceptionListItemSchema -): ExceptionListItemSchema => { - const entries = exceptionItem.entries.map((entry) => { - if (entry.type === 'nested') { - return addIdToItem({ - ...entry, - entries: entry.entries.map((nestedEntry) => addIdToItem(nestedEntry)), - }); - } else { - return addIdToItem(entry); - } - }); - return { ...exceptionItem, entries }; -}; - -/** - * This removes an id from the exceptionItem entries as ReactJS prefers to have - * an id added to them for use as a stable id. Later if we decide to change the data - * model to have id's within the array then this code should be removed. If not, then - * this code should stay as an adapter for ReactJS. - * - * @param exceptionItem The exceptionItem to remove an id from the entries. - * @returns exceptionItem The exceptionItem but with id removed from the entries - */ -export const removeIdFromExceptionItemsEntries = <T extends { entries: EntriesArray }>( - exceptionItem: T -): T => { - const { entries } = exceptionItem; - const entriesNoId = entries.map((entry) => { - if (entry.type === 'nested') { - return removeIdFromItem({ - ...entry, - entries: entry.entries.map((nestedEntry) => removeIdFromItem(nestedEntry)), - }); - } else { - return removeIdFromItem<Entry>(entry); - } - }); - return { ...exceptionItem, entries: entriesNoId }; -}; - -/** - * This removes createdAt, createdBy from the exceptionItem if a comment was added to - * the Exception item, and return the comment message with id to prevent creating the commet - * twice - * @param exceptionItem The exceptionItem to remove createdAt, createdBy from the comments array. - * @returns exceptionItem The exceptionItem with comments do not have createdAt, createdBy. - */ -export const removeCreatedAtCreatedByFromCommentsOnUpdate = ( - exceptionItem: UpdateExceptionListItemSchema | ExceptionListItemSchema -) => { - const { comments } = exceptionItem; - if (!comments || !comments.length) return exceptionItem; - - const entriesNoCreatedAtAndBy = comments.map(({ comment, id }) => ({ - comment, - id, - })); - return { ...exceptionItem, comments: entriesNoCreatedAtAndBy }; -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts deleted file mode 100644 index 4ce917f5ad95..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts +++ /dev/null @@ -1,317 +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 { useMemo } from 'react'; -import type { - CreateExceptionListItemSchema, - ExceptionListItemSchema, - ExceptionListSchema, - UpdateExceptionListItemSchema, - ApiCallFindListsItemsMemoProps, - ApiCallMemoProps, - ApiListExportProps, - ApiCallGetExceptionFilterFromIdsMemoProps, - ApiCallGetExceptionFilterFromExceptionsMemoProps, - ApiListDuplicateProps, -} from '@kbn/securitysolution-io-ts-list-types'; -import * as Api from '@kbn/securitysolution-list-api'; -import { getIdsAndNamespaces } from '@kbn/securitysolution-list-utils'; -import type { HttpStart } from '@kbn/core-http-browser'; - -import { transformInput, transformNewItemOutput, transformOutput } from '../transforms'; - -export interface ExceptionsApi { - addExceptionListItem: (arg: { - listItem: CreateExceptionListItemSchema; - }) => Promise<ExceptionListItemSchema>; - updateExceptionListItem: (arg: { - listItem: UpdateExceptionListItemSchema; - }) => Promise<ExceptionListItemSchema>; - deleteExceptionItem: (arg: ApiCallMemoProps) => Promise<void>; - deleteExceptionList: (arg: ApiCallMemoProps) => Promise<void>; - duplicateExceptionList: (arg: ApiListDuplicateProps) => Promise<void>; - getExceptionItem: ( - arg: ApiCallMemoProps & { onSuccess: (arg: ExceptionListItemSchema) => void } - ) => Promise<void>; - getExceptionList: ( - arg: ApiCallMemoProps & { onSuccess: (arg: ExceptionListSchema) => void } - ) => Promise<void>; - getExceptionListsItems: (arg: ApiCallFindListsItemsMemoProps) => Promise<void>; - getExceptionFilterFromIds: (arg: ApiCallGetExceptionFilterFromIdsMemoProps) => Promise<void>; - getExceptionFilterFromExceptions: ( - arg: ApiCallGetExceptionFilterFromExceptionsMemoProps - ) => Promise<void>; - exportExceptionList: (arg: ApiListExportProps) => Promise<void>; -} - -export const useApi = (http: HttpStart): ExceptionsApi => { - return useMemo( - (): ExceptionsApi => ({ - async addExceptionListItem({ - listItem, - }: { - listItem: CreateExceptionListItemSchema; - }): Promise<ExceptionListItemSchema> { - const abortCtrl = new AbortController(); - const sanitizedItem: CreateExceptionListItemSchema = transformNewItemOutput(listItem); - - return Api.addExceptionListItem({ - http, - listItem: sanitizedItem, - signal: abortCtrl.signal, - }); - }, - async deleteExceptionItem({ - id, - namespaceType, - onSuccess, - onError, - }: ApiCallMemoProps): Promise<void> { - const abortCtrl = new AbortController(); - - try { - await Api.deleteExceptionListItemById({ - http, - id, - namespaceType, - signal: abortCtrl.signal, - }); - onSuccess(); - } catch (error) { - onError(error); - } - }, - async deleteExceptionList({ - id, - namespaceType, - onSuccess, - onError, - }: ApiCallMemoProps): Promise<void> { - const abortCtrl = new AbortController(); - - try { - await Api.deleteExceptionListById({ - http, - id, - namespaceType, - signal: abortCtrl.signal, - }); - onSuccess(); - } catch (error) { - onError(error); - } - }, - async duplicateExceptionList({ - includeExpiredExceptions, - listId, - namespaceType, - onError, - onSuccess, - }: ApiListDuplicateProps): Promise<void> { - const abortCtrl = new AbortController(); - - try { - const newList = await Api.duplicateExceptionList({ - http, - includeExpiredExceptions, - listId, - namespaceType, - signal: abortCtrl.signal, - }); - onSuccess(newList); - } catch (error) { - onError(error); - } - }, - async exportExceptionList({ - id, - includeExpiredExceptions, - listId, - namespaceType, - onError, - onSuccess, - }: ApiListExportProps): Promise<void> { - const abortCtrl = new AbortController(); - - try { - const blob = await Api.exportExceptionList({ - http, - id, - includeExpiredExceptions, - listId, - namespaceType, - signal: abortCtrl.signal, - }); - onSuccess(blob); - } catch (error) { - onError(error); - } - }, - async getExceptionItem({ - id, - namespaceType, - onSuccess, - onError, - }: ApiCallMemoProps & { onSuccess: (arg: ExceptionListItemSchema) => void }): Promise<void> { - const abortCtrl = new AbortController(); - - try { - const item = transformInput( - await Api.fetchExceptionListItemById({ - http, - id, - namespaceType, - signal: abortCtrl.signal, - }) - ); - onSuccess(item); - } catch (error) { - onError(error); - } - }, - async getExceptionList({ - id, - namespaceType, - onSuccess, - onError, - }: ApiCallMemoProps & { onSuccess: (arg: ExceptionListSchema) => void }): Promise<void> { - const abortCtrl = new AbortController(); - - try { - const list = await Api.fetchExceptionListById({ - http, - id, - namespaceType, - signal: abortCtrl.signal, - }); - onSuccess(list); - } catch (error) { - onError(error); - } - }, - async getExceptionListsItems({ - lists, - filter, - pagination, - showDetectionsListsOnly, - showEndpointListsOnly, - onSuccess, - onError, - }: ApiCallFindListsItemsMemoProps): Promise<void> { - const abortCtrl = new AbortController(); - const { ids, namespaces } = getIdsAndNamespaces({ - lists, - showDetection: showDetectionsListsOnly, - showEndpoint: showEndpointListsOnly, - }); - - try { - if (ids.length > 0 && namespaces.length > 0) { - const { - data, - page, - per_page: perPage, - total, - } = await Api.fetchExceptionListsItemsByListIds({ - filter, - http, - listIds: ids, - namespaceTypes: namespaces, - pagination, - signal: abortCtrl.signal, - }); - onSuccess({ - // This data transform is UI specific and useful for UI concerns - // to compensate for the differences and preferences of how ReactJS might prefer - // data vs. how we want to model data. View `transformInput` for more details - exceptions: data.map((item) => transformInput(item)), - pagination: { - page, - perPage, - total, - }, - }); - } else { - onSuccess({ - exceptions: [], - pagination: { - page: 0, - perPage: pagination.perPage != null ? pagination.perPage : 0, - total: 0, - }, - }); - } - } catch (error) { - onError(error); - } - }, - async getExceptionFilterFromIds({ - exceptionListIds, - chunkSize, - alias, - excludeExceptions, - onSuccess, - onError, - }: ApiCallGetExceptionFilterFromIdsMemoProps): Promise<void> { - const abortCtrl = new AbortController(); - try { - const { filter } = await Api.getExceptionFilterFromExceptionListIds({ - http, - exceptionListIds, - signal: abortCtrl.signal, - chunkSize, - alias, - excludeExceptions, - }); - onSuccess(filter); - } catch (error) { - onError(error); - } - }, - async getExceptionFilterFromExceptions({ - exceptions, - chunkSize, - alias, - excludeExceptions, - onSuccess, - onError, - }: ApiCallGetExceptionFilterFromExceptionsMemoProps): Promise<void> { - const abortCtrl = new AbortController(); - try { - const { filter } = await Api.getExceptionFilterFromExceptions({ - http, - exceptions, - signal: abortCtrl.signal, - chunkSize, - alias, - excludeExceptions, - }); - onSuccess(filter); - } catch (error) { - onError(error); - } - }, - async updateExceptionListItem({ - listItem, - }: { - listItem: UpdateExceptionListItemSchema; - }): Promise<ExceptionListItemSchema> { - const abortCtrl = new AbortController(); - const sanitizedItem: UpdateExceptionListItemSchema = transformOutput(listItem); - - return Api.updateExceptionListItem({ - http, - listItem: sanitizedItem, - signal: abortCtrl.signal, - }); - }, - }), - [http] - ); -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.ts deleted file mode 100644 index 06b87f510796..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.ts +++ /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 { useMutation, useQueryClient } from '@tanstack/react-query'; - -import { createListIndex, ApiParams } from '@kbn/securitysolution-list-api'; -import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; - -import { READ_INDEX_QUERY_KEY } from '../constants'; - -const createListIndexWithOptionalSignal = withOptionalSignal(createListIndex); - -export const useCreateListIndex = ({ - http, - onError, -}: { - http: ApiParams['http']; - onError?: (err: unknown) => void; -}) => { - const queryClient = useQueryClient(); - - const { mutate, isLoading, error } = useMutation( - () => createListIndexWithOptionalSignal({ http }), - { - onSuccess: () => { - queryClient.invalidateQueries(READ_INDEX_QUERY_KEY); - }, - onError, - } - ); - - return { - start: mutate, - loading: isLoading, - error, - }; -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_create_list_item/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_create_list_item/index.ts deleted file mode 100644 index 98939d93185d..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_create_list_item/index.ts +++ /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 type { UseMutationOptions } from '@tanstack/react-query'; -import type { ListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import type { IHttpFetchError } from '@kbn/core-http-browser'; -import { useMutation } from '@tanstack/react-query'; -import { createListItem } from '@kbn/securitysolution-list-api'; -import type { CreateListItemParams } from '@kbn/securitysolution-list-api'; -import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; - -import { useInvalidateListItemQuery } from '../use_find_list_items'; - -const createListItemWithOptionalSignal = withOptionalSignal(createListItem); - -export const CREATE_LIST_ITEM_MUTATION_KEY = ['POST', 'LIST_ITEM_CREATE']; -type CreateListMutationParams = Omit<CreateListItemParams, 'refresh' | 'signal'>; - -export const useCreateListItemMutation = ( - options?: UseMutationOptions<ListItemSchema, IHttpFetchError<Error>, CreateListMutationParams> -) => { - const invalidateListItemQuery = useInvalidateListItemQuery(); - return useMutation<ListItemSchema, IHttpFetchError<Error>, CreateListMutationParams>( - ({ listId, value, http }) => - createListItemWithOptionalSignal({ listId, value, http, refresh: 'wait_for' }), - { - ...options, - mutationKey: CREATE_LIST_ITEM_MUTATION_KEY, - onSettled: (...args) => { - invalidateListItemQuery(); - if (options?.onSettled) { - options.onSettled(...args); - } - }, - } - ); -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.test.ts deleted file mode 100644 index ca94eda81f95..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.test.ts +++ /dev/null @@ -1,121 +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 { renderHook, act } from '@testing-library/react'; - -import { UseCursorProps, useCursor } from '.'; - -describe('useCursor', () => { - it('returns undefined cursor if no values have been set', () => { - const { result } = renderHook((props: UseCursorProps) => useCursor(props), { - initialProps: { pageIndex: 0, pageSize: 0 }, - }); - - expect(result.current[0]).toBeUndefined(); - }); - - it('retrieves a cursor for the next page of a given page size', () => { - const { rerender, result } = renderHook((props: UseCursorProps) => useCursor(props), { - initialProps: { pageIndex: 0, pageSize: 0 }, - }); - rerender({ pageIndex: 1, pageSize: 1 }); - act(() => { - result.current[1]('new_cursor'); - }); - - expect(result.current[0]).toBeUndefined(); - - rerender({ pageIndex: 2, pageSize: 1 }); - expect(result.current[0]).toEqual('new_cursor'); - }); - - it('returns undefined cursor for an unknown search', () => { - const { rerender, result } = renderHook((props: UseCursorProps) => useCursor(props), { - initialProps: { pageIndex: 0, pageSize: 0 }, - }); - act(() => { - result.current[1]('new_cursor'); - }); - - rerender({ pageIndex: 1, pageSize: 2 }); - expect(result.current[0]).toBeUndefined(); - }); - - it('remembers cursor through rerenders', () => { - const { rerender, result } = renderHook((props: UseCursorProps) => useCursor(props), { - initialProps: { pageIndex: 0, pageSize: 0 }, - }); - - rerender({ pageIndex: 1, pageSize: 1 }); - act(() => { - result.current[1]('new_cursor'); - }); - - rerender({ pageIndex: 2, pageSize: 1 }); - expect(result.current[0]).toEqual('new_cursor'); - - rerender({ pageIndex: 0, pageSize: 0 }); - expect(result.current[0]).toBeUndefined(); - - rerender({ pageIndex: 2, pageSize: 1 }); - expect(result.current[0]).toEqual('new_cursor'); - }); - - it('remembers multiple cursors', () => { - const { rerender, result } = renderHook((props: UseCursorProps) => useCursor(props), { - initialProps: { pageIndex: 0, pageSize: 0 }, - }); - - rerender({ pageIndex: 1, pageSize: 1 }); - act(() => { - result.current[1]('new_cursor'); - }); - rerender({ pageIndex: 2, pageSize: 2 }); - act(() => { - result.current[1]('another_cursor'); - }); - - rerender({ pageIndex: 2, pageSize: 1 }); - expect(result.current[0]).toEqual('new_cursor'); - - rerender({ pageIndex: 3, pageSize: 2 }); - expect(result.current[0]).toEqual('another_cursor'); - }); - - it('returns the "nearest" cursor for the given page size', () => { - const { rerender, result } = renderHook((props: UseCursorProps) => useCursor(props), { - initialProps: { pageIndex: 0, pageSize: 0 }, - }); - - rerender({ pageIndex: 1, pageSize: 2 }); - act(() => { - result.current[1]('cursor1'); - }); - rerender({ pageIndex: 2, pageSize: 2 }); - act(() => { - result.current[1]('cursor2'); - }); - rerender({ pageIndex: 3, pageSize: 2 }); - act(() => { - result.current[1]('cursor3'); - }); - - rerender({ pageIndex: 2, pageSize: 2 }); - expect(result.current[0]).toEqual('cursor1'); - - rerender({ pageIndex: 3, pageSize: 2 }); - expect(result.current[0]).toEqual('cursor2'); - - rerender({ pageIndex: 4, pageSize: 2 }); - expect(result.current[0]).toEqual('cursor3'); - - rerender({ pageIndex: 6, pageSize: 2 }); - expect(result.current[0]).toEqual('cursor3'); - }); -}); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.ts deleted file mode 100644 index b8e8df043882..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.ts +++ /dev/null @@ -1,46 +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 { useCallback, useState } from 'react'; - -export interface UseCursorProps { - pageIndex: number; - pageSize: number; -} -type Cursor = string | undefined; -type SetCursor = (cursor: Cursor) => void; -type UseCursor = (props: UseCursorProps) => [Cursor, SetCursor]; - -const hash = (props: UseCursorProps): string => JSON.stringify(props); - -export const useCursor: UseCursor = ({ pageIndex, pageSize }) => { - const [cache, setCache] = useState<Record<string, Cursor>>({}); - - const setCursor = useCallback<SetCursor>( - (cursor) => { - setCache({ - ...cache, - [hash({ pageIndex: pageIndex + 1, pageSize })]: cursor, - }); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [pageIndex, pageSize] - ); - - let cursor: Cursor; - for (let i = pageIndex; i >= 0; i--) { - const currentProps = { pageIndex: i, pageSize }; - cursor = cache[hash(currentProps)]; - if (cursor) { - break; - } - } - - return [cursor, setCursor]; -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.ts deleted file mode 100644 index fe88e19f8489..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.ts +++ /dev/null @@ -1,15 +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 { deleteList } from '@kbn/securitysolution-list-api'; -import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; - -const deleteListWithOptionalSignal = withOptionalSignal(deleteList); - -export const useDeleteList = () => useAsync(deleteListWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_delete_list_item/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_delete_list_item/index.ts deleted file mode 100644 index 63107edda73a..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_delete_list_item/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { UseMutationOptions } from '@tanstack/react-query'; -import type { ListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { useMutation } from '@tanstack/react-query'; -import { deleteListItem } from '@kbn/securitysolution-list-api'; -import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { DeleteListItemParams } from '@kbn/securitysolution-list-api'; -import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; -import { useInvalidateListItemQuery } from '../use_find_list_items'; - -const deleteListItemWithOptionalSignal = withOptionalSignal(deleteListItem); - -export const DELETE_LIST_ITEM_MUTATION_KEY = ['POST', ' DELETE_LIST_ITEM_MUTATION']; -type DeleteListMutationParams = Omit<DeleteListItemParams, 'refresh' | 'signal'>; - -export const useDeleteListItemMutation = ( - options?: UseMutationOptions<ListItemSchema, IHttpFetchError<Error>, DeleteListMutationParams> -) => { - const invalidateListItemQuery = useInvalidateListItemQuery(); - return useMutation<ListItemSchema, IHttpFetchError<Error>, DeleteListMutationParams>( - ({ id, http }) => deleteListItemWithOptionalSignal({ id, http }), - { - ...options, - mutationKey: DELETE_LIST_ITEM_MUTATION_KEY, - onSettled: (...args) => { - invalidateListItemQuery(); - if (options?.onSettled) { - options.onSettled(...args); - } - }, - } - ); -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.ts deleted file mode 100644 index 2907de45eba4..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.ts +++ /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", 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 { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import type { - ExceptionListSchema, - UseExceptionListsProps, - Pagination, - Sort, -} from '@kbn/securitysolution-io-ts-list-types'; -import { fetchExceptionLists } from '@kbn/securitysolution-list-api'; - -import { getFilters } from '@kbn/securitysolution-list-utils'; - -export type Func = () => void; -export type ReturnExceptionLists = [ - loading: boolean, - exceptionLists: ExceptionListSchema[], - pagination: Pagination, - setPagination: React.Dispatch<React.SetStateAction<Pagination>>, - fetchLists: Func | null, - sort: Sort, - setSort: React.Dispatch<React.SetStateAction<Sort>> -]; - -const DEFAULT_PAGINATION = { - page: 1, - perPage: 20, - total: 0, -}; - -const DEFAULT_SORT = { - field: 'created_at', - order: 'desc', -}; - -/** - * Hook for fetching ExceptionLists - * - * @param http Kibana http service - * @param errorMessage message shown to user if error occurs - * @param filterOptions filter by certain fields - * @param namespaceTypes spaces to be searched - * @param notifications kibana service for displaying toasters - * @param hideLists a list of listIds we don't want to query - * @param initialPagination - * - */ -export const useExceptionLists = ({ - errorMessage, - http, - initialPagination = DEFAULT_PAGINATION, - filterOptions = {}, - namespaceTypes, - notifications, - hideLists = [], - initialSort = DEFAULT_SORT, -}: UseExceptionListsProps): ReturnExceptionLists => { - const [exceptionLists, setExceptionLists] = useState<ExceptionListSchema[]>([]); - const [pagination, setPagination] = useState<Pagination>(initialPagination); - const [sort, setSort] = useState<Sort>(initialSort); - const [loading, setLoading] = useState(true); - const abortCtrlRef = useRef<AbortController>(); - - const namespaceTypesAsString = useMemo(() => namespaceTypes.join(','), [namespaceTypes]); - const filters = useMemo( - (): string => - getFilters({ - filters: filterOptions, - namespaceTypes, - hideLists, - }), - [namespaceTypes, filterOptions, hideLists] - ); - - const fetchData = useCallback(async (): Promise<void> => { - try { - setLoading(true); - - abortCtrlRef.current = new AbortController(); - - const { - page, - per_page: perPage, - total, - data, - } = await fetchExceptionLists({ - filters, - http, - namespaceTypes: namespaceTypesAsString, - pagination: { - page: pagination.page, - perPage: pagination.perPage, - }, - sort, - signal: abortCtrlRef.current.signal, - }); - - setPagination({ - page, - perPage, - total, - }); - setExceptionLists(data); - setLoading(false); - } catch (error) { - if (error.name !== 'AbortError') { - notifications.toasts.addError(error, { - title: errorMessage, - }); - setExceptionLists([]); - setPagination(DEFAULT_PAGINATION); - setLoading(false); - } - } - }, [ - errorMessage, - filters, - http, - namespaceTypesAsString, - notifications.toasts, - pagination.page, - pagination.perPage, - sort, - ]); - - useEffect(() => { - fetchData(); - - return (): void => { - abortCtrlRef.current?.abort(); - }; - }, [fetchData]); - - return [loading, exceptionLists, pagination, setPagination, fetchData, sort, setSort]; -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.ts deleted file mode 100644 index 77a213cc47b9..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.ts +++ /dev/null @@ -1,15 +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 { exportList } from '@kbn/securitysolution-list-api'; -import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; - -const exportListWithOptionalSignal = withOptionalSignal(exportList); - -export const useExportList = () => useAsync(exportListWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_find_list_items/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_find_list_items/index.ts deleted file mode 100644 index f385408d26fd..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_find_list_items/index.ts +++ /dev/null @@ -1,75 +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 { useCallback } from 'react'; -import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { findListItems, ApiParams } from '@kbn/securitysolution-list-api'; -import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; -import { useCursor } from '../use_cursor'; - -const findListItemsWithOptionalSignal = withOptionalSignal(findListItems); - -const FIND_LIST_ITEMS_QUERY_KEY = 'FIND_LIST_ITEMS'; - -export const useInvalidateListItemQuery = () => { - const queryClient = useQueryClient(); - - return useCallback(() => { - queryClient.invalidateQueries([FIND_LIST_ITEMS_QUERY_KEY], { - refetchType: 'active', - }); - }, [queryClient]); -}; - -export const useFindListItems = ({ - pageIndex, - pageSize, - sortField, - sortOrder, - listId, - filter, - http, -}: { - pageIndex: number; - pageSize: number; - sortField: string; - sortOrder: 'asc' | 'desc'; - listId: string; - filter: string; - http: ApiParams['http']; -}) => { - const [cursor, setCursor] = useCursor({ pageIndex, pageSize }); - return useQuery( - [FIND_LIST_ITEMS_QUERY_KEY, pageIndex, pageSize, sortField, sortOrder, listId, filter], - async ({ signal }) => { - const response = await findListItemsWithOptionalSignal({ - http, - signal, - pageIndex, - pageSize, - sortField, - sortOrder, - listId, - cursor, - filter, - }); - return response; - }, - { - keepPreviousData: true, - refetchOnWindowFocus: false, - retry: false, - onSuccess: (data) => { - if (data?.cursor) { - setCursor(data?.cursor); - } - }, - } - ); -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.test.ts deleted file mode 100644 index fcce54ac3be3..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.test.ts +++ /dev/null @@ -1,39 +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 { waitFor, renderHook, act } from '@testing-library/react'; - -import { useFindLists } from '.'; -import { httpServiceMock } from '@kbn/core-http-browser-mocks'; -import * as Api from '@kbn/securitysolution-list-api'; - -import { getFoundListSchemaMock } from '../mocks/response/found_list_schema.mock'; - -jest.mock('@kbn/securitysolution-list-api'); - -describe('useFindLists', () => { - let httpMock: ReturnType<typeof httpServiceMock.createStartContract>; - - beforeEach(() => { - httpMock = httpServiceMock.createStartContract(); - (Api.findLists as jest.Mock).mockResolvedValue(getFoundListSchemaMock()); - }); - - it('invokes Api.findLists', async () => { - const { result } = renderHook(() => useFindLists()); - act(() => { - result.current.start({ http: httpMock, pageIndex: 1, pageSize: 10 }); - }); - await waitFor(() => - expect(Api.findLists).toHaveBeenCalledWith( - expect.objectContaining({ http: httpMock, pageIndex: 1, pageSize: 10 }) - ) - ); - }); -}); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.ts deleted file mode 100644 index a33e3ba9f860..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.ts +++ /dev/null @@ -1,15 +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 { findLists } from '@kbn/securitysolution-list-api'; -import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; - -const findListsWithOptionalSignal = withOptionalSignal(findLists); - -export const useFindLists = () => useAsync(findListsWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_find_lists_by_size/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_find_lists_by_size/index.ts deleted file mode 100644 index 25027ee3901d..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_find_lists_by_size/index.ts +++ /dev/null @@ -1,15 +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 { findListsBySize } from '@kbn/securitysolution-list-api'; -import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; - -const findListsBySizeWithOptionalSignal = withOptionalSignal(findListsBySize); - -export const useFindListsBySize = () => useAsync(findListsBySizeWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_get_list_by_id/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_get_list_by_id/index.ts deleted file mode 100644 index 030fbcfae829..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_get_list_by_id/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { useQuery } from '@tanstack/react-query'; -import { getListById, ApiParams } from '@kbn/securitysolution-list-api'; -import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; - -const getListByIdWithOptionalSignal = withOptionalSignal(getListById); - -const GET_LIST_BY_ID_QUERY_KEY = 'GET_LIST_BY_ID'; -export const useGetListById = ({ http, id }: { http: ApiParams['http']; id: string }) => { - return useQuery( - [GET_LIST_BY_ID_QUERY_KEY, id], - async ({ signal }) => { - const respone = await getListByIdWithOptionalSignal({ http, signal, id }); - return respone; - }, - { - refetchOnWindowFocus: false, - } - ); -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts deleted file mode 100644 index 1bbccd144801..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts +++ /dev/null @@ -1,15 +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 { importList } from '@kbn/securitysolution-list-api'; -import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; - -const importListWithOptionalSignal = withOptionalSignal(importList); - -export const useImportList = () => useAsync(importListWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_patch_list_item/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_patch_list_item/index.ts deleted file mode 100644 index cdae3015cff8..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_patch_list_item/index.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", 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 { UseMutationOptions } from '@tanstack/react-query'; -import type { ListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { useMutation } from '@tanstack/react-query'; -import type { PatchListItemParams } from '@kbn/securitysolution-list-api'; -import { patchListItem } from '@kbn/securitysolution-list-api'; -import type { IHttpFetchError } from '@kbn/core-http-browser'; -import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; -import { useInvalidateListItemQuery } from '../use_find_list_items'; - -const patchListItemWithOptionalSignal = withOptionalSignal(patchListItem); - -export const PATCH_LIST_ITEM_MUTATION_KEY = ['PATCH', 'LIST_ITEM_MUTATION']; -type PatchListMutationParams = Omit<PatchListItemParams, 'refresh' | 'signal'>; - -export const usePatchListItemMutation = ( - options?: UseMutationOptions<ListItemSchema, IHttpFetchError<Error>, PatchListMutationParams> -) => { - const invalidateListItemQuery = useInvalidateListItemQuery(); - return useMutation<ListItemSchema, IHttpFetchError<Error>, PatchListMutationParams>( - ({ id, value, _version, http }: PatchListMutationParams) => - patchListItemWithOptionalSignal({ id, value, http, refresh: 'true', _version }), - { - ...options, - mutationKey: PATCH_LIST_ITEM_MUTATION_KEY, - onSettled: (...args) => { - invalidateListItemQuery(); - if (options?.onSettled) { - options.onSettled(...args); - } - }, - } - ); -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.ts deleted file mode 100644 index 79f8748345fd..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.ts +++ /dev/null @@ -1,111 +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 { Dispatch, useEffect, useRef, useState } from 'react'; -import type { - CreateExceptionListItemSchema, - PersistHookProps, - UpdateExceptionListItemSchema, -} from '@kbn/securitysolution-io-ts-list-types'; -import { addExceptionListItem, updateExceptionListItem } from '@kbn/securitysolution-list-api'; - -import { transformNewItemOutput, transformOutput } from '../transforms'; - -interface PersistReturnExceptionItem { - isLoading: boolean; - isSaved: boolean; -} - -export type ReturnPersistExceptionItem = [ - PersistReturnExceptionItem, - Dispatch<CreateExceptionListItemSchema | UpdateExceptionListItemSchema | null> -]; - -// TODO: Add this to @kbn/securitysolution-list-hooks - -/** - * Hook for creating or updating ExceptionListItem - * - * @param http Kibana http service - * @param onError error callback - * - */ -export const usePersistExceptionItem = ({ - http, - onError, -}: PersistHookProps): ReturnPersistExceptionItem => { - const [exceptionListItem, setExceptionItem] = useState< - CreateExceptionListItemSchema | UpdateExceptionListItemSchema | null - >(null); - const [isSaved, setIsSaved] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const isUpdateExceptionItem = (item: unknown): item is UpdateExceptionListItemSchema => - Boolean(item && (item as UpdateExceptionListItemSchema).id != null); - const isSubscribed = useRef(false); - - useEffect(() => { - let abortCtrl: AbortController | null = null; - isSubscribed.current = true; - setIsSaved(false); - - const saveExceptionItem = async (): Promise<void> => { - if (exceptionListItem === null) { - return; - } - - try { - abortCtrl = new AbortController(); - setIsLoading(true); - - if (isUpdateExceptionItem(exceptionListItem)) { - // Please see `x-pack/plugins/lists/public/exceptions/transforms.ts` doc notes - // for context around the temporary `id` - const transformedList = transformOutput(exceptionListItem); - - await updateExceptionListItem({ - http, - listItem: transformedList, - signal: abortCtrl.signal, - }); - } else { - // Please see `x-pack/plugins/lists/public/exceptions/transforms.ts` doc notes - // for context around the temporary `id` - const transformedList = transformNewItemOutput(exceptionListItem); - - await addExceptionListItem({ - http, - listItem: transformedList, - signal: abortCtrl.signal, - }); - } - - if (isSubscribed.current) { - setIsSaved(true); - } - } catch (error) { - if (isSubscribed.current) { - onError(error); - } - } finally { - if (isSubscribed.current) { - setIsLoading(false); - } - } - }; - - saveExceptionItem(); - - return (): void => { - isSubscribed.current = false; - abortCtrl?.abort(); - }; - }, [http, exceptionListItem, onError]); - - return [{ isLoading, isSaved }, setExceptionItem]; -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.ts deleted file mode 100644 index 8648516d008c..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.ts +++ /dev/null @@ -1,81 +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 { Dispatch, useEffect, useState } from 'react'; -import type { - AddExceptionList, - PersistHookProps, - UpdateExceptionListSchema, -} from '@kbn/securitysolution-io-ts-list-types'; -import { addExceptionList, updateExceptionList } from '@kbn/securitysolution-list-api'; - -interface PersistReturnExceptionList { - isLoading: boolean; - isSaved: boolean; -} - -export type ReturnPersistExceptionList = [ - PersistReturnExceptionList, - Dispatch<AddExceptionList | null> -]; - -/** - * Hook for creating or updating ExceptionList - * - * @param http Kibana http service - * @param onError error callback - * - */ -export const usePersistExceptionList = ({ - http, - onError, -}: PersistHookProps): ReturnPersistExceptionList => { - const [exceptionList, setExceptionList] = useState<AddExceptionList | null>(null); - const [isSaved, setIsSaved] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const isUpdateExceptionList = (item: unknown): item is UpdateExceptionListSchema => - Boolean(item && (item as UpdateExceptionListSchema).id != null); - - useEffect(() => { - let isSubscribed = true; - const abortCtrl = new AbortController(); - setIsSaved(false); - - const saveExceptionList = async (): Promise<void> => { - if (exceptionList != null) { - try { - setIsLoading(true); - if (isUpdateExceptionList(exceptionList)) { - await updateExceptionList({ http, list: exceptionList, signal: abortCtrl.signal }); - } else { - await addExceptionList({ http, list: exceptionList, signal: abortCtrl.signal }); - } - if (isSubscribed) { - setIsSaved(true); - } - } catch (error) { - if (isSubscribed) { - onError(error); - } - } - if (isSubscribed) { - setIsLoading(false); - } - } - }; - - saveExceptionList(); - return (): void => { - isSubscribed = false; - abortCtrl.abort(); - }; - }, [http, exceptionList, onError]); - - return [{ isLoading, isSaved }, setExceptionList]; -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.ts deleted file mode 100644 index 4853f0a7480a..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.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 { useQuery } from '@tanstack/react-query'; - -import { readListIndex, ApiParams } from '@kbn/securitysolution-list-api'; -import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; - -import { READ_INDEX_QUERY_KEY } from '../constants'; - -const readListIndexWithOptionalSignal = withOptionalSignal(readListIndex); - -export const useReadListIndex = ({ - http, - isEnabled, - onError, -}: { - isEnabled: boolean; - http: ApiParams['http']; - onError?: (err: unknown) => void; -}) => { - const query = useQuery( - READ_INDEX_QUERY_KEY, - async ({ signal }) => { - if (!isEnabled) { - return null; - } - - return readListIndexWithOptionalSignal({ http, signal }); - }, - { - onError, - retry: false, - refetchOnWindowFocus: false, - enabled: isEnabled, - staleTime: Infinity, - } - ); - - return { - result: query.data, - loading: query.isFetching, - error: query.error, - }; -}; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_read_list_privileges/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_read_list_privileges/index.ts deleted file mode 100644 index 42a055c16808..000000000000 --- a/packages/kbn-securitysolution-list-hooks/src/use_read_list_privileges/index.ts +++ /dev/null @@ -1,15 +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 { readListPrivileges } from '@kbn/securitysolution-list-api'; -import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; - -const readListPrivilegesWithOptionalSignal = withOptionalSignal(readListPrivileges); - -export const useReadListPrivileges = () => useAsync(readListPrivilegesWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/tsconfig.json b/packages/kbn-securitysolution-list-hooks/tsconfig.json deleted file mode 100644 index 53e105e60975..000000000000 --- a/packages/kbn-securitysolution-list-hooks/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts", - ], - "kbn_references": [ - "@kbn/securitysolution-hook-utils", - "@kbn/securitysolution-io-ts-list-types", - "@kbn/securitysolution-list-api", - "@kbn/securitysolution-list-utils", - "@kbn/securitysolution-utils", - "@kbn/core-http-browser-mocks", - "@kbn/core-http-browser", - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-list-utils/index.ts b/packages/kbn-securitysolution-list-utils/index.ts deleted file mode 100644 index c5999a044a18..000000000000 --- a/packages/kbn-securitysolution-list-utils/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './src/autocomplete_operators'; -export * from './src/get_exception_list_type'; -export * from './src/get_filters'; -export * from './src/get_general_filters'; -export * from './src/get_ids_and_namespaces'; -export * from './src/get_saved_object_type'; -export * from './src/get_saved_object_types'; -export * from './src/has_large_value_list'; -export * from './src/helpers'; -export * from './src/types'; diff --git a/packages/kbn-securitysolution-list-utils/jest.config.js b/packages/kbn-securitysolution-list-utils/jest.config.js deleted file mode 100644 index b24a4fa543f7..000000000000 --- a/packages/kbn-securitysolution-list-utils/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-list-utils'], -}; diff --git a/packages/kbn-securitysolution-list-utils/package.json b/packages/kbn-securitysolution-list-utils/package.json deleted file mode 100644 index e9ace6e30234..000000000000 --- a/packages/kbn-securitysolution-list-utils/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@kbn/securitysolution-list-utils", - "version": "1.0.0", - "description": "security solution list utilities", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "private": true, - "sideEffects": false -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts b/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts deleted file mode 100644 index b4b1eec6be34..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts +++ /dev/null @@ -1,160 +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 { i18n } from '@kbn/i18n'; -import { - ListOperatorEnum as OperatorEnum, - ListOperatorTypeEnum as OperatorTypeEnum, -} from '@kbn/securitysolution-io-ts-list-types'; -import { OperatorOption } from '../types'; - -export const isOperator: OperatorOption = { - message: i18n.translate('lists.exceptions.isOperatorLabel', { - defaultMessage: 'is', - }), - operator: OperatorEnum.INCLUDED, - type: OperatorTypeEnum.MATCH, - value: 'is', -}; - -export const isNotOperator: OperatorOption = { - message: i18n.translate('lists.exceptions.isNotOperatorLabel', { - defaultMessage: 'is not', - }), - operator: OperatorEnum.EXCLUDED, - type: OperatorTypeEnum.MATCH, - value: 'is_not', -}; - -export const isOneOfOperator: OperatorOption = { - message: i18n.translate('lists.exceptions.isOneOfOperatorLabel', { - defaultMessage: 'is one of', - }), - operator: OperatorEnum.INCLUDED, - type: OperatorTypeEnum.MATCH_ANY, - value: 'is_one_of', -}; - -export const isNotOneOfOperator: OperatorOption = { - message: i18n.translate('lists.exceptions.isNotOneOfOperatorLabel', { - defaultMessage: 'is not one of', - }), - operator: OperatorEnum.EXCLUDED, - type: OperatorTypeEnum.MATCH_ANY, - value: 'is_not_one_of', -}; - -export const existsOperator: OperatorOption = { - message: i18n.translate('lists.exceptions.existsOperatorLabel', { - defaultMessage: 'exists', - }), - operator: OperatorEnum.INCLUDED, - type: OperatorTypeEnum.EXISTS, - value: 'exists', -}; - -export const doesNotExistOperator: OperatorOption = { - message: i18n.translate('lists.exceptions.doesNotExistOperatorLabel', { - defaultMessage: 'does not exist', - }), - operator: OperatorEnum.EXCLUDED, - type: OperatorTypeEnum.EXISTS, - value: 'does_not_exist', -}; - -export const isInListOperator: OperatorOption = { - message: i18n.translate('lists.exceptions.isInListOperatorLabel', { - defaultMessage: 'is in list', - }), - operator: OperatorEnum.INCLUDED, - type: OperatorTypeEnum.LIST, - value: 'is_in_list', -}; - -export const isNotInListOperator: OperatorOption = { - message: i18n.translate('lists.exceptions.isNotInListOperatorLabel', { - defaultMessage: 'is not in list', - }), - operator: OperatorEnum.EXCLUDED, - type: OperatorTypeEnum.LIST, - value: 'is_not_in_list', -}; - -export const matchesOperator: OperatorOption = { - message: i18n.translate('lists.exceptions.matchesOperatorLabel', { - defaultMessage: 'matches', - }), - operator: OperatorEnum.INCLUDED, - type: OperatorTypeEnum.WILDCARD, - value: 'matches', -}; - -export const doesNotMatchOperator: OperatorOption = { - message: i18n.translate('lists.exceptions.doesNotMatchOperatorLabel', { - defaultMessage: 'does not match', - }), - operator: OperatorEnum.EXCLUDED, - type: OperatorTypeEnum.WILDCARD, - value: 'does_not_match', -}; - -export const EVENT_FILTERS_OPERATORS: OperatorOption[] = [ - isOperator, - isNotOperator, - isOneOfOperator, - isNotOneOfOperator, - matchesOperator, - doesNotMatchOperator, -]; - -/* - * !IMPORTANT! - Please only add to this list if it is an operator - * supported by the detection engine. - */ -export const DETECTION_ENGINE_EXCEPTION_OPERATORS: OperatorOption[] = [ - isOperator, - isNotOperator, - isOneOfOperator, - isNotOneOfOperator, - existsOperator, - doesNotExistOperator, - isInListOperator, - isNotInListOperator, - matchesOperator, - doesNotMatchOperator, -]; - -export const ALL_OPERATORS: OperatorOption[] = [ - isOperator, - isNotOperator, - isOneOfOperator, - isNotOneOfOperator, - existsOperator, - doesNotExistOperator, - isInListOperator, - isNotInListOperator, - matchesOperator, - doesNotMatchOperator, -]; - -export const EXCEPTION_OPERATORS_SANS_LISTS: OperatorOption[] = [ - isOperator, - isNotOperator, - isOneOfOperator, - isNotOneOfOperator, - existsOperator, - doesNotExistOperator, - matchesOperator, - doesNotMatchOperator, -]; - -export const EXCEPTION_OPERATORS_ONLY_LISTS: OperatorOption[] = [ - isInListOperator, - isNotInListOperator, -]; diff --git a/packages/kbn-securitysolution-list-utils/src/get_exception_list_type/index.ts b/packages/kbn-securitysolution-list-utils/src/get_exception_list_type/index.ts deleted file mode 100644 index 568436c487de..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/get_exception_list_type/index.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 type { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; - -import { exceptionListAgnosticSavedObjectType } from '../types'; - -export const getExceptionListType = ({ - savedObjectType, -}: { - savedObjectType: string; -}): NamespaceType => { - if (savedObjectType === exceptionListAgnosticSavedObjectType) { - return 'agnostic'; - } else { - return 'single'; - } -}; diff --git a/packages/kbn-securitysolution-list-utils/src/get_filters/index.test.ts b/packages/kbn-securitysolution-list-utils/src/get_filters/index.test.ts deleted file mode 100644 index 76b9eeda44f8..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/get_filters/index.test.ts +++ /dev/null @@ -1,209 +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 { getFilters } from '.'; - -describe('getFilters', () => { - describe('single', () => { - test('it properly formats when no filters and hide lists contains few list ids', () => { - const filter = getFilters({ - filters: {}, - namespaceTypes: ['single'], - hideLists: ['listId-1', 'listId-2', 'listId-3'], - }); - - expect(filter).toEqual( - '(not exception-list.attributes.list_id: listId-1*) AND (not exception-list.attributes.list_id: listId-2*) AND (not exception-list.attributes.list_id: listId-3*)' - ); - }); - test('it properly formats when no filters and hide lists contains one list id', () => { - const filter = getFilters({ - filters: {}, - namespaceTypes: ['single'], - hideLists: ['listId-1'], - }); - - expect(filter).toEqual('(not exception-list.attributes.list_id: listId-1*)'); - }); - test('it properly formats when no filters and no hide lists', () => { - const filter = getFilters({ - filters: {}, - namespaceTypes: ['single'], - hideLists: [], - }); - - expect(filter).toEqual(''); - }); - test('it properly formats when filters passed and hide lists contains few list ids', () => { - const filter = getFilters({ - filters: { created_by: 'moi', name: 'Sample' }, - namespaceTypes: ['single'], - hideLists: ['listId-1', 'listId-2', 'listId-3'], - }); - - expect(filter).toEqual( - '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample) AND (not exception-list.attributes.list_id: listId-1*) AND (not exception-list.attributes.list_id: listId-2*) AND (not exception-list.attributes.list_id: listId-3*)' - ); - }); - test('it properly formats when filters passed and hide lists contains one list id', () => { - const filter = getFilters({ - filters: { created_by: 'moi', name: 'Sample' }, - namespaceTypes: ['single'], - hideLists: ['listId-1'], - }); - - expect(filter).toEqual( - '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample) AND (not exception-list.attributes.list_id: listId-1*)' - ); - }); - test('it properly formats when filters passed and no hide lists', () => { - const filter = getFilters({ - filters: { created_by: 'moi', name: 'Sample' }, - namespaceTypes: ['single'], - hideLists: [], - }); - - expect(filter).toEqual( - '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample)' - ); - }); - }); - - describe('agnostic', () => { - test('it properly formats when no filters and hide lists contains few list ids', () => { - const filter = getFilters({ - filters: {}, - namespaceTypes: ['agnostic'], - hideLists: ['listId-1', 'listId-2', 'listId-3'], - }); - - expect(filter).toEqual( - '(not exception-list-agnostic.attributes.list_id: listId-1*) AND (not exception-list-agnostic.attributes.list_id: listId-2*) AND (not exception-list-agnostic.attributes.list_id: listId-3*)' - ); - }); - test('it properly formats when no filters and hide lists contains one list id', () => { - const filter = getFilters({ - filters: {}, - namespaceTypes: ['agnostic'], - hideLists: ['listId-1'], - }); - - expect(filter).toEqual('(not exception-list-agnostic.attributes.list_id: listId-1*)'); - }); - test('it properly formats when no filters and no hide lists', () => { - const filter = getFilters({ - filters: {}, - namespaceTypes: ['agnostic'], - hideLists: [], - }); - - expect(filter).toEqual(''); - }); - test('it properly formats when filters passed and hide lists contains few list ids', () => { - const filter = getFilters({ - filters: { created_by: 'moi', name: 'Sample' }, - namespaceTypes: ['agnostic'], - hideLists: ['listId-1', 'listId-2', 'listId-3'], - }); - - expect(filter).toEqual( - '(exception-list-agnostic.attributes.created_by:moi) AND (exception-list-agnostic.attributes.name.text:Sample) AND (not exception-list-agnostic.attributes.list_id: listId-1*) AND (not exception-list-agnostic.attributes.list_id: listId-2*) AND (not exception-list-agnostic.attributes.list_id: listId-3*)' - ); - }); - test('it properly formats when filters passed and hide lists contains one list id', () => { - const filter = getFilters({ - filters: { created_by: 'moi', name: 'Sample' }, - namespaceTypes: ['agnostic'], - hideLists: ['listId-1'], - }); - - expect(filter).toEqual( - '(exception-list-agnostic.attributes.created_by:moi) AND (exception-list-agnostic.attributes.name.text:Sample) AND (not exception-list-agnostic.attributes.list_id: listId-1*)' - ); - }); - test('it properly formats when filters passed and no hide lists', () => { - const filter = getFilters({ - filters: { created_by: 'moi', name: 'Sample' }, - namespaceTypes: ['agnostic'], - hideLists: [], - }); - - expect(filter).toEqual( - '(exception-list-agnostic.attributes.created_by:moi) AND (exception-list-agnostic.attributes.name.text:Sample)' - ); - }); - }); - - describe('single, agnostic', () => { - test('it properly formats when no filters and hide lists contains few list ids', () => { - const filter = getFilters({ - filters: {}, - namespaceTypes: ['single', 'agnostic'], - hideLists: ['listId-1', 'listId-2', 'listId-3'], - }); - - expect(filter).toEqual( - '(not exception-list.attributes.list_id: listId-1* AND not exception-list-agnostic.attributes.list_id: listId-1*) AND (not exception-list.attributes.list_id: listId-2* AND not exception-list-agnostic.attributes.list_id: listId-2*) AND (not exception-list.attributes.list_id: listId-3* AND not exception-list-agnostic.attributes.list_id: listId-3*)' - ); - }); - test('it properly formats when no filters and hide lists contains one list id', () => { - const filter = getFilters({ - filters: {}, - namespaceTypes: ['single', 'agnostic'], - hideLists: ['listId-1'], - }); - - expect(filter).toEqual( - '(not exception-list.attributes.list_id: listId-1* AND not exception-list-agnostic.attributes.list_id: listId-1*)' - ); - }); - test('it properly formats when no filters and no hide lists', () => { - const filter = getFilters({ - filters: {}, - namespaceTypes: ['single', 'agnostic'], - hideLists: [], - }); - - expect(filter).toEqual(''); - }); - test('it properly formats when filters passed and hide lists contains few list ids', () => { - const filter = getFilters({ - filters: { created_by: 'moi', name: 'Sample' }, - namespaceTypes: ['single', 'agnostic'], - hideLists: ['listId-1', 'listId-2', 'listId-3'], - }); - - expect(filter).toEqual( - '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample) AND (not exception-list.attributes.list_id: listId-1* AND not exception-list-agnostic.attributes.list_id: listId-1*) AND (not exception-list.attributes.list_id: listId-2* AND not exception-list-agnostic.attributes.list_id: listId-2*) AND (not exception-list.attributes.list_id: listId-3* AND not exception-list-agnostic.attributes.list_id: listId-3*)' - ); - }); - test('it properly formats when filters passed and hide lists contains one list id', () => { - const filter = getFilters({ - filters: { created_by: 'moi', name: 'Sample' }, - namespaceTypes: ['single', 'agnostic'], - hideLists: ['listId-1'], - }); - - expect(filter).toEqual( - '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample) AND (not exception-list.attributes.list_id: listId-1* AND not exception-list-agnostic.attributes.list_id: listId-1*)' - ); - }); - test('it properly formats when filters passed and no hide lists', () => { - const filter = getFilters({ - filters: { created_by: 'moi', name: 'Sample' }, - namespaceTypes: ['single', 'agnostic'], - hideLists: [], - }); - - expect(filter).toEqual( - '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample)' - ); - }); - }); -}); diff --git a/packages/kbn-securitysolution-list-utils/src/get_filters/index.ts b/packages/kbn-securitysolution-list-utils/src/get_filters/index.ts deleted file mode 100644 index 6ca3119855b3..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/get_filters/index.ts +++ /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 { ExceptionListFilter, NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; -import { getGeneralFilters } from '../get_general_filters'; -import { getSavedObjectTypes } from '../get_saved_object_types'; -export interface GetFiltersParams { - filters: ExceptionListFilter; - namespaceTypes: NamespaceType[]; - hideLists: readonly string[]; -} - -export const getFilters = ({ filters, namespaceTypes, hideLists }: GetFiltersParams): string => { - const namespaces = getSavedObjectTypes({ namespaceType: namespaceTypes }); - const generalFilters = getGeneralFilters(filters, namespaces); - const hideListsFilters = hideLists.map((listId) => { - const filtersByNamespace = namespaces.map((namespace) => { - return `not ${namespace}.attributes.list_id: ${listId}*`; - }); - return `(${filtersByNamespace.join(' AND ')})`; - }); - - return [generalFilters, ...hideListsFilters] - .filter((filter) => filter.trim() !== '') - .join(' AND '); -}; diff --git a/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.test.ts b/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.test.ts deleted file mode 100644 index b349c57ab386..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.test.ts +++ /dev/null @@ -1,53 +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 { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import { getGeneralFilters } from '.'; - -describe('getGeneralFilters', () => { - test('it returns empty string if no filters', () => { - const filters = getGeneralFilters({}, ['exception-list']); - - expect(filters).toEqual(''); - }); - - test('it properly formats filters when one namespace type passed in', () => { - const filters = getGeneralFilters({ created_by: 'moi', name: 'Sample' }, ['exception-list']); - - expect(filters).toEqual( - '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample)' - ); - }); - - test('it properly formats filters when two namespace types passed in', () => { - const filters = getGeneralFilters({ created_by: 'moi', name: 'Sample' }, [ - 'exception-list', - 'exception-list-agnostic', - ]); - - expect(filters).toEqual( - '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample)' - ); - }); - - test('it properly formats filters when two types are passed in', () => { - const filters = getGeneralFilters( - { - created_by: 'moi', - name: 'Sample', - types: [ExceptionListTypeEnum.DETECTION, ExceptionListTypeEnum.RULE_DEFAULT], - }, - ['exception-list', 'exception-list-agnostic'] - ); - - expect(filters).toEqual( - '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample) AND (exception-list.attributes.type:detection OR exception-list.attributes.type:rule_default OR exception-list-agnostic.attributes.type:detection OR exception-list-agnostic.attributes.type:rule_default)' - ); - }); -}); diff --git a/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.ts b/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.ts deleted file mode 100644 index cc9b46728cbc..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.ts +++ /dev/null @@ -1,37 +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 { ExceptionListFilter } from '@kbn/securitysolution-io-ts-list-types'; -import { isArray } from 'lodash'; -import { get } from 'lodash/fp'; -import { SavedObjectType } from '../types'; - -export const getGeneralFilters = ( - filters: ExceptionListFilter, - namespaceTypes: SavedObjectType[] -): string => { - return Object.keys(filters) - .map((filterKey) => { - const value = get(filterKey, filters); - if (isArray(value) || (value != null && value.trim() !== '')) { - const filtersByNamespace = namespaceTypes - .map((namespace) => { - const fieldToSearch = - filterKey === 'name' ? 'name.text' : filterKey === 'types' ? 'type' : filterKey; - return isArray(value) - ? value.map((val) => `${namespace}.attributes.${fieldToSearch}:${val}`).join(' OR ') - : `${namespace}.attributes.${fieldToSearch}:${value}`; - }) - .join(' OR '); - return `(${filtersByNamespace})`; - } else return null; - }) - .filter((item) => item != null) - .join(' AND '); -}; diff --git a/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.test.ts b/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.test.ts deleted file mode 100644 index df5bedc2d2b4..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.test.ts +++ /dev/null @@ -1,106 +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 { getIdsAndNamespaces } from '.'; - -describe('getIdsAndNamespaces', () => { - test('it returns empty arrays if no lists found', async () => { - const output = getIdsAndNamespaces({ - lists: [], - showDetection: false, - showEndpoint: false, - }); - - expect(output).toEqual({ ids: [], namespaces: [] }); - }); - - test('it returns all lists if "showDetection" and "showEndpoint" are "false"', async () => { - const output = getIdsAndNamespaces({ - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - { - id: 'myListIdEndpoint', - listId: 'list_id_endpoint', - namespaceType: 'agnostic', - type: 'endpoint', - }, - ], - showDetection: false, - showEndpoint: false, - }); - - expect(output).toEqual({ - ids: ['list_id', 'list_id_endpoint'], - namespaces: ['single', 'agnostic'], - }); - }); - - test('it returns only detections lists if "showDetection" is "true"', async () => { - const output = getIdsAndNamespaces({ - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - { - id: 'myListIdEndpoint', - listId: 'list_id_endpoint', - namespaceType: 'agnostic', - type: 'endpoint', - }, - ], - showDetection: true, - showEndpoint: false, - }); - - expect(output).toEqual({ - ids: ['list_id'], - namespaces: ['single'], - }); - }); - - test('it returns only endpoint lists if "showEndpoint" is "true"', async () => { - const output = getIdsAndNamespaces({ - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - { - id: 'myListIdEndpoint', - listId: 'list_id_endpoint', - namespaceType: 'agnostic', - type: 'endpoint', - }, - ], - showDetection: false, - showEndpoint: true, - }); - - expect(output).toEqual({ - ids: ['list_id_endpoint'], - namespaces: ['agnostic'], - }); - }); - - test('it returns only detection lists if both "showEndpoint" and "showDetection" are "true"', async () => { - const output = getIdsAndNamespaces({ - lists: [ - { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, - { - id: 'myListIdEndpoint', - listId: 'list_id_endpoint', - namespaceType: 'agnostic', - type: 'endpoint', - }, - ], - showDetection: true, - showEndpoint: true, - }); - - expect(output).toEqual({ - ids: ['list_id'], - namespaces: ['single'], - }); - }); -}); diff --git a/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.ts b/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.ts deleted file mode 100644 index bc18dff7d33e..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.ts +++ /dev/null @@ -1,37 +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 { ExceptionListIdentifiers, NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; - -export const getIdsAndNamespaces = ({ - lists, - showDetection, - showEndpoint, -}: { - lists: ExceptionListIdentifiers[]; - showDetection: boolean; - showEndpoint: boolean; -}): { ids: string[]; namespaces: NamespaceType[] } => - lists - .filter((list) => { - if (showDetection) { - return list.type === 'detection'; - } else if (showEndpoint) { - return list.type === 'endpoint'; - } else { - return true; - } - }) - .reduce<{ ids: string[]; namespaces: NamespaceType[] }>( - (acc, { listId, namespaceType }) => ({ - ids: [...acc.ids, listId], - namespaces: [...acc.namespaces, namespaceType], - }), - { ids: [], namespaces: [] } - ); diff --git a/packages/kbn-securitysolution-list-utils/src/get_saved_object_type/index.ts b/packages/kbn-securitysolution-list-utils/src/get_saved_object_type/index.ts deleted file mode 100644 index 3e6cd312cb96..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/get_saved_object_type/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; - -import { - exceptionListAgnosticSavedObjectType, - exceptionListSavedObjectType, - SavedObjectType, -} from '../types'; - -export const getSavedObjectType = ({ - namespaceType, -}: { - namespaceType: NamespaceType; -}): SavedObjectType => { - if (namespaceType === 'agnostic') { - return exceptionListAgnosticSavedObjectType; - } else { - return exceptionListSavedObjectType; - } -}; diff --git a/packages/kbn-securitysolution-list-utils/src/get_saved_object_types/index.ts b/packages/kbn-securitysolution-list-utils/src/get_saved_object_types/index.ts deleted file mode 100644 index ec09fc25bd77..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/get_saved_object_types/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 { NamespaceTypeArray } from '@kbn/securitysolution-io-ts-list-types'; - -import { SavedObjectType } from '../types'; -import { getSavedObjectType } from '../get_saved_object_type'; - -export const getSavedObjectTypes = ({ - namespaceType, -}: { - namespaceType: NamespaceTypeArray; -}): SavedObjectType[] => { - return namespaceType.map((singleNamespaceType) => - getSavedObjectType({ namespaceType: singleNamespaceType }) - ); -}; diff --git a/packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.ts b/packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.ts deleted file mode 100644 index ef774b5c8cf1..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.ts +++ /dev/null @@ -1,15 +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 { EntriesArray } from '@kbn/securitysolution-io-ts-list-types'; - -export const hasLargeValueList = (entries: EntriesArray): boolean => { - const found = entries.filter(({ type }) => type === 'list'); - return found.length > 0; -}; diff --git a/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts b/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts deleted file mode 100644 index fffcabbf4157..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts +++ /dev/null @@ -1,377 +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 { - getMappingConflictsInfo, - fieldSupportsMatches, - hasWrongOperatorWithWildcard, - hasPartialCodeSignatureEntry, -} from '.'; - -describe('Helpers', () => { - describe('getMappingConflictsInfo', () => { - test('it return null if there are not conflicts', () => { - const field = { - name: 'field1', - type: 'string', - }; - const conflictsInfo = getMappingConflictsInfo(field); - - expect(conflictsInfo).toBeNull(); - }); - test('it groups ".ds-" data stream indices', () => { - const field = { - name: 'field1', - type: 'conflict', - conflictDescriptions: { - text: [ - '.ds-logs-default-2023.01.18-000001', - '.ds-logs-default-2023.01.18-000002', - '.ds-logs-tortilla.process-default-2022.11.20-000011', - '.ds-logs-tortilla.process-default-2022.11.20-000012', - '.ds-logs-tortilla.process-default-2022.11.20-000016', - ], - long: [ - '.ds-logs-default-2023.01.18-000004', - '.ds-logs-default-2023.01.18-000005', - 'partial-.ds-logs-gcp.audit-2021.12.22-000240', - 'partial-.ds-logs-gcp.audit-2021.12.22-000242', - ], - }, - }; - const conflictsInfo = getMappingConflictsInfo(field); - - expect(conflictsInfo).toEqual([ - { - type: 'text', - totalIndexCount: 5, - groupedIndices: [ - { name: 'logs-tortilla.process-default', count: 3 }, - { name: 'logs-default', count: 2 }, - ], - }, - { - type: 'long', - totalIndexCount: 4, - groupedIndices: [ - { name: 'logs-default', count: 2 }, - { name: 'logs-gcp.audit', count: 2 }, - ], - }, - ]); - }); - test('it groups old ".siem-" indices', () => { - const field = { - name: 'field1', - type: 'conflict', - conflictDescriptions: { - text: [ - '.siem-signals-default-000001', - '.siem-signals-default-000002', - '.siem-signals-default-000011', - '.siem-signals-default-000012', - ], - unmapped: [ - '.siem-signals-default-000004', - '.siem-signals-default-000005', - '.siem-signals-default-000240', - ], - }, - }; - const conflictsInfo = getMappingConflictsInfo(field); - - expect(conflictsInfo).toEqual([ - { - type: 'text', - totalIndexCount: 4, - groupedIndices: [{ name: '.siem-signals-default', count: 4 }], - }, - { - type: 'unmapped', - totalIndexCount: 3, - groupedIndices: [{ name: '.siem-signals-default', count: 3 }], - }, - ]); - }); - test('it groups mixed indices', () => { - const field = { - name: 'field1', - type: 'conflict', - conflictDescriptions: { - boolean: [ - '.ds-logs-default-2023.01.18-000001', - '.ds-logs-tortilla.process-default-2022.11.20-000011', - '.ds-logs-tortilla.process-default-2022.11.20-000012', - '.ds-logs-tortilla.process-default-2022.11.20-000016', - '.siem-signals-default-000001', - '.siem-signals-default-000002', - '.siem-signals-default-000012', - 'my-own-index-1', - 'my-own-index-2', - ], - unmapped: [ - '.siem-signals-default-000004', - 'partial-.ds-logs-gcp.audit-2021.12.22-000240', - 'partial-.ds-logs-gcp.audit-2021.12.22-000242', - 'my-own-index-3', - ], - }, - }; - const conflictsInfo = getMappingConflictsInfo(field); - - expect(conflictsInfo).toEqual([ - { - type: 'boolean', - totalIndexCount: 9, - groupedIndices: [ - { name: 'logs-tortilla.process-default', count: 3 }, - { name: '.siem-signals-default', count: 3 }, - { name: 'logs-default', count: 1 }, - { name: 'my-own-index-1', count: 1 }, - { name: 'my-own-index-2', count: 1 }, - ], - }, - { - type: 'unmapped', - totalIndexCount: 4, - groupedIndices: [ - { name: 'logs-gcp.audit', count: 2 }, - { name: '.siem-signals-default', count: 1 }, - { name: 'my-own-index-3', count: 1 }, - ], - }, - ]); - }); - }); - - describe('fieldSupportsMatches', () => { - test('it returns true if esTypes is keyword', () => { - expect( - fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword'] }) - ).toBeTruthy(); - }); - - test('it returns true if one of the esTypes is kibana type string and another is not', () => { - expect( - fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword', 'object'] }) - ).toBeTruthy(); - }); - - test('it returns true if one of the esTypes is keyword', () => { - expect( - fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword', 'unmapped'] }) - ).toBeTruthy(); - }); - - test('it returns true if one of the esTypes is text', () => { - expect( - fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['text', 'unmapped'] }) - ).toBeTruthy(); - }); - - test('it returns true if all of the esTypes is map to kibana type string', () => { - expect( - fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['text', 'keyword'] }) - ).toBeTruthy(); - }); - - test('it returns false if none of the esTypes map to kibana type string', () => { - expect( - fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['bool', 'unmapped'] }) - ).toBeFalsy(); - }); - }); - describe('hasWrongOperatorWithWildcard', () => { - test('it returns true if there is at least one exception entry with a wildcard and the wrong operator', () => { - expect( - hasWrongOperatorWithWildcard([ - { - description: '', - name: '', - type: 'simple', - entries: [{ type: 'match', value: 'withwildcard*', field: '', operator: 'included' }], - }, - ]) - ).toBeTruthy(); - expect( - hasWrongOperatorWithWildcard([ - { - description: '', - name: '', - type: 'simple', - entries: [{ type: 'match', value: 'withwildcard?', field: '', operator: 'included' }], - }, - ]) - ).toBeTruthy(); - }); - - test('it returns true if there are entries joined with an OR that have a wildcard and the wrong operator', () => { - expect( - hasWrongOperatorWithWildcard([ - { - description: '', - name: '', - type: 'simple', - entries: [{ type: 'match', value: 'withwildcard?', field: '', operator: 'included' }], - }, - { - description: '', - name: '', - type: 'simple', - entries: [{ type: 'match', value: 'withwildcard?*', field: '', operator: 'included' }], - }, - ]) - ).toBeTruthy(); - }); - - test('it returns false if there are no exception entries with a wildcard and the wrong operator', () => { - expect( - hasWrongOperatorWithWildcard([ - { - description: '', - name: '', - type: 'simple', - entries: [ - { type: 'match', value: 'nowildcard', field: '', operator: 'excluded' }, - { type: 'wildcard', value: 'withwildcard*?', field: '', operator: 'included' }, - ], - }, - ]) - ).toBeFalsy(); - }); - - test('it returns true if there are nested entries with a wildcard and the wrong operator', () => { - expect( - hasWrongOperatorWithWildcard([ - { - description: '', - name: '', - type: 'simple', - entries: [ - { type: 'match', value: 'nowildcard', field: '', operator: 'excluded' }, - { - field: '', - type: 'nested', - entries: [{ type: 'match', value: 'wildcard?', field: '', operator: 'excluded' }], - }, - ], - }, - ]) - ).toBeTruthy(); - }); - }); - - describe('hasPartialCodeSignatureEntry', () => { - it('returns false if the entry has neither code signature subject name nor trusted field', () => { - expect( - hasPartialCodeSignatureEntry([ - { - description: '', - name: '', - type: 'simple', - os_types: ['windows'], - entries: [{ type: 'match', value: 'asdf', field: 'someField', operator: 'excluded' }], - }, - ]) - ).toBeFalsy(); - }); - it('returns true if the entry has code signature subject name but not trusted field', () => { - expect( - hasPartialCodeSignatureEntry([ - { - description: '', - name: '', - type: 'simple', - os_types: ['windows'], - entries: [ - { - type: 'match', - value: 'asdf', - field: 'process.code_signature.subject_name', - operator: 'excluded', - }, - ], - }, - ]) - ).toBeTruthy(); - }); - it('returns true if the entry has code signature trusted but not the subject name field', () => { - expect( - hasPartialCodeSignatureEntry([ - { - description: '', - name: '', - type: 'simple', - os_types: ['windows'], - entries: [ - { - type: 'match', - value: 'asdf', - field: 'process.code_signature.trusted', - operator: 'excluded', - }, - ], - }, - ]) - ).toBeTruthy(); - }); - it('returns false if the entry has both code signature subject name and trusted field', () => { - expect( - hasPartialCodeSignatureEntry([ - { - description: '', - name: '', - type: 'simple', - os_types: ['windows'], - entries: [ - { - type: 'match', - value: 'asdf', - field: 'process.code_signature.subject_name', - operator: 'excluded', - }, - { - type: 'match', - value: 'true', - field: 'process.code_signature.trusted', - operator: 'excluded', - }, - ], - }, - ]) - ).toBeFalsy(); - }); - it('returns false if the entry has both code signature team_id and trusted fields for mac os', () => { - expect( - hasPartialCodeSignatureEntry([ - { - description: '', - name: '', - type: 'simple', - os_types: ['macos'], - entries: [ - { - type: 'match', - value: 'asdf', - field: 'process.code_signature.team_id', - operator: 'excluded', - }, - { - type: 'match', - value: 'true', - field: 'process.code_signature.trusted', - operator: 'excluded', - }, - ], - }, - ]) - ).toBeFalsy(); - }); - }); -}); diff --git a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts deleted file mode 100644 index 8031cfba14f2..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts +++ /dev/null @@ -1,1089 +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 { v4 as uuidv4 } from 'uuid'; -import { - addIdToItem, - removeIdFromItem, - validateHasWildcardWithWrongOperator, -} from '@kbn/securitysolution-utils'; -import { validate } from '@kbn/securitysolution-io-ts-utils'; -import { - CreateExceptionListItemSchema, - EntriesArray, - Entry, - EntryNested, - ExceptionListType, - ListSchema, - NamespaceType, - ListOperatorEnum as OperatorEnum, - ListOperatorTypeEnum as OperatorTypeEnum, - createExceptionListItemSchema, - entriesList, - entriesNested, - entry, - exceptionListItemSchema, - nestedEntryItem, - CreateRuleExceptionListItemSchema, - createRuleExceptionListItemSchema, -} from '@kbn/securitysolution-io-ts-list-types'; -import { - DataViewBase, - DataViewFieldBase, - getDataViewFieldSubtypeNested, - isDataViewFieldSubtypeNested, -} from '@kbn/es-query'; -import { castEsToKbnFieldTypeName, KBN_FIELD_TYPES } from '@kbn/field-types'; - -import { - ALL_OPERATORS, - EXCEPTION_OPERATORS_SANS_LISTS, - doesNotExistOperator, - existsOperator, - isNotOperator, - isOneOfOperator, - isOperator, - DETECTION_ENGINE_EXCEPTION_OPERATORS, - isNotOneOfOperator, - isInListOperator, - isNotInListOperator, - matchesOperator, - doesNotMatchOperator, -} from '../autocomplete_operators'; - -import { - BuilderEntry, - CreateExceptionListItemBuilderSchema, - DataViewField, - EmptyEntry, - EmptyNestedEntry, - ExceptionsBuilderExceptionItem, - ExceptionsBuilderReturnExceptionItem, - FormattedBuilderEntry, - OperatorOption, - SavedObjectType, -} from '../types'; - -export const isEntryNested = (item: BuilderEntry): item is EntryNested => { - return (item as EntryNested).entries != null; -}; - -export const filterExceptionItems = ( - exceptions: ExceptionsBuilderExceptionItem[] -): ExceptionsBuilderReturnExceptionItem[] => { - return exceptions.reduce<ExceptionsBuilderReturnExceptionItem[]>((acc, exception) => { - const entries = exception.entries.reduce<BuilderEntry[]>((nestedAcc, singleEntry) => { - const strippedSingleEntry = removeIdFromItem(singleEntry); - if (entriesNested.is(strippedSingleEntry)) { - const nestedEntriesArray = strippedSingleEntry.entries.filter((singleNestedEntry) => { - const noIdSingleNestedEntry = removeIdFromItem(singleNestedEntry); - const [validatedNestedEntry] = validate(noIdSingleNestedEntry, nestedEntryItem); - return validatedNestedEntry != null; - }); - const noIdNestedEntries = nestedEntriesArray.map((singleNestedEntry) => - removeIdFromItem(singleNestedEntry) - ); - - const [validatedNestedEntry] = validate( - { ...strippedSingleEntry, entries: noIdNestedEntries }, - entriesNested - ); - - if (validatedNestedEntry != null) { - return [...nestedAcc, { ...singleEntry, entries: nestedEntriesArray }]; - } - return nestedAcc; - } else { - const [validatedEntry] = validate(strippedSingleEntry, entry); - if (validatedEntry != null) { - return [...nestedAcc, singleEntry]; - } - return nestedAcc; - } - }, []); - - if (entries.length === 0) { - return acc; - } - - const item = { ...exception, entries }; - - if (exceptionListItemSchema.is(item)) { - return [...acc, item]; - } else if ( - createExceptionListItemSchema.is(item) || - createRuleExceptionListItemSchema.is(item) - ) { - const { meta, ...rest } = item; - const itemSansMetaId: CreateExceptionListItemSchema | CreateRuleExceptionListItemSchema = { - ...rest, - meta: undefined, - }; - return [...acc, itemSansMetaId]; - } else { - return acc; - } - }, []); -}; - -export const addIdToEntries = (entries: EntriesArray): EntriesArray => { - return entries.map((singleEntry) => { - if (singleEntry.type === 'nested') { - return addIdToItem({ - ...singleEntry, - entries: singleEntry.entries.map((nestedEntry) => addIdToItem(nestedEntry)), - }); - } else { - return addIdToItem(singleEntry); - } - }); -}; - -export const getNewExceptionItem = ({ - listId, - namespaceType, - name, -}: { - listId: string | undefined; - namespaceType: NamespaceType | undefined; - name: string; -}): CreateExceptionListItemBuilderSchema => { - return { - comments: [], - description: `Exception list item`, - entries: addIdToEntries([ - { - field: '', - operator: 'included', - type: 'match', - value: '', - }, - ]), - item_id: undefined, - list_id: listId, - meta: { - temporaryUuid: uuidv4(), - }, - name, - namespace_type: namespaceType, - tags: [], - type: 'simple', - }; -}; - -/** - * Returns the operator type, may not need this if using io-ts types - * - * @param item a single ExceptionItem entry - */ -export const getOperatorType = (item: BuilderEntry): OperatorTypeEnum => { - switch (item.type) { - case 'match': - return OperatorTypeEnum.MATCH; - case 'match_any': - return OperatorTypeEnum.MATCH_ANY; - case 'wildcard': - return OperatorTypeEnum.WILDCARD; - case 'list': - return OperatorTypeEnum.LIST; - default: - return OperatorTypeEnum.EXISTS; - } -}; - -/** - * Determines operator selection (is/is not/is one of, etc.) - * Default operator is "is" - * - * @param item a single ExceptionItem entry - */ -export const getExceptionOperatorSelect = (item: BuilderEntry): OperatorOption => { - if (item.type === 'nested') { - return isOperator; - } else { - const operatorType = getOperatorType(item); - const foundOperator = ALL_OPERATORS.find((operatorOption) => { - return item.operator === operatorOption.operator && operatorType === operatorOption.type; - }); - - return foundOperator != null ? foundOperator : isOperator; - } -}; - -/** - * Returns the fields corresponding value for an entry - * - * @param item a single ExceptionItem entry - */ -export const getEntryValue = (item: BuilderEntry): string | string[] | undefined => { - switch (item.type) { - case OperatorTypeEnum.MATCH: - case OperatorTypeEnum.MATCH_ANY: - case OperatorTypeEnum.WILDCARD: - return item.value; - case OperatorTypeEnum.EXISTS: - return undefined; - case OperatorTypeEnum.LIST: - return item.list.id; - default: - return undefined; - } -}; - -/** - * Determines whether an entire entry, exception item, or entry within a nested - * entry needs to be removed - * - * @param exceptionItem - * @param entryIndex index of given entry, for nested entries, this will correspond - * to their parent index - * @param nestedEntryIndex index of nested entry - * - */ -export const getUpdatedEntriesOnDelete = ( - exceptionItem: ExceptionsBuilderExceptionItem, - entryIndex: number, - nestedParentIndex: number | null -): ExceptionsBuilderExceptionItem => { - const itemOfInterest: BuilderEntry = - exceptionItem.entries[nestedParentIndex != null ? nestedParentIndex : entryIndex]; - - if (nestedParentIndex != null && itemOfInterest.type === OperatorTypeEnum.NESTED) { - const updatedEntryEntries = [ - ...itemOfInterest.entries.slice(0, entryIndex), - ...itemOfInterest.entries.slice(entryIndex + 1), - ]; - - if (updatedEntryEntries.length === 0) { - return { - ...exceptionItem, - entries: [ - ...exceptionItem.entries.slice(0, nestedParentIndex), - ...exceptionItem.entries.slice(nestedParentIndex + 1), - ], - }; - } else { - const { field } = itemOfInterest; - const updatedItemOfInterest: EntryNested | EmptyNestedEntry = { - entries: updatedEntryEntries, - field, - id: itemOfInterest.id != null ? itemOfInterest.id : `${entryIndex}`, - type: OperatorTypeEnum.NESTED, - }; - - return { - ...exceptionItem, - entries: [ - ...exceptionItem.entries.slice(0, nestedParentIndex), - updatedItemOfInterest, - ...exceptionItem.entries.slice(nestedParentIndex + 1), - ], - }; - } - } else { - return { - ...exceptionItem, - entries: [ - ...exceptionItem.entries.slice(0, entryIndex), - ...exceptionItem.entries.slice(entryIndex + 1), - ], - }; - } -}; - -/** - * Returns filtered index patterns based on the field - if a user selects to - * add nested entry, should only show nested fields, if item is the parent - * field of a nested entry, we only display the parent field - * - * @param patterns DataViewBase containing available fields on rule index - * @param item exception item entry - * set to add a nested field - */ -export const getFilteredIndexPatterns = ( - patterns: DataViewBase, - item: FormattedBuilderEntry -): DataViewBase => { - if (item.nested === 'child' && item.parent != null) { - // when user has selected a nested entry, only fields with the common parent are shown - return { - ...patterns, - fields: patterns.fields - .filter((indexField) => { - const subTypeNested = getDataViewFieldSubtypeNested(indexField); - const fieldHasCommonParentPath = - subTypeNested && - item.parent != null && - subTypeNested.nested.path === item.parent.parent.field; - - return fieldHasCommonParentPath; - }) - .map((f) => { - const [fieldNameWithoutParentPath] = f.name.split('.').slice(-1); - return { ...f, name: fieldNameWithoutParentPath }; - }), - }; - } else if (item.nested === 'parent' && item.field != null) { - // when user has selected a nested entry, right above it we show the common parent - return { ...patterns, fields: [item.field] }; - } else if (item.nested === 'parent' && item.field == null) { - // when user selects to add a nested entry, only nested fields are shown as options - return { - ...patterns, - fields: patterns.fields.filter((field) => isDataViewFieldSubtypeNested(field)), - }; - } else { - return patterns; - } -}; - -/** - * Determines proper entry update when user selects new field - * - * @param item - current exception item entry values - * @param newField - newly selected field - * - */ -export const getEntryOnFieldChange = ( - item: FormattedBuilderEntry, - newField: DataViewFieldBase -): { index: number; updatedEntry: BuilderEntry } => { - const { parent, entryIndex, nested } = item; - const newChildFieldValue = newField != null ? newField.name.split('.').slice(-1)[0] : ''; - - if (nested === 'parent') { - // For nested entries, when user first selects to add a nested - // entry, they first see a row similar to what is shown for when - // a user selects "exists", as soon as they make a selection - // we can now identify the 'parent' and 'child' this is where - // we first convert the entry into type "nested" - const subTypeNested = getDataViewFieldSubtypeNested(newField); - const newParentFieldValue = subTypeNested?.nested.path || ''; - - return { - index: entryIndex, - updatedEntry: { - entries: [ - addIdToItem({ - field: newChildFieldValue != null ? newChildFieldValue : '', - operator: isOperator.operator, - type: OperatorTypeEnum.MATCH, - value: '', - }), - ], - field: newParentFieldValue, - id: item.id, - type: OperatorTypeEnum.NESTED, - }, - }; - } else if (nested === 'child' && parent != null) { - return { - index: parent.parentIndex, - updatedEntry: { - ...parent.parent, - entries: [ - ...parent.parent.entries.slice(0, entryIndex), - { - field: newChildFieldValue != null ? newChildFieldValue : '', - id: item.id, - operator: isOperator.operator, - type: OperatorTypeEnum.MATCH, - value: '', - }, - ...parent.parent.entries.slice(entryIndex + 1), - ], - }, - }; - } else { - return { - index: entryIndex, - updatedEntry: { - field: newField != null ? newField.name : '', - id: item.id, - operator: isOperator.operator, - type: OperatorTypeEnum.MATCH, - value: '', - }, - }; - } -}; - -/** - * Determines proper entry update when user updates value - * when operator is of type "list" - * - * @param item - current exception item entry values - * @param newField - newly selected list - * - */ -export const getEntryOnListChange = ( - item: FormattedBuilderEntry, - newField: ListSchema -): { index: number; updatedEntry: BuilderEntry } => { - const { entryIndex, field, operator } = item; - const { id, type } = newField; - - return { - index: entryIndex, - updatedEntry: { - field: field != null ? field.name : '', - id: item.id, - list: { id, type }, - operator: operator.operator, - type: OperatorTypeEnum.LIST, - }, - }; -}; - -/** - * Determines proper entry update when user updates value - * when operator is of type "match_any" - * - * @param item - current exception item entry values - * @param newField - newly entered value - * - */ -export const getEntryOnMatchAnyChange = ( - item: FormattedBuilderEntry, - newField: string[] -): { index: number; updatedEntry: BuilderEntry } => { - const { nested, parent, entryIndex, field, operator } = item; - - if (nested != null && parent != null) { - const fieldName = field != null ? field.name.split('.').slice(-1)[0] : ''; - - return { - index: parent.parentIndex, - updatedEntry: { - ...parent.parent, - entries: [ - ...parent.parent.entries.slice(0, entryIndex), - { - field: fieldName, - id: item.id, - operator: operator.operator, - type: OperatorTypeEnum.MATCH_ANY, - value: newField, - }, - ...parent.parent.entries.slice(entryIndex + 1), - ], - }, - }; - } else { - return { - index: entryIndex, - updatedEntry: { - field: field != null ? field.name : '', - id: item.id, - operator: operator.operator, - type: OperatorTypeEnum.MATCH_ANY, - value: newField, - }, - }; - } -}; - -/** - * Determines proper entry update when user updates value - * when operator is of type "match" - * - * @param item - current exception item entry values - * @param newField - newly entered value - * - */ -export const getEntryOnMatchChange = ( - item: FormattedBuilderEntry, - newField: string -): { index: number; updatedEntry: BuilderEntry } => { - const { nested, parent, entryIndex, field, operator } = item; - - if (nested != null && parent != null) { - const fieldName = field != null ? field.name.split('.').slice(-1)[0] : ''; - - return { - index: parent.parentIndex, - updatedEntry: { - ...parent.parent, - entries: [ - ...parent.parent.entries.slice(0, entryIndex), - { - field: fieldName, - id: item.id, - operator: operator.operator, - type: OperatorTypeEnum.MATCH, - value: newField, - }, - ...parent.parent.entries.slice(entryIndex + 1), - ], - }, - }; - } else { - return { - index: entryIndex, - updatedEntry: { - field: field != null ? field.name : '', - id: item.id, - operator: operator.operator, - type: OperatorTypeEnum.MATCH, - value: newField, - }, - }; - } -}; - -/** - * Determines proper entry update when user updates value - * when operator is of type "wildcard" - * - * @param item - current exception item entry values - * @param newField - newly entered value - * - */ -export const getEntryOnWildcardChange = ( - item: FormattedBuilderEntry, - newField: string -): { index: number; updatedEntry: BuilderEntry } => { - const { nested, parent, entryIndex, field, operator } = item; - - if (nested != null && parent != null) { - const fieldName = field != null ? field.name.split('.').slice(-1)[0] : ''; - - return { - index: parent.parentIndex, - updatedEntry: { - ...parent.parent, - entries: [ - ...parent.parent.entries.slice(0, entryIndex), - { - field: fieldName, - id: item.id, - operator: operator.operator, - type: OperatorTypeEnum.WILDCARD, - value: newField, - }, - ...parent.parent.entries.slice(entryIndex + 1), - ], - }, - }; - } else { - return { - index: entryIndex, - updatedEntry: { - field: field != null ? field.name : '', - id: item.id, - operator: operator.operator, - type: OperatorTypeEnum.WILDCARD, - value: newField, - }, - }; - } -}; - -/** - * On operator change, determines whether value needs to be cleared or not - * - * @param field - * @param selectedOperator - * @param currentEntry - * - */ -export const getEntryFromOperator = ( - selectedOperator: OperatorOption, - currentEntry: FormattedBuilderEntry -): Entry & { id?: string } => { - const isSameOperatorType = currentEntry.operator.type === selectedOperator.type; - const fieldValue = currentEntry.field != null ? currentEntry.field.name : ''; - switch (selectedOperator.type) { - case 'match': - return { - field: fieldValue, - id: currentEntry.id, - operator: selectedOperator.operator, - type: OperatorTypeEnum.MATCH, - value: - isSameOperatorType && typeof currentEntry.value === 'string' ? currentEntry.value : '', - }; - case 'match_any': - return { - field: fieldValue, - id: currentEntry.id, - operator: selectedOperator.operator, - type: OperatorTypeEnum.MATCH_ANY, - value: isSameOperatorType && Array.isArray(currentEntry.value) ? currentEntry.value : [], - }; - case 'list': - return { - field: fieldValue, - id: currentEntry.id, - list: { id: '', type: 'ip' }, - operator: selectedOperator.operator, - type: OperatorTypeEnum.LIST, - }; - case 'wildcard': - return { - field: fieldValue, - id: currentEntry.id, - operator: selectedOperator.operator, - type: OperatorTypeEnum.WILDCARD, - value: - isSameOperatorType && typeof currentEntry.value === 'string' ? currentEntry.value : '', - }; - default: - return { - field: fieldValue, - id: currentEntry.id, - operator: selectedOperator.operator, - type: OperatorTypeEnum.EXISTS, - }; - } -}; - -/** - * Determines proper entry update when user selects new operator - * - * @param item - current exception item entry values - * @param newOperator - newly selected operator - * - */ -export const getEntryOnOperatorChange = ( - item: FormattedBuilderEntry, - newOperator: OperatorOption -): { updatedEntry: BuilderEntry; index: number } => { - const { parent, entryIndex, field, nested } = item; - const newEntry = getEntryFromOperator(newOperator, item); - - if (!entriesList.is(newEntry) && nested != null && parent != null) { - return { - index: parent.parentIndex, - updatedEntry: { - ...parent.parent, - entries: [ - ...parent.parent.entries.slice(0, entryIndex), - { - ...newEntry, - field: field != null ? field.name.split('.').slice(-1)[0] : '', - }, - ...parent.parent.entries.slice(entryIndex + 1), - ], - }, - }; - } else { - return { index: entryIndex, updatedEntry: newEntry }; - } -}; - -export const isKibanaStringType = (type: string) => { - const kbnFieldType = castEsToKbnFieldTypeName(type); - return kbnFieldType === KBN_FIELD_TYPES.STRING; -}; - -export const fieldSupportsMatches = (field: DataViewFieldBase) => { - return field.esTypes?.some(isKibanaStringType); -}; - -/** - * Determines which operators to make available - * - * @param item - * @param listType - * @param isBoolean - * @param includeValueListOperators whether or not to include the 'is in list' and 'is not in list' operators - */ -export const getOperatorOptions = ( - item: FormattedBuilderEntry, - listType: ExceptionListType, - isBoolean: boolean, - includeValueListOperators = true -): OperatorOption[] => { - if (item.nested === 'parent' || item.field == null) { - return [isOperator]; - } else if (listType === 'endpoint') { - if (isBoolean) { - return [isOperator]; - } else { - return fieldSupportsMatches(item.field) - ? [isOperator, isOneOfOperator, matchesOperator, doesNotMatchOperator] - : [isOperator, isOneOfOperator]; - } - } else if (item.nested != null && listType === 'detection') { - return isBoolean ? [isOperator, existsOperator] : [isOperator, isOneOfOperator, existsOperator]; - } else if (isBoolean) { - return [isOperator, isNotOperator, existsOperator, doesNotExistOperator]; - } else if (!includeValueListOperators) { - return fieldSupportsMatches(item.field) - ? EXCEPTION_OPERATORS_SANS_LISTS - : [ - isOperator, - isNotOperator, - isOneOfOperator, - isNotOneOfOperator, - existsOperator, - doesNotExistOperator, - ]; - } else { - return listType === 'detection' - ? fieldSupportsMatches(item.field) - ? DETECTION_ENGINE_EXCEPTION_OPERATORS - : [ - isOperator, - isNotOperator, - isOneOfOperator, - isNotOneOfOperator, - existsOperator, - doesNotExistOperator, - isInListOperator, - isNotInListOperator, - ] - : ALL_OPERATORS; - } -}; - -/** - * Fields of type 'text' do not generate autocomplete values, we want - * to find it's corresponding keyword type (if available) which does - * generate autocomplete values - * - * @param fields DataViewFieldBase fields - * @param selectedField the field name that was selected - * @param isTextType we only want a corresponding keyword field if - * the selected field is of type 'text' - * - */ -export const getCorrespondingKeywordField = ({ - fields, - selectedField, -}: { - fields: DataViewFieldBase[]; - selectedField: string | undefined; -}): DataViewFieldBase | undefined => { - const selectedFieldBits = - selectedField != null && selectedField !== '' ? selectedField.split('.') : []; - const selectedFieldIsTextType = selectedFieldBits.slice(-1)[0] === 'text'; - - if (selectedFieldIsTextType && selectedFieldBits.length > 0) { - const keywordField = selectedFieldBits.slice(0, selectedFieldBits.length - 1).join('.'); - const [foundKeywordField] = fields.filter( - ({ name }) => keywordField !== '' && keywordField === name - ); - return foundKeywordField; - } - - return undefined; -}; - -/** - * Formats the entry into one that is easily usable for the UI, most of the - * complexity was introduced with nested fields - * - * @param patterns DataViewBase containing available fields on rule index - * @param item exception item entry - * @param itemIndex entry index - * @param parent nested entries hold copy of their parent for use in various logic - * @param parentIndex corresponds to the entry index, this might seem obvious, but - * was added to ensure that nested items could be identified with their parent entry - * @param allowCustomFieldOptions determines if field must be found to match in indexPattern or not - */ -export const getFormattedBuilderEntry = ( - indexPattern: DataViewBase, - item: BuilderEntry, - itemIndex: number, - parent: EntryNested | undefined, - parentIndex: number | undefined, - allowCustomFieldOptions: boolean -): FormattedBuilderEntry => { - const { fields } = indexPattern; - const field = parent != null ? `${parent.field}.${item.field}` : item.field; - const [foundField] = fields.filter(({ name }) => field != null && field === name); - const correspondingKeywordField = getCorrespondingKeywordField({ - fields, - selectedField: field, - }); - - if (parent != null && parentIndex != null) { - return { - correspondingKeywordField, - entryIndex: itemIndex, - field: - foundField != null - ? { ...foundField, name: foundField.name.split('.').slice(-1)[0] } - : foundField, - id: item.id != null ? item.id : `${itemIndex}`, - nested: 'child', - operator: getExceptionOperatorSelect(item), - parent: { parent, parentIndex }, - value: getEntryValue(item), - }; - } else { - const fieldToUse = allowCustomFieldOptions - ? foundField ?? { name: item.field, type: 'keyword' } - : foundField; - - return { - correspondingKeywordField, - entryIndex: itemIndex, - field: fieldToUse, - id: item.id != null ? item.id : `${itemIndex}`, - nested: undefined, - operator: getExceptionOperatorSelect(item), - parent: undefined, - value: getEntryValue(item), - }; - } -}; - -/** - * Formats the entries to be easily usable for the UI, most of the - * complexity was introduced with nested fields - * - * @param patterns DataViewBase containing available fields on rule index - * @param entries exception item entries - * @param allowCustomFieldOptions determines if field must be found to match in indexPattern or not - * @param parent nested entries hold copy of their parent for use in various logic - * @param parentIndex corresponds to the entry index, this might seem obvious, but - * was added to ensure that nested items could be identified with their parent entry - */ -export const getFormattedBuilderEntries = ( - indexPattern: DataViewBase, - entries: BuilderEntry[], - allowCustomFieldOptions: boolean, - parent?: EntryNested, - parentIndex?: number -): FormattedBuilderEntry[] => { - return entries.reduce<FormattedBuilderEntry[]>((acc, item, index) => { - const isNewNestedEntry = item.type === 'nested' && item.entries.length === 0; - if (item.type !== 'nested' && !isNewNestedEntry) { - const newItemEntry: FormattedBuilderEntry = getFormattedBuilderEntry( - indexPattern, - item, - index, - parent, - parentIndex, - allowCustomFieldOptions - ); - return [...acc, newItemEntry]; - } else { - const parentEntry: FormattedBuilderEntry = { - correspondingKeywordField: undefined, - entryIndex: index, - field: isNewNestedEntry - ? undefined - : // This type below is really a FieldSpec type from "src/plugins/data/common/index_patterns/fields/types.ts", we cast it here to keep using the DataViewFieldBase interface - ({ - aggregatable: false, - esTypes: ['nested'], - name: item.field != null ? item.field : '', - searchable: false, - type: 'string', - } as DataViewFieldBase), - id: item.id != null ? item.id : `${index}`, - nested: 'parent', - operator: isOperator, - parent: undefined, - value: undefined, - }; - - // User has selected to add a nested field, but not yet selected the field - if (isNewNestedEntry) { - return [...acc, parentEntry]; - } - - if (isEntryNested(item)) { - const nestedItems = getFormattedBuilderEntries( - indexPattern, - item.entries, - allowCustomFieldOptions, - item, - index - ); - - return [...acc, parentEntry, ...nestedItems]; - } - - return [...acc]; - } - }, []); -}; - -export const getDefaultEmptyEntry = (): EmptyEntry => ({ - field: '', - id: uuidv4(), - operator: OperatorEnum.INCLUDED, - type: OperatorTypeEnum.MATCH, - value: '', -}); - -export const getDefaultNestedEmptyEntry = (): EmptyNestedEntry => ({ - entries: [], - field: '', - id: uuidv4(), - type: OperatorTypeEnum.NESTED, -}); - -export const containsValueListEntry = (items: ExceptionsBuilderExceptionItem[]): boolean => - items.some((item) => item.entries.some(({ type }) => type === OperatorTypeEnum.LIST)); - -export const buildShowActiveExceptionsFilter = (savedObjectPrefix: SavedObjectType[]): string => { - const now = new Date().toISOString(); - const filters = savedObjectPrefix.map( - (prefix) => - `${prefix}.attributes.expire_time > "${now}" OR NOT ${prefix}.attributes.expire_time: *` - ); - return filters.join(','); -}; - -export const buildShowExpiredExceptionsFilter = (savedObjectPrefix: SavedObjectType[]): string => { - const now = new Date().toISOString(); - const filters = savedObjectPrefix.map((prefix) => `${prefix}.attributes.expire_time <= "${now}"`); - return filters.join(','); -}; - -const getIndexGroupName = (indexName: string): string => { - // Check whether it is a Data Stream index - const dataStreamExp = /.ds-(.*?)-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-[0-9]{6}/; - let result = indexName.match(dataStreamExp); - if (result && result.length === 2) { - return result[1]; - } - - // Check whether it is an old '.siem' index group - const siemSignalsExp = /.siem-(.*?)-[0-9]{6}/; - result = indexName.match(siemSignalsExp); - if (result && result.length === 2) { - return `.siem-${result[1]}`; - } - - // Otherwise return index name - return indexName; -}; - -export interface FieldConflictsInfo { - /** - * Kibana field type - */ - type: string; - /** - * Total count of the indices of this type - */ - totalIndexCount: number; - /** - * Grouped indices info - */ - groupedIndices: Array<{ - /** - * Index group name (like '.ds-...' or '.siem-signals-...') - */ - name: string; - /** - * Count of indices in the group - */ - count: number; - }>; -} - -export const getMappingConflictsInfo = (field: DataViewField): FieldConflictsInfo[] | null => { - if (!field.conflictDescriptions) { - return null; - } - const conflicts: FieldConflictsInfo[] = []; - for (const [key, value] of Object.entries(field.conflictDescriptions)) { - const groupedIndices: Array<{ - name: string; - count: number; - }> = []; - - // Group indices and calculate count of indices in each group - const groupedInfo: { [key: string]: number } = {}; - value.forEach((index) => { - const groupName = getIndexGroupName(index); - if (!groupedInfo[groupName]) { - groupedInfo[groupName] = 0; - } - groupedInfo[groupName]++; - }); - for (const [name, count] of Object.entries(groupedInfo)) { - groupedIndices.push({ - name, - count, - }); - } - - // Sort groups by the indices count - groupedIndices.sort((group1, group2) => { - return group2.count - group1.count; - }); - - conflicts.push({ - type: key, - totalIndexCount: value.length, - groupedIndices, - }); - } - return conflicts; -}; - -/** - * Given an exceptions list, determine if any entries have an "IS" operator with a wildcard value - */ -export const hasWrongOperatorWithWildcard = ( - items: ExceptionsBuilderReturnExceptionItem[] -): boolean => { - // flattens array of multiple entries added with OR - const multipleEntries = items.flatMap((item) => item.entries); - // flattens nested entries - const allEntries = multipleEntries.flatMap((item) => { - if (item.type === 'nested') { - return item.entries; - } - return item; - }); - - return allEntries.some((e) => { - if (e.type !== 'list' && 'value' in e) { - return validateHasWildcardWithWrongOperator({ - operator: e.type, - value: e.value, - }); - } - }); -}; - -/** - * Event filters helper where given an exceptions list, - * determine if both 'subject_name' and 'trusted' are - * included in an entry with 'code_signature' - */ -export const hasPartialCodeSignatureEntry = ( - items: ExceptionsBuilderReturnExceptionItem[] -): boolean => { - const { os_types: os = ['windows'], entries = [] } = items[0] || {}; - let name = false; - let trusted = false; - - for (const e of entries) { - if (e.type === 'nested' && e.field === 'process.Ext.code_signature') { - const includesNestedName = e.entries.some( - (nestedEntry) => nestedEntry.field === 'subject_name' - ); - const includesNestedTrusted = e.entries.some( - (nestedEntry) => nestedEntry.field === 'trusted' - ); - if (includesNestedName !== includesNestedTrusted) { - return true; - } - } else if ( - e.field === 'process.code_signature.subject_name' || - (os.includes('macos') && e.field === 'process.code_signature.team_id') - ) { - name = true; - } else if (e.field === 'process.code_signature.trusted') { - trusted = true; - } - } - return name !== trusted; -}; diff --git a/packages/kbn-securitysolution-list-utils/src/types/index.ts b/packages/kbn-securitysolution-list-utils/src/types/index.ts deleted file mode 100644 index 97098af88caf..000000000000 --- a/packages/kbn-securitysolution-list-utils/src/types/index.ts +++ /dev/null @@ -1,124 +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 { DataViewFieldBase } from '@kbn/es-query'; -import type { - CreateExceptionListItemSchema, - CreateRuleExceptionListItemSchema, - Entry, - EntryExists, - EntryMatch, - EntryMatchAny, - EntryMatchWildcard, - EntryNested, - ExceptionListItemSchema, - ListOperatorEnum as OperatorEnum, - ListOperatorTypeEnum as OperatorTypeEnum, - NamespaceType, -} from '@kbn/securitysolution-io-ts-list-types'; -import { - EXCEPTION_LIST_NAMESPACE, - EXCEPTION_LIST_NAMESPACE_AGNOSTIC, -} from '@kbn/securitysolution-list-constants'; - -export interface DataViewField extends DataViewFieldBase { - conflictDescriptions?: Record<string, string[]>; -} - -export interface OperatorOption { - message: string; - value: string; - operator: OperatorEnum; - type: OperatorTypeEnum; -} - -export interface FormattedBuilderEntry { - id: string; - field: DataViewField | undefined; - operator: OperatorOption; - value: string | string[] | undefined; - nested: 'parent' | 'child' | undefined; - entryIndex: number; - parent: { parent: BuilderEntryNested; parentIndex: number } | undefined; - correspondingKeywordField: DataViewFieldBase | undefined; -} - -export interface EmptyEntry { - id: string; - field: string | undefined; - operator: OperatorEnum; - type: OperatorTypeEnum.MATCH | OperatorTypeEnum.MATCH_ANY | OperatorTypeEnum.WILDCARD; - value: string | string[] | undefined; -} - -export interface EmptyListEntry { - id: string; - field: string | undefined; - operator: OperatorEnum; - type: OperatorTypeEnum.LIST; - list: { id: string | undefined; type: string | undefined }; -} - -export interface EmptyNestedEntry { - id: string; - field: string | undefined; - type: OperatorTypeEnum.NESTED; - entries: Array< - | (EntryMatch & { id?: string }) - | (EntryMatchAny & { id?: string }) - | (EntryMatchWildcard & { id?: string }) - | (EntryExists & { id?: string }) - >; -} - -export type BuilderEntry = - | (Entry & { id?: string }) - | EmptyListEntry - | EmptyEntry - | BuilderEntryNested - | EmptyNestedEntry; - -export type BuilderEntryNested = Omit<EntryNested, 'entries'> & { - id?: string; - entries: Array< - | (EntryMatch & { id?: string }) - | (EntryMatchAny & { id?: string }) - | (EntryMatchWildcard & { id?: string }) - | (EntryExists & { id?: string }) - >; -}; - -export type ExceptionListItemBuilderSchema = Omit<ExceptionListItemSchema, 'entries'> & { - entries: BuilderEntry[]; -}; - -export type CreateExceptionListItemBuilderSchema = Omit< - CreateExceptionListItemSchema, - 'meta' | 'entries' | 'list_id' | 'namespace_type' -> & { - meta: { temporaryUuid: string }; - entries: BuilderEntry[]; - list_id: string | undefined; - namespace_type: NamespaceType | undefined; -}; - -export type ExceptionsBuilderExceptionItem = - | ExceptionListItemBuilderSchema - | CreateExceptionListItemBuilderSchema; - -export type ExceptionsBuilderReturnExceptionItem = - | ExceptionListItemSchema - | CreateExceptionListItemSchema - | CreateRuleExceptionListItemSchema; - -export const exceptionListSavedObjectType = EXCEPTION_LIST_NAMESPACE; -export const exceptionListAgnosticSavedObjectType = EXCEPTION_LIST_NAMESPACE_AGNOSTIC; -export type SavedObjectType = - | typeof EXCEPTION_LIST_NAMESPACE - | typeof EXCEPTION_LIST_NAMESPACE_AGNOSTIC; diff --git a/packages/kbn-securitysolution-list-utils/tsconfig.json b/packages/kbn-securitysolution-list-utils/tsconfig.json deleted file mode 100644 index d75eb1f98314..000000000000 --- a/packages/kbn-securitysolution-list-utils/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts" - ], - "kbn_references": [ - "@kbn/es-query", - "@kbn/i18n", - "@kbn/securitysolution-io-ts-list-types", - "@kbn/securitysolution-io-ts-utils", - "@kbn/securitysolution-list-constants", - "@kbn/securitysolution-utils", - "@kbn/field-types" - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-lists-common/api/create_list/create_list.gen.ts b/packages/kbn-securitysolution-lists-common/api/create_list/create_list.gen.ts deleted file mode 100644 index ce05117c3308..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/create_list/create_list.gen.ts +++ /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", 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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Create list API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { - ListId, - ListName, - ListDescription, - ListType, - ListMetadata, -} from '../model/list_common.gen'; -import { List } from '../model/list_schemas.gen'; - -export type CreateListRequestBody = z.infer<typeof CreateListRequestBody>; -export const CreateListRequestBody = z.object({ - id: ListId.optional(), - name: ListName, - description: ListDescription, - type: ListType, - serializer: z.string().optional(), - deserializer: z.string().optional(), - meta: ListMetadata.optional(), - version: z.number().int().min(1).optional().default(1), -}); -export type CreateListRequestBodyInput = z.input<typeof CreateListRequestBody>; - -export type CreateListResponse = z.infer<typeof CreateListResponse>; -export const CreateListResponse = List; diff --git a/packages/kbn-securitysolution-lists-common/api/create_list/create_list.schema.yaml b/packages/kbn-securitysolution-lists-common/api/create_list/create_list.schema.yaml deleted file mode 100644 index 191e973beba6..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/create_list/create_list.schema.yaml +++ /dev/null @@ -1,81 +0,0 @@ -openapi: 3.0.0 -info: - title: Create list API endpoint - version: '2023-10-31' -paths: - /api/lists: - post: - x-labels: [serverless, ess] - operationId: CreateList - x-codegen-enabled: true - summary: Create a list - description: Create a new list. - requestBody: - description: List's properties - required: true - content: - application/json: - schema: - type: object - properties: - id: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - name: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListName' - description: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListDescription' - type: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListType' - serializer: - type: string - deserializer: - type: string - meta: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListMetadata' - version: - type: integer - minimum: 1 - default: 1 - required: - - name - - description - - type - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/list_schemas.schema.yaml#/components/schemas/List' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 409: - description: List already exists response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.gen.ts b/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.gen.ts deleted file mode 100644 index 13b5248b9270..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.gen.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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Create list DS API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -export type CreateListIndexResponse = z.infer<typeof CreateListIndexResponse>; -export const CreateListIndexResponse = z.object({ - acknowledged: z.boolean(), -}); diff --git a/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.schema.yaml b/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.schema.yaml deleted file mode 100644 index c775a9c7d873..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.schema.yaml +++ /dev/null @@ -1,55 +0,0 @@ -openapi: 3.0.0 -info: - title: Create list DS API endpoint - version: '2023-10-31' -paths: - /api/lists/index: - post: - x-labels: [serverless, ess] - operationId: CreateListIndex - x-codegen-enabled: true - summary: Create list data streams - description: Create `.lists` and `.items` data streams in the relevant space. - responses: - 200: - description: Successful response - content: - application/json: - schema: - type: object - properties: - acknowledged: - type: boolean - required: [acknowledged] - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 409: - description: List data stream exists response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.gen.ts b/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.gen.ts deleted file mode 100644 index 4ebafd656857..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.gen.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", 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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Create list item API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { ListItemId, ListId, ListItemValue, ListItemMetadata } from '../model/list_common.gen'; -import { ListItem } from '../model/list_schemas.gen'; - -export type CreateListItemRequestBody = z.infer<typeof CreateListItemRequestBody>; -export const CreateListItemRequestBody = z.object({ - id: ListItemId.optional(), - list_id: ListId, - value: ListItemValue, - meta: ListItemMetadata.optional(), - /** - * Determines when changes made by the request are made visible to search - */ - refresh: z.enum(['true', 'false', 'wait_for']).optional(), -}); -export type CreateListItemRequestBodyInput = z.input<typeof CreateListItemRequestBody>; - -export type CreateListItemResponse = z.infer<typeof CreateListItemResponse>; -export const CreateListItemResponse = ListItem; diff --git a/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.schema.yaml b/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.schema.yaml deleted file mode 100644 index 01121d014392..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.schema.yaml +++ /dev/null @@ -1,82 +0,0 @@ -openapi: 3.0.0 -info: - title: Create list item API endpoint - version: '2023-10-31' -paths: - /api/lists/items: - post: - x-labels: [serverless, ess] - operationId: CreateListItem - x-codegen-enabled: true - summary: Create a list item - description: | - Create a list item and associate it with the specified list. - - All list items in the same list must be the same type. For example, each list item in an `ip` list must define a specific IP address. - > info - > Before creating a list item, you must create a list. - requestBody: - description: List item's properties - required: true - content: - application/json: - schema: - type: object - properties: - id: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemId' - list_id: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - value: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemValue' - meta: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemMetadata' - refresh: - type: string - enum: - - 'true' - - 'false' - - wait_for - description: Determines when changes made by the request are made visible to search - required: - - list_id - - value - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 409: - description: List item already exists response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.gen.ts b/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.gen.ts deleted file mode 100644 index 109e6c58a116..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.gen.ts +++ /dev/null @@ -1,37 +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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Delete list API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; -import { BooleanFromString } from '@kbn/zod-helpers'; - -import { ListId } from '../model/list_common.gen'; -import { List } from '../model/list_schemas.gen'; - -export type DeleteListRequestQuery = z.infer<typeof DeleteListRequestQuery>; -export const DeleteListRequestQuery = z.object({ - /** - * List's `id` value - */ - id: ListId, - deleteReferences: BooleanFromString.optional().default(false), - ignoreReferences: BooleanFromString.optional().default(false), -}); -export type DeleteListRequestQueryInput = z.input<typeof DeleteListRequestQuery>; - -export type DeleteListResponse = z.infer<typeof DeleteListResponse>; -export const DeleteListResponse = List; diff --git a/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.schema.yaml b/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.schema.yaml deleted file mode 100644 index 709875363637..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.schema.yaml +++ /dev/null @@ -1,73 +0,0 @@ -openapi: 3.0.0 -info: - title: Delete list API endpoint - version: '2023-10-31' -paths: - /api/lists: - delete: - x-labels: [serverless, ess] - operationId: DeleteList - x-codegen-enabled: true - summary: Delete a list - description: | - Delete a list using the list ID. - > info - > When you delete a list, all of its list items are also deleted. - parameters: - - name: id - in: query - required: true - description: List's `id` value - schema: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - - name: deleteReferences - in: query - required: false - schema: - type: boolean - default: false - - name: ignoreReferences - in: query - required: false - schema: - type: boolean - default: false - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/list_schemas.schema.yaml#/components/schemas/List' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.gen.ts b/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.gen.ts deleted file mode 100644 index 314c1e81bc6a..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.gen.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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Delete list DS API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -export type DeleteListIndexResponse = z.infer<typeof DeleteListIndexResponse>; -export const DeleteListIndexResponse = z.object({ - acknowledged: z.boolean(), -}); diff --git a/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.schema.yaml b/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.schema.yaml deleted file mode 100644 index 4f4b0f00e881..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.schema.yaml +++ /dev/null @@ -1,55 +0,0 @@ -openapi: 3.0.0 -info: - title: Delete list DS API endpoint - version: '2023-10-31' -paths: - /api/lists/index: - delete: - x-labels: [serverless, ess] - operationId: DeleteListIndex - x-codegen-enabled: true - summary: Delete list data streams - description: Delete the `.lists` and `.items` data streams. - responses: - 200: - description: Successful response - content: - application/json: - schema: - type: object - properties: - acknowledged: - type: boolean - required: [acknowledged] - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List data stream not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.schema.yaml b/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.schema.yaml deleted file mode 100644 index 28913259387d..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.schema.yaml +++ /dev/null @@ -1,82 +0,0 @@ -openapi: 3.0.0 -info: - title: Delete list item API endpoint - version: '2023-10-31' -paths: - /api/lists/items: - delete: - x-labels: [serverless, ess] - operationId: DeleteListItem - x-codegen-enabled: true - summary: Delete a list item - description: Delete a list item using its `id`, or its `list_id` and `value` fields. - parameters: - - name: id - in: query - required: false - description: Required if `list_id` and `value` are not specified - schema: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - - name: list_id - in: query - required: false - description: Required if `id` is not specified - schema: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - - name: value - in: query - required: false - description: Required if `id` is not specified - schema: - type: string - - name: refresh - in: query - required: false - description: Determines when changes made by the request are made visible to search - schema: - type: string - enum: ['true', 'false', 'wait_for'] - default: 'false' - responses: - 200: - description: Successful response - content: - application/json: - schema: - oneOf: - - $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' - - type: array - items: - $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List item not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.gen.ts b/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.gen.ts deleted file mode 100644 index 4e514abefccc..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.gen.ts +++ /dev/null @@ -1,30 +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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Export list items API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { ListId } from '../model/list_common.gen'; - -export type ExportListItemsRequestQuery = z.infer<typeof ExportListItemsRequestQuery>; -export const ExportListItemsRequestQuery = z.object({ - /** - * List's id to export - */ - list_id: ListId, -}); -export type ExportListItemsRequestQueryInput = z.input<typeof ExportListItemsRequestQuery>; diff --git a/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.schema.yaml b/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.schema.yaml deleted file mode 100644 index 8d185a23b64c..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.schema.yaml +++ /dev/null @@ -1,60 +0,0 @@ -openapi: 3.0.0 -info: - title: Export list items API endpoint - version: '2023-10-31' -paths: - /api/lists/items/_export: - post: - x-labels: [serverless, ess] - operationId: ExportListItems - x-codegen-enabled: true - summary: Export list items - description: Export list item values from the specified list. - parameters: - - name: list_id - in: query - required: true - description: List's id to export - schema: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - responses: - 200: - description: Successful response - content: - application/ndjson: - schema: - type: string - format: binary - description: A `.txt` file containing list items from the specified list - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/index.ts b/packages/kbn-securitysolution-lists-common/api/index.ts deleted file mode 100644 index 27c70d162810..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './model/list_common.gen'; -export * from './model/list_schemas.gen'; -export * from './create_list_index/create_list_index.gen'; -export * from './create_list_item/create_list_item.gen'; -export * from './create_list/create_list.gen'; -export * from './delete_list_index/delete_list_index.gen'; -export * from './delete_list_item/delete_list_item.gen'; -export * from './delete_list/delete_list.gen'; -export * from './find_list_items/find_list_items.gen'; -export * from './find_lists/find_lists.gen'; -export * from './export_list_items/export_list_items.gen'; -export * from './import_list_items/import_list_items.gen'; -export * from './patch_list_item/patch_list_item.gen'; -export * from './patch_list/patch_list.gen'; -export * from './read_list_index/read_list_index.gen'; -export * from './read_list_item/read_list_item.gen'; -export * from './read_list/read_list.gen'; -export * from './update_list_item/update_list_item.gen'; -export * from './update_list/update_list.gen'; diff --git a/packages/kbn-securitysolution-lists-common/api/model/list_common.schema.yaml b/packages/kbn-securitysolution-lists-common/api/model/list_common.schema.yaml deleted file mode 100644 index 6fb160105bb5..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/model/list_common.schema.yaml +++ /dev/null @@ -1,59 +0,0 @@ -openapi: 3.0.0 -info: - title: Common List Attributes - version: 'not applicable' -paths: {} -components: - schemas: - ListId: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - - ListType: - type: string - enum: - - binary - - boolean - - byte - - date - - date_nanos - - date_range - - double - - double_range - - float - - float_range - - geo_point - - geo_shape - - half_float - - integer - - integer_range - - ip - - ip_range - - keyword - - long - - long_range - - shape - - short - - text - - ListName: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - - ListDescription: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - - ListMetadata: - type: object - additionalProperties: true - - ListItemId: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - - ListItemValue: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - - ListItemDescription: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - - ListItemMetadata: - type: object - additionalProperties: true diff --git a/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.gen.ts b/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.gen.ts deleted file mode 100644 index d7c955e6daac..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.gen.ts +++ /dev/null @@ -1,36 +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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Patch list API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { ListId, ListName, ListDescription, ListMetadata } from '../model/list_common.gen'; -import { List } from '../model/list_schemas.gen'; - -export type PatchListRequestBody = z.infer<typeof PatchListRequestBody>; -export const PatchListRequestBody = z.object({ - id: ListId, - name: ListName.optional(), - description: ListDescription.optional(), - meta: ListMetadata.optional(), - version: z.number().int().min(1).optional(), - _version: z.string().optional(), -}); -export type PatchListRequestBodyInput = z.input<typeof PatchListRequestBody>; - -export type PatchListResponse = z.infer<typeof PatchListResponse>; -export const PatchListResponse = List; diff --git a/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.schema.yaml b/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.schema.yaml deleted file mode 100644 index b98b34e6347e..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.schema.yaml +++ /dev/null @@ -1,74 +0,0 @@ -openapi: 3.0.0 -info: - title: Patch list API endpoint - version: '2023-10-31' -paths: - /api/lists: - patch: - x-labels: [serverless, ess] - operationId: PatchList - x-codegen-enabled: true - summary: Patch a list - description: Update specific fields of an existing list using the list ID. - requestBody: - description: List's properties - required: true - content: - application/json: - schema: - type: object - properties: - id: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - name: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListName' - description: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListDescription' - meta: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListMetadata' - version: - type: integer - minimum: 1 - _version: - type: string - required: - - id - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/list_schemas.schema.yaml#/components/schemas/List' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.gen.ts b/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.gen.ts deleted file mode 100644 index 9943a9999a89..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.gen.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", 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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Patch list item API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { ListItemId, ListItemValue, ListItemMetadata } from '../model/list_common.gen'; -import { ListItem } from '../model/list_schemas.gen'; - -export type PatchListItemRequestBody = z.infer<typeof PatchListItemRequestBody>; -export const PatchListItemRequestBody = z.object({ - id: ListItemId, - value: ListItemValue.optional(), - meta: ListItemMetadata.optional(), - _version: z.string().optional(), - /** - * Determines when changes made by the request are made visible to search - */ - refresh: z.enum(['true', 'false', 'wait_for']).optional(), -}); -export type PatchListItemRequestBodyInput = z.input<typeof PatchListItemRequestBody>; - -export type PatchListItemResponse = z.infer<typeof PatchListItemResponse>; -export const PatchListItemResponse = ListItem; diff --git a/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.schema.yaml b/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.schema.yaml deleted file mode 100644 index f79efc4691dd..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.schema.yaml +++ /dev/null @@ -1,76 +0,0 @@ -openapi: 3.0.0 -info: - title: Patch list item API endpoint - version: '2023-10-31' -paths: - /api/lists/items: - patch: - x-labels: [serverless, ess] - operationId: PatchListItem - x-codegen-enabled: true - summary: Patch a list item - description: Update specific fields of an existing list item using the list item ID. - requestBody: - description: List item's properties - required: true - content: - application/json: - schema: - type: object - properties: - id: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemId' - value: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemValue' - meta: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemMetadata' - _version: - type: string - refresh: - type: string - enum: - - 'true' - - 'false' - - wait_for - description: Determines when changes made by the request are made visible to search - required: - - id - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List item not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/quickstart_client.gen.ts b/packages/kbn-securitysolution-lists-common/api/quickstart_client.gen.ts deleted file mode 100644 index 7bf343d935f2..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/quickstart_client.gen.ts +++ /dev/null @@ -1,430 +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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Lists API client for quickstart - * version: Bundle (no version) - */ - -import type { KbnClient } from '@kbn/test'; -import { ToolingLog } from '@kbn/tooling-log'; -import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; -import { catchAxiosErrorFormatAndThrow } from '@kbn/securitysolution-utils'; - -import type { CreateListIndexResponse } from './create_list_index/create_list_index.gen'; -import type { - CreateListItemRequestBodyInput, - CreateListItemResponse, -} from './create_list_item/create_list_item.gen'; -import type { CreateListRequestBodyInput, CreateListResponse } from './create_list/create_list.gen'; -import type { DeleteListIndexResponse } from './delete_list_index/delete_list_index.gen'; -import type { - DeleteListItemRequestQueryInput, - DeleteListItemResponse, -} from './delete_list_item/delete_list_item.gen'; -import type { - DeleteListRequestQueryInput, - DeleteListResponse, -} from './delete_list/delete_list.gen'; -import type { ExportListItemsRequestQueryInput } from './export_list_items/export_list_items.gen'; -import type { - FindListItemsRequestQueryInput, - FindListItemsResponse, -} from './find_list_items/find_list_items.gen'; -import type { FindListsRequestQueryInput, FindListsResponse } from './find_lists/find_lists.gen'; -import type { - ImportListItemsRequestQueryInput, - ImportListItemsResponse, -} from './import_list_items/import_list_items.gen'; -import type { - PatchListItemRequestBodyInput, - PatchListItemResponse, -} from './patch_list_item/patch_list_item.gen'; -import type { PatchListRequestBodyInput, PatchListResponse } from './patch_list/patch_list.gen'; -import type { ReadListIndexResponse } from './read_list_index/read_list_index.gen'; -import type { - ReadListItemRequestQueryInput, - ReadListItemResponse, -} from './read_list_item/read_list_item.gen'; -import type { ReadListPrivilegesResponse } from './read_list_privileges/read_list_privileges.gen'; -import type { ReadListRequestQueryInput, ReadListResponse } from './read_list/read_list.gen'; -import type { - UpdateListItemRequestBodyInput, - UpdateListItemResponse, -} from './update_list_item/update_list_item.gen'; -import type { UpdateListRequestBodyInput, UpdateListResponse } from './update_list/update_list.gen'; - -export interface ClientOptions { - kbnClient: KbnClient; - log: ToolingLog; -} - -export class Client { - readonly kbnClient: KbnClient; - readonly log: ToolingLog; - - constructor(options: ClientOptions) { - this.kbnClient = options.kbnClient; - this.log = options.log; - } - /** - * Create a new list. - */ - async createList(props: CreateListProps) { - this.log.info(`${new Date().toISOString()} Calling API CreateList`); - return this.kbnClient - .request<CreateListResponse>({ - path: '/api/lists', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Create `.lists` and `.items` data streams in the relevant space. - */ - async createListIndex() { - this.log.info(`${new Date().toISOString()} Calling API CreateListIndex`); - return this.kbnClient - .request<CreateListIndexResponse>({ - path: '/api/lists/index', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Create a list item and associate it with the specified list. - -All list items in the same list must be the same type. For example, each list item in an `ip` list must define a specific IP address. -> info -> Before creating a list item, you must create a list. - - */ - async createListItem(props: CreateListItemProps) { - this.log.info(`${new Date().toISOString()} Calling API CreateListItem`); - return this.kbnClient - .request<CreateListItemResponse>({ - path: '/api/lists/items', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Delete a list using the list ID. -> info -> When you delete a list, all of its list items are also deleted. - - */ - async deleteList(props: DeleteListProps) { - this.log.info(`${new Date().toISOString()} Calling API DeleteList`); - return this.kbnClient - .request<DeleteListResponse>({ - path: '/api/lists', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'DELETE', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Delete the `.lists` and `.items` data streams. - */ - async deleteListIndex() { - this.log.info(`${new Date().toISOString()} Calling API DeleteListIndex`); - return this.kbnClient - .request<DeleteListIndexResponse>({ - path: '/api/lists/index', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'DELETE', - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Delete a list item using its `id`, or its `list_id` and `value` fields. - */ - async deleteListItem(props: DeleteListItemProps) { - this.log.info(`${new Date().toISOString()} Calling API DeleteListItem`); - return this.kbnClient - .request<DeleteListItemResponse>({ - path: '/api/lists/items', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'DELETE', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Export list item values from the specified list. - */ - async exportListItems(props: ExportListItemsProps) { - this.log.info(`${new Date().toISOString()} Calling API ExportListItems`); - return this.kbnClient - .request({ - path: '/api/lists/items/_export', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Get all list items in the specified list. - */ - async findListItems(props: FindListItemsProps) { - this.log.info(`${new Date().toISOString()} Calling API FindListItems`); - return this.kbnClient - .request<FindListItemsResponse>({ - path: '/api/lists/items/_find', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Get a paginated subset of lists. By default, the first page is returned, with 20 results per page. - */ - async findLists(props: FindListsProps) { - this.log.info(`${new Date().toISOString()} Calling API FindLists`); - return this.kbnClient - .request<FindListsResponse>({ - path: '/api/lists/_find', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Import list items from a TXT or CSV file. The maximum file size is 9 million bytes. - -You can import items to a new or existing list. - - */ - async importListItems(props: ImportListItemsProps) { - this.log.info(`${new Date().toISOString()} Calling API ImportListItems`); - return this.kbnClient - .request<ImportListItemsResponse>({ - path: '/api/lists/items/_import', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'POST', - body: props.attachment, - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Update specific fields of an existing list using the list ID. - */ - async patchList(props: PatchListProps) { - this.log.info(`${new Date().toISOString()} Calling API PatchList`); - return this.kbnClient - .request<PatchListResponse>({ - path: '/api/lists', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'PATCH', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Update specific fields of an existing list item using the list item ID. - */ - async patchListItem(props: PatchListItemProps) { - this.log.info(`${new Date().toISOString()} Calling API PatchListItem`); - return this.kbnClient - .request<PatchListItemResponse>({ - path: '/api/lists/items', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'PATCH', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Get the details of a list using the list ID. - */ - async readList(props: ReadListProps) { - this.log.info(`${new Date().toISOString()} Calling API ReadList`); - return this.kbnClient - .request<ReadListResponse>({ - path: '/api/lists', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Verify that `.lists` and `.items` data streams exist. - */ - async readListIndex() { - this.log.info(`${new Date().toISOString()} Calling API ReadListIndex`); - return this.kbnClient - .request<ReadListIndexResponse>({ - path: '/api/lists/index', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Get the details of a list item. - */ - async readListItem(props: ReadListItemProps) { - this.log.info(`${new Date().toISOString()} Calling API ReadListItem`); - return this.kbnClient - .request<ReadListItemResponse>({ - path: '/api/lists/items', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - - query: props.query, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - async readListPrivileges() { - this.log.info(`${new Date().toISOString()} Calling API ReadListPrivileges`); - return this.kbnClient - .request<ReadListPrivilegesResponse>({ - path: '/api/lists/privileges', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'GET', - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Update a list using the list ID. The original list is replaced, and all unspecified fields are deleted. -> info -> You cannot modify the `id` value. - - */ - async updateList(props: UpdateListProps) { - this.log.info(`${new Date().toISOString()} Calling API UpdateList`); - return this.kbnClient - .request<UpdateListResponse>({ - path: '/api/lists', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'PUT', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } - /** - * Update a list item using the list item ID. The original list item is replaced, and all unspecified fields are deleted. -> info -> You cannot modify the `id` value. - - */ - async updateListItem(props: UpdateListItemProps) { - this.log.info(`${new Date().toISOString()} Calling API UpdateListItem`); - return this.kbnClient - .request<UpdateListItemResponse>({ - path: '/api/lists/items', - headers: { - [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', - }, - method: 'PUT', - body: props.body, - }) - .catch(catchAxiosErrorFormatAndThrow); - } -} - -export interface CreateListProps { - body: CreateListRequestBodyInput; -} -export interface CreateListItemProps { - body: CreateListItemRequestBodyInput; -} -export interface DeleteListProps { - query: DeleteListRequestQueryInput; -} -export interface DeleteListItemProps { - query: DeleteListItemRequestQueryInput; -} -export interface ExportListItemsProps { - query: ExportListItemsRequestQueryInput; -} -export interface FindListItemsProps { - query: FindListItemsRequestQueryInput; -} -export interface FindListsProps { - query: FindListsRequestQueryInput; -} -export interface ImportListItemsProps { - query: ImportListItemsRequestQueryInput; - attachment: FormData; -} -export interface PatchListProps { - body: PatchListRequestBodyInput; -} -export interface PatchListItemProps { - body: PatchListItemRequestBodyInput; -} -export interface ReadListProps { - query: ReadListRequestQueryInput; -} -export interface ReadListItemProps { - query: ReadListItemRequestQueryInput; -} -export interface UpdateListProps { - body: UpdateListRequestBodyInput; -} -export interface UpdateListItemProps { - body: UpdateListItemRequestBodyInput; -} diff --git a/packages/kbn-securitysolution-lists-common/api/read_list/read_list.gen.ts b/packages/kbn-securitysolution-lists-common/api/read_list/read_list.gen.ts deleted file mode 100644 index d744eb15c9b5..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/read_list/read_list.gen.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", 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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Read list API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { ListId } from '../model/list_common.gen'; -import { List } from '../model/list_schemas.gen'; - -export type ReadListRequestQuery = z.infer<typeof ReadListRequestQuery>; -export const ReadListRequestQuery = z.object({ - /** - * List's `id` value - */ - id: ListId, -}); -export type ReadListRequestQueryInput = z.input<typeof ReadListRequestQuery>; - -export type ReadListResponse = z.infer<typeof ReadListResponse>; -export const ReadListResponse = List; diff --git a/packages/kbn-securitysolution-lists-common/api/read_list/read_list.schema.yaml b/packages/kbn-securitysolution-lists-common/api/read_list/read_list.schema.yaml deleted file mode 100644 index d932e16f528a..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/read_list/read_list.schema.yaml +++ /dev/null @@ -1,58 +0,0 @@ -openapi: 3.0.0 -info: - title: Read list API endpoint - version: '2023-10-31' -paths: - /api/lists: - get: - x-labels: [serverless, ess] - operationId: ReadList - x-codegen-enabled: true - summary: Get list details - description: Get the details of a list using the list ID. - parameters: - - name: id - in: query - required: true - description: List's `id` value - schema: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/list_schemas.schema.yaml#/components/schemas/List' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.gen.ts b/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.gen.ts deleted file mode 100644 index 8a5068f72f23..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.gen.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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Read list DS existence status API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -export type ReadListIndexResponse = z.infer<typeof ReadListIndexResponse>; -export const ReadListIndexResponse = z.object({ - list_index: z.boolean(), - list_item_index: z.boolean(), -}); diff --git a/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.schema.yaml b/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.schema.yaml deleted file mode 100644 index b67526460015..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.schema.yaml +++ /dev/null @@ -1,57 +0,0 @@ -openapi: 3.0.0 -info: - title: Read list DS existence status API endpoint - version: '2023-10-31' -paths: - /api/lists/index: - get: - x-labels: [serverless, ess] - operationId: ReadListIndex - x-codegen-enabled: true - summary: Get status of list data streams - description: Verify that `.lists` and `.items` data streams exist. - responses: - 200: - description: Successful response - content: - application/json: - schema: - type: object - properties: - list_index: - type: boolean - list_item_index: - type: boolean - required: [list_index, list_item_index] - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List data stream(s) not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.gen.ts b/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.gen.ts deleted file mode 100644 index cd0c1d8fca26..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.gen.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", 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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Read list item API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { ListId } from '../model/list_common.gen'; -import { ListItem } from '../model/list_schemas.gen'; - -export type ReadListItemRequestQuery = z.infer<typeof ReadListItemRequestQuery>; -export const ReadListItemRequestQuery = z.object({ - /** - * Required if `list_id` and `value` are not specified - */ - id: ListId.optional(), - /** - * Required if `id` is not specified - */ - list_id: ListId.optional(), - /** - * Required if `id` is not specified - */ - value: z.string().optional(), -}); -export type ReadListItemRequestQueryInput = z.input<typeof ReadListItemRequestQuery>; - -export type ReadListItemResponse = z.infer<typeof ReadListItemResponse>; -export const ReadListItemResponse = z.union([ListItem, z.array(ListItem)]); diff --git a/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.schema.yaml b/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.schema.yaml deleted file mode 100644 index 4d686f5452e0..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.schema.yaml +++ /dev/null @@ -1,74 +0,0 @@ -openapi: 3.0.0 -info: - title: Read list item API endpoint - version: '2023-10-31' -paths: - /api/lists/items: - get: - x-labels: [serverless, ess] - operationId: ReadListItem - x-codegen-enabled: true - summary: Get a list item - description: Get the details of a list item. - parameters: - - name: id - in: query - required: false - description: Required if `list_id` and `value` are not specified - schema: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - - name: list_id - in: query - required: false - description: Required if `id` is not specified - schema: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - - name: value - in: query - required: false - description: Required if `id` is not specified - schema: - type: string - responses: - 200: - description: Successful response - content: - application/json: - schema: - oneOf: - - $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' - - type: array - items: - $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List item not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/update_list/update_list.gen.ts b/packages/kbn-securitysolution-lists-common/api/update_list/update_list.gen.ts deleted file mode 100644 index f5eb085fe4aa..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/update_list/update_list.gen.ts +++ /dev/null @@ -1,36 +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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Update list API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { ListId, ListName, ListDescription, ListMetadata } from '../model/list_common.gen'; -import { List } from '../model/list_schemas.gen'; - -export type UpdateListRequestBody = z.infer<typeof UpdateListRequestBody>; -export const UpdateListRequestBody = z.object({ - id: ListId, - name: ListName, - description: ListDescription, - meta: ListMetadata.optional(), - version: z.number().int().min(1).optional(), - _version: z.string().optional(), -}); -export type UpdateListRequestBodyInput = z.input<typeof UpdateListRequestBody>; - -export type UpdateListResponse = z.infer<typeof UpdateListResponse>; -export const UpdateListResponse = List; diff --git a/packages/kbn-securitysolution-lists-common/api/update_list/update_list.schema.yaml b/packages/kbn-securitysolution-lists-common/api/update_list/update_list.schema.yaml deleted file mode 100644 index c41b52427b63..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/update_list/update_list.schema.yaml +++ /dev/null @@ -1,79 +0,0 @@ -openapi: 3.0.0 -info: - title: Update list API endpoint - version: '2023-10-31' -paths: - /api/lists: - put: - x-labels: [serverless, ess] - operationId: UpdateList - x-codegen-enabled: true - summary: Update a list - description: | - Update a list using the list ID. The original list is replaced, and all unspecified fields are deleted. - > info - > You cannot modify the `id` value. - requestBody: - description: List's properties - required: true - content: - application/json: - schema: - type: object - properties: - id: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' - name: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListName' - description: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListDescription' - meta: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListMetadata' - version: - type: integer - minimum: 1 - _version: - type: string - required: - - id - - name - - description - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/list_schemas.schema.yaml#/components/schemas/List' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.gen.ts b/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.gen.ts deleted file mode 100644 index 052c4c979e84..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.gen.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", 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". - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Update list item API endpoint - * version: 2023-10-31 - */ - -import { z } from '@kbn/zod'; - -import { ListItemId, ListItemValue, ListItemMetadata } from '../model/list_common.gen'; -import { ListItem } from '../model/list_schemas.gen'; - -export type UpdateListItemRequestBody = z.infer<typeof UpdateListItemRequestBody>; -export const UpdateListItemRequestBody = z.object({ - id: ListItemId, - value: ListItemValue, - meta: ListItemMetadata.optional(), - _version: z.string().optional(), -}); -export type UpdateListItemRequestBodyInput = z.input<typeof UpdateListItemRequestBody>; - -export type UpdateListItemResponse = z.infer<typeof UpdateListItemResponse>; -export const UpdateListItemResponse = ListItem; diff --git a/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.schema.yaml b/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.schema.yaml deleted file mode 100644 index 6b05e01f35aa..000000000000 --- a/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.schema.yaml +++ /dev/null @@ -1,73 +0,0 @@ -openapi: 3.0.0 -info: - title: Update list item API endpoint - version: '2023-10-31' -paths: - /api/lists/items: - put: - x-labels: [serverless, ess] - operationId: UpdateListItem - x-codegen-enabled: true - summary: Update a list item - description: | - Update a list item using the list item ID. The original list item is replaced, and all unspecified fields are deleted. - > info - > You cannot modify the `id` value. - requestBody: - description: List item's properties - required: true - content: - application/json: - schema: - type: object - properties: - id: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemId' - value: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemValue' - meta: - $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemMetadata' - _version: - type: string - required: - - id - - value - responses: - 200: - description: Successful response - content: - application/json: - schema: - $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' - 400: - description: Invalid input data response - content: - application/json: - schema: - oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 401: - description: Unsuccessful authentication response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 403: - description: Not enough privileges response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - 404: - description: List item not found response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' - 500: - description: Internal server error response - content: - application/json: - schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/package.json b/packages/kbn-securitysolution-lists-common/package.json deleted file mode 100644 index 624c2709ea73..000000000000 --- a/packages/kbn-securitysolution-lists-common/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "description": "Security Solution Lists common package", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "name": "@kbn/securitysolution-lists-common", - "private": true, - "version": "1.0.0", - "scripts": { - "openapi:generate": "node scripts/openapi_generate", - "openapi:bundle": "node scripts/openapi_bundle" - } -} diff --git a/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle.js b/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle.js deleted file mode 100644 index 7a6172475917..000000000000 --- a/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle.js +++ /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". - */ - -require('../../../src/setup_node_env'); -const { join, resolve } = require('path'); -const { bundle } = require('@kbn/openapi-bundler'); - -const ROOT = resolve(__dirname, '..'); - -(async () => { - await bundle({ - sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), - outputFilePath: join( - ROOT, - 'docs/openapi/serverless/security_solution_lists_api_{version}.bundled.schema.yaml' - ), - options: { - includeLabels: ['serverless'], - prototypeDocument: join(ROOT, 'scripts/openapi_bundle_info/lists_serverless.info.yaml'), - }, - }); - - await bundle({ - sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), - outputFilePath: join( - ROOT, - 'docs/openapi/ess/security_solution_lists_api_{version}.bundled.schema.yaml' - ), - options: { - includeLabels: ['ess'], - prototypeDocument: join(ROOT, 'scripts/openapi_bundle_info/lists_ess.info.yaml'), - }, - }); -})(); diff --git a/packages/kbn-securitysolution-lists-common/scripts/openapi_generate.js b/packages/kbn-securitysolution-lists-common/scripts/openapi_generate.js deleted file mode 100644 index 7adc36ed32ca..000000000000 --- a/packages/kbn-securitysolution-lists-common/scripts/openapi_generate.js +++ /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". - */ - -require('../../../src/setup_node_env'); -const { join, resolve } = require('path'); -const { generate } = require('@kbn/openapi-generator'); -const { REPO_ROOT } = require('@kbn/repo-info'); - -const ROOT = resolve(__dirname, '..'); - -(async () => { - await generate({ - title: 'OpenAPI Lists API Schemas', - rootDir: ROOT, - sourceGlob: './api/**/*.schema.yaml', - templateName: 'zod_operation_schema', - }); - - await generate({ - title: 'Lists API client for tests', - rootDir: ROOT, - sourceGlob: './api/**/*.schema.yaml', - templateName: 'api_client_supertest', - skipLinting: true, - bundle: { - outFile: join( - REPO_ROOT, - 'x-pack/test/api_integration/services/security_solution_lists_api.gen.ts' - ), - }, - }); - - await generate({ - title: 'Lists API client for quickstart', - rootDir: ROOT, - sourceGlob: './api/**/*.schema.yaml', - templateName: 'api_client_quickstart', - skipLinting: true, - bundle: { - outFile: join( - REPO_ROOT, - 'packages/kbn-securitysolution-lists-common/api/quickstart_client.gen.ts' - ), - }, - }); -})(); diff --git a/packages/kbn-securitysolution-lists-common/tsconfig.json b/packages/kbn-securitysolution-lists-common/tsconfig.json deleted file mode 100644 index e8149be08355..000000000000 --- a/packages/kbn-securitysolution-lists-common/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "outDir": "target/types", - "types": ["jest", "node"] - }, - "exclude": ["target/**/*"], - "extends": "../../tsconfig.base.json", - "include": ["**/*.ts"], - "kbn_references": [ - "@kbn/zod-helpers", - "@kbn/openapi-common", - "@kbn/test", - "@kbn/tooling-log", - "@kbn/core-http-common", - "@kbn/securitysolution-utils", - "@kbn/zod", - ] -} diff --git a/packages/kbn-securitysolution-rules/tsconfig.json b/packages/kbn-securitysolution-rules/tsconfig.json deleted file mode 100644 index 9bd4f35cf62a..000000000000 --- a/packages/kbn-securitysolution-rules/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts" - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-t-grid/index.ts b/packages/kbn-securitysolution-t-grid/index.ts deleted file mode 100644 index fb5ebc443ccd..000000000000 --- a/packages/kbn-securitysolution-t-grid/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './src/constants'; -export * from './src/utils'; -// eslint-disable-next-line @kbn/imports/no_boundary_crossing -export * from './src/mock'; diff --git a/packages/kbn-securitysolution-t-grid/package.json b/packages/kbn-securitysolution-t-grid/package.json deleted file mode 100644 index b262a21e7dc6..000000000000 --- a/packages/kbn-securitysolution-t-grid/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "@kbn/securitysolution-t-grid", - "version": "1.0.0", - "description": "security solution t-grid packages will allow sharing components between timelines and security_solution plugin until we transfer all functionality to timelines plugin", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "private": true -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-t-grid/src/constants/index.ts b/packages/kbn-securitysolution-t-grid/src/constants/index.ts deleted file mode 100644 index 3ff541240a8c..000000000000 --- a/packages/kbn-securitysolution-t-grid/src/constants/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export const HIGHLIGHTED_DROP_TARGET_CLASS_NAME = 'highlighted-drop-target'; -export const EMPTY_PROVIDERS_GROUP_CLASS_NAME = 'empty-providers-group'; - -/** The draggable will move this many pixels via the keyboard when the arrow key is pressed */ -export const KEYBOARD_DRAG_OFFSET = 20; - -export const DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME = 'draggable-keyboard-wrapper'; - -export const ROW_RENDERER_CLASS_NAME = 'row-renderer'; - -export const NOTES_CONTAINER_CLASS_NAME = 'notes-container'; - -export const NOTE_CONTENT_CLASS_NAME = 'note-content'; - -/** This class is added to the document body while dragging */ -export const IS_DRAGGING_CLASS_NAME = 'is-dragging'; - -export const HOVER_ACTIONS_ALWAYS_SHOW_CLASS_NAME = 'hover-actions-always-show'; diff --git a/packages/kbn-securitysolution-t-grid/src/mock/index.ts b/packages/kbn-securitysolution-t-grid/src/mock/index.ts deleted file mode 100644 index 51a5726d914f..000000000000 --- a/packages/kbn-securitysolution-t-grid/src/mock/index.ts +++ /dev/null @@ -1,10 +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". - */ - -export * from './mock_event_details'; diff --git a/packages/kbn-securitysolution-t-grid/src/utils/api/index.ts b/packages/kbn-securitysolution-t-grid/src/utils/api/index.ts deleted file mode 100644 index 8f7588fdeafd..000000000000 --- a/packages/kbn-securitysolution-t-grid/src/utils/api/index.ts +++ /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 { has } from 'lodash/fp'; - -export interface AppError extends Error { - body: { - message: string; - }; -} - -export interface KibanaError extends AppError { - body: { - message: string; - statusCode: number; - }; -} - -export interface SecurityAppError extends AppError { - body: { - message: string; - status_code: number; - }; -} - -export const isKibanaError = (error: unknown): error is KibanaError => - has('message', error) && has('body.message', error) && has('body.statusCode', error); - -export const isSecurityAppError = (error: unknown): error is SecurityAppError => - has('message', error) && has('body.message', error) && has('body.status_code', error); - -export const isAppError = (error: unknown): error is AppError => - isKibanaError(error) || isSecurityAppError(error); - -export const isNotFoundError = (error: unknown) => - (isKibanaError(error) && error.body.statusCode === 404) || - (isSecurityAppError(error) && error.body.status_code === 404); diff --git a/packages/kbn-securitysolution-t-grid/src/utils/drag_and_drop/index.ts b/packages/kbn-securitysolution-t-grid/src/utils/drag_and_drop/index.ts deleted file mode 100644 index 9b7f4bf6ed7d..000000000000 --- a/packages/kbn-securitysolution-t-grid/src/utils/drag_and_drop/index.ts +++ /dev/null @@ -1,134 +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 { DropResult } from '@hello-pangea/dnd'; - -export const draggableIdPrefix = 'draggableId'; - -export const droppableIdPrefix = 'droppableId'; - -export const draggableContentPrefix = `${draggableIdPrefix}.content.`; - -export const draggableTimelineProvidersPrefix = `${draggableIdPrefix}.timelineProviders.`; - -export const draggableFieldPrefix = `${draggableIdPrefix}.field.`; - -export const droppableContentPrefix = `${droppableIdPrefix}.content.`; - -export const droppableFieldPrefix = `${droppableIdPrefix}.field.`; - -export const droppableTimelineProvidersPrefix = `${droppableIdPrefix}.timelineProviders.`; - -export const droppableTimelineColumnsPrefix = `${droppableIdPrefix}.timelineColumns.`; - -export const droppableTimelineFlyoutBottomBarPrefix = `${droppableIdPrefix}.flyoutButton.`; - -export const getDraggableId = (dataProviderId: string): string => - `${draggableContentPrefix}${dataProviderId}`; - -export const getDraggableFieldId = ({ - contextId, - fieldId, -}: { - contextId: string; - fieldId: string; -}): string => `${draggableFieldPrefix}${escapeContextId(contextId)}.${escapeFieldId(fieldId)}`; - -export const getTimelineProviderDroppableId = ({ - groupIndex, - timelineId, -}: { - groupIndex: number; - timelineId: string; -}): string => `${droppableTimelineProvidersPrefix}${timelineId}.group.${groupIndex}`; - -export const getTimelineProviderDraggableId = ({ - dataProviderId, - groupIndex, - timelineId, -}: { - dataProviderId: string; - groupIndex: number; - timelineId: string; -}): string => - `${draggableTimelineProvidersPrefix}${timelineId}.group.${groupIndex}.${dataProviderId}`; - -export const getDroppableId = (visualizationPlaceholderId: string): string => - `${droppableContentPrefix}${visualizationPlaceholderId}`; - -export const sourceIsContent = (result: DropResult): boolean => - result.source.droppableId.startsWith(droppableContentPrefix); - -export const sourceAndDestinationAreSameTimelineProviders = (result: DropResult): boolean => { - const regex = /^droppableId\.timelineProviders\.(\S+)\./; - const sourceMatches = result.source.droppableId.match(regex) || []; - const destinationMatches = - (result.destination && result.destination.droppableId.match(regex)) || []; - - return ( - sourceMatches.length >= 2 && - destinationMatches.length >= 2 && - sourceMatches[1] === destinationMatches[1] - ); -}; - -export const draggableIsContent = (result: DropResult | { draggableId: string }): boolean => - result.draggableId.startsWith(draggableContentPrefix); - -export const draggableIsField = (result: DropResult | { draggableId: string }): boolean => - result.draggableId.startsWith(draggableFieldPrefix); - -export const reasonIsDrop = (result: DropResult): boolean => result.reason === 'DROP'; - -export const destinationIsTimelineProviders = (result: DropResult): boolean => - result.destination != null && - result.destination.droppableId.startsWith(droppableTimelineProvidersPrefix); - -export const destinationIsTimelineColumns = (result: DropResult): boolean => - result.destination != null && - result.destination.droppableId.startsWith(droppableTimelineColumnsPrefix); - -export const destinationIsTimelineButton = (result: DropResult): boolean => - result.destination != null && - result.destination.droppableId.startsWith(droppableTimelineFlyoutBottomBarPrefix); - -export const getProviderIdFromDraggable = (result: DropResult): string => - result.draggableId.substring(result.draggableId.lastIndexOf('.') + 1); - -export const getFieldIdFromDraggable = (result: DropResult): string => - unEscapeFieldId(result.draggableId.substring(result.draggableId.lastIndexOf('.') + 1)); - -export const escapeDataProviderId = (path: string) => path.replace(/\./g, '_'); - -export const escapeContextId = (path: string) => path.replace(/\./g, '_'); - -export const escapeFieldId = (path: string) => path.replace(/\./g, '!!!DOT!!!'); - -export const unEscapeFieldId = (path: string) => path.replace(/!!!DOT!!!/g, '.'); - -export const providerWasDroppedOnTimeline = (result: DropResult): boolean => - reasonIsDrop(result) && - draggableIsContent(result) && - sourceIsContent(result) && - destinationIsTimelineProviders(result); - -export const userIsReArrangingProviders = (result: DropResult): boolean => - reasonIsDrop(result) && sourceAndDestinationAreSameTimelineProviders(result); - -export const fieldWasDroppedOnTimelineColumns = (result: DropResult): boolean => - reasonIsDrop(result) && draggableIsField(result) && destinationIsTimelineColumns(result); - -/** - * Prevents fields from being dragged or dropped to any area other than column - * header drop zone in the timeline - */ -export const DRAG_TYPE_FIELD = 'drag-type-field'; - -/** This class is added to the document body while timeline field dragging */ -export const IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME = 'is-timeline-field-dragging'; diff --git a/packages/kbn-securitysolution-t-grid/src/utils/index.ts b/packages/kbn-securitysolution-t-grid/src/utils/index.ts deleted file mode 100644 index c921565845e9..000000000000 --- a/packages/kbn-securitysolution-t-grid/src/utils/index.ts +++ /dev/null @@ -1,11 +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". - */ - -export * from './api'; -export * from './drag_and_drop'; diff --git a/packages/kbn-securitysolution-t-grid/tsconfig.json b/packages/kbn-securitysolution-t-grid/tsconfig.json deleted file mode 100644 index 9bd4f35cf62a..000000000000 --- a/packages/kbn-securitysolution-t-grid/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts" - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-securitysolution-utils/index.ts b/packages/kbn-securitysolution-utils/index.ts deleted file mode 100644 index 8769a281e220..000000000000 --- a/packages/kbn-securitysolution-utils/index.ts +++ /dev/null @@ -1,15 +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". - */ - -export * from './src/add_remove_id_to_item'; -export * from './src/axios'; -export * from './src/transform_data_to_ndjson'; -export * from './src/path_validations'; -export * from './src/esql'; -export * from './src/debounce_async/debounce_async'; diff --git a/packages/kbn-securitysolution-utils/jest.config.js b/packages/kbn-securitysolution-utils/jest.config.js deleted file mode 100644 index 43e3bc62c156..000000000000 --- a/packages/kbn-securitysolution-utils/jest.config.js +++ /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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['<rootDir>/packages/kbn-securitysolution-utils'], -}; diff --git a/packages/kbn-securitysolution-utils/package.json b/packages/kbn-securitysolution-utils/package.json deleted file mode 100644 index 2e18ecbc7642..000000000000 --- a/packages/kbn-securitysolution-utils/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@kbn/securitysolution-utils", - "version": "1.0.0", - "description": "security solution utilities to use across plugins such lists, security_solution, cases, etc...", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "private": true, - "sideEffects": false -} \ No newline at end of file diff --git a/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.test.ts b/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.test.ts deleted file mode 100644 index 462f1245500e..000000000000 --- a/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.test.ts +++ /dev/null @@ -1,79 +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 { addIdToItem, removeIdFromItem } from '.'; - -jest.mock('uuid', () => ({ - v4: jest.fn().mockReturnValue('123'), -})); - -describe('add_remove_id_to_item', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('addIdToItem', () => { - test('it adds an id to an empty item', () => { - expect(addIdToItem({})).toEqual({ id: '123' }); - }); - - test('it adds a complex object', () => { - expect( - addIdToItem({ - field: '', - type: 'mapping', - value: '', - }) - ).toEqual({ - id: '123', - field: '', - type: 'mapping', - value: '', - }); - }); - - test('it adds an id to an existing item', () => { - expect(addIdToItem({ test: '456' })).toEqual({ id: '123', test: '456' }); - }); - - test('it does not change the id if it already exists', () => { - expect(addIdToItem({ id: '456' })).toEqual({ id: '456' }); - }); - - test('it returns the same reference if it has an id already', () => { - const obj = { id: '456' }; - expect(addIdToItem(obj)).toBe(obj); - }); - - test('it returns a new reference if it adds an id to an item', () => { - const obj = { test: '456' }; - expect(addIdToItem(obj)).not.toBe(obj); - }); - }); - - describe('removeIdFromItem', () => { - test('it removes an id from an item', () => { - expect(removeIdFromItem({ id: '456' })).toEqual({}); - }); - - test('it returns a new reference if it removes an id from an item', () => { - const obj = { id: '123', test: '456' }; - expect(removeIdFromItem(obj)).not.toBe(obj); - }); - - test('it does not effect an item without an id', () => { - expect(removeIdFromItem({ test: '456' })).toEqual({ test: '456' }); - }); - - test('it returns the same reference if it does not have an id already', () => { - const obj = { test: '456' }; - expect(removeIdFromItem(obj)).toBe(obj); - }); - }); -}); diff --git a/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.ts b/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.ts deleted file mode 100644 index b01a0f7db1ec..000000000000 --- a/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.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 { v4 as uuidv4 } from 'uuid'; - -/** - * This is useful for when you have arrays without an ID and need to add one for - * ReactJS keys. I break the types slightly by introducing an id to an arbitrary item - * but then cast it back to the regular type T. - * Usage of this could be considered tech debt as I am adding an ID when the backend - * could be doing the same thing but it depends on how you want to model your data and - * if you view modeling your data with id's to please ReactJS a good or bad thing. - * @param item The item to add an id to. - */ -type NotArray<T> = T extends unknown[] ? never : T; -export const addIdToItem = <T>(item: NotArray<T>): T => { - const maybeId = item as typeof item & { id?: string }; - if (maybeId.id != null) { - return item; - } else { - return { ...item, id: uuidv4() }; - } -}; - -/** - * This is to reverse the id you added to your arrays for ReactJS keys. - * @param item The item to remove the id from. - */ -export const removeIdFromItem = <T>( - item: NotArray<T> -): - | T - | Pick< - T & { - id?: string | undefined; - }, - Exclude<keyof T, 'id'> - > => { - const maybeId = item as typeof item & { id?: string }; - if (maybeId.id != null) { - const { id, ...noId } = maybeId; - return noId; - } else { - return item; - } -}; diff --git a/packages/kbn-securitysolution-utils/src/axios/index.ts b/packages/kbn-securitysolution-utils/src/axios/index.ts deleted file mode 100644 index c5c0bbe9d1fd..000000000000 --- a/packages/kbn-securitysolution-utils/src/axios/index.ts +++ /dev/null @@ -1,72 +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 { AxiosError } from 'axios'; - -export class FormattedAxiosError extends Error { - public readonly request: { - method: string; - url: string; - data: unknown; - }; - public readonly response: { - status: number; - statusText: string; - data: any; - }; - - constructor(axiosError: AxiosError) { - const method = axiosError.config?.method ?? ''; - const url = axiosError.config?.url ?? ''; - - super( - `${axiosError.message}${ - axiosError?.response?.data ? `: ${JSON.stringify(axiosError?.response?.data)}` : '' - }${url ? `\n(Request: ${method} ${url})` : ''}` - ); - - this.request = { - method, - url, - data: axiosError.config?.data ?? '', - }; - - this.response = { - status: axiosError?.response?.status ?? 0, - statusText: axiosError?.response?.statusText ?? '', - data: axiosError?.response?.data, - }; - - this.name = this.constructor.name; - } - - toJSON() { - return { - message: this.message, - request: this.request, - response: this.response, - }; - } - - toString() { - return JSON.stringify(this.toJSON(), null, 2); - } -} - -/** - * Used with `promise.catch()`, it will format the Axios error to a new error and will re-throw - * @param error - */ -export const catchAxiosErrorFormatAndThrow = (error: Error): never => { - if (error instanceof AxiosError) { - throw new FormattedAxiosError(error); - } - - throw error; -}; diff --git a/packages/kbn-securitysolution-utils/src/client_concurrency/index.ts b/packages/kbn-securitysolution-utils/src/client_concurrency/index.ts deleted file mode 100644 index d3a479b44102..000000000000 --- a/packages/kbn-securitysolution-utils/src/client_concurrency/index.ts +++ /dev/null @@ -1,37 +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 limit from 'p-limit'; - -/** - * This type is just an async function's type - */ -type RequestFactory<Output> = () => Promise<Output>; - -/** - * Helper function to call a large number of async functions with limited concurrency. - * Example pattern of how to create functions to pass in: - * - * const ruleCopies = duplicateRuleParams(basicRule, 200); - * const functions = ruleCopies.map((rule) => () => detectionsClient.createRule({ body: rule })); - * - * Note that the `map` call in the example returns a *function* that calls detectionsClient.createRule, it doesn't call createRule immediately. - * - * @param functions Async functions to call with limited concurrency - * @param concurrency Maximum number of concurrent function calls - * @returns Results from all functions passed in - */ -export const concurrentlyExec = async <Output>( - requestFactories: Array<RequestFactory<Output>>, - concurrency: number = 10 -) => { - const limiter = limit(concurrency); - const promises = requestFactories.map((f) => limiter(f)); - return Promise.all(promises); -}; diff --git a/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.ts b/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.ts deleted file mode 100644 index 99fe653b0e21..000000000000 --- a/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.ts +++ /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". - */ - -/** - * Unlike lodash's debounce, which resolves intermediate calls with the most - * recent value, this implementation waits to resolve intermediate calls until - * the next invocation resolves. - * - * @param fn an async function - * - * @returns A debounced async function that resolves on the next invocation - */ -export function debounceAsync<Args extends unknown[], Result>( - fn: (...args: Args) => Result, - intervalMs: number -): (...args: Args) => Promise<Awaited<Result>> { - let timeoutId: ReturnType<typeof setTimeout> | undefined; - let resolve: (value: Awaited<Result>) => void; - let promise = new Promise<Awaited<Result>>((_resolve) => { - resolve = _resolve; - }); - - return (...args) => { - if (timeoutId) { - clearTimeout(timeoutId); - } - - timeoutId = setTimeout(async () => { - resolve(await fn(...args)); - promise = new Promise((_resolve) => { - resolve = _resolve; - }); - }, intervalMs); - - return promise; - }; -} diff --git a/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts b/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts deleted file mode 100644 index c9b8474b5596..000000000000 --- a/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 ESQLAstQueryExpression, parse } from '@kbn/esql-ast'; - -export const isAggregatingQuery = (astExpression: ESQLAstQueryExpression): boolean => - astExpression.commands.some((command) => command.name === 'stats'); - -/** - * compute if esqlQuery is aggregating/grouping, i.e. using STATS...BY command - * @param esqlQuery - * @returns boolean - */ -export const computeIsESQLQueryAggregating = (esqlQuery: string): boolean => { - const { root } = parse(esqlQuery); - return isAggregatingQuery(root); -}; diff --git a/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.ts b/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.ts deleted file mode 100644 index 7cf69b409d5d..000000000000 --- a/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.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", 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 { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; - -/** - * parses ES|QL query and returns array of indices - */ -export const getIndexListFromEsqlQuery = (query: string | undefined): string[] => { - try { - const indexString = getIndexPatternFromESQLQuery(query); - - return getIndexListFromIndexString(indexString); - } catch (e) { - return []; - } -}; - -/** - * transforms sting of indices, separated by commas to array - * index*, index2* => [index*, index2*] - */ -export const getIndexListFromIndexString = (indexString: string | undefined): string[] => { - if (!indexString) { - return []; - } - return indexString.split(',').map((index) => index.trim()); -}; diff --git a/packages/kbn-securitysolution-utils/src/esql/index.ts b/packages/kbn-securitysolution-utils/src/esql/index.ts deleted file mode 100644 index 930ff246988e..000000000000 --- a/packages/kbn-securitysolution-utils/src/esql/index.ts +++ /dev/null @@ -1,12 +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". - */ - -export * from './compute_if_esql_query_aggregating'; -export * from './get_index_list_from_esql_query'; -export * from './parse_esql_query'; diff --git a/packages/kbn-securitysolution-utils/src/path_validations/index.test.ts b/packages/kbn-securitysolution-utils/src/path_validations/index.test.ts deleted file mode 100644 index e6dfded20007..000000000000 --- a/packages/kbn-securitysolution-utils/src/path_validations/index.test.ts +++ /dev/null @@ -1,760 +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 { - isPathValid, - hasSimpleExecutableName, - OperatingSystem, - ConditionEntryField, - validateWildcardInput, - validateHasWildcardWithWrongOperator, - validatePotentialWildcardInput, - validateFilePathInput, - WILDCARD_WARNING, - FILEPATH_WARNING, -} from '.'; - -describe('validatePotentialWildcardInput', () => { - it('warns on wildcard when field is file.path.text', () => { - expect( - validatePotentialWildcardInput({ - field: 'file.path.text', - os: OperatingSystem.WINDOWS, - value: 'c:\\path*.exe', - }) - ).toEqual(WILDCARD_WARNING); - }); - it('warns on wildcard when field is not file.path.text', () => { - expect( - validatePotentialWildcardInput({ - field: 'event.category', - os: OperatingSystem.WINDOWS, - value: 'some*value', - }) - ).toEqual(WILDCARD_WARNING); - }); -}); - -describe('validateWildcardInput', () => { - it('warns on wildcard for fields that are not file paths', () => { - expect(validateWildcardInput('*')).toEqual(WILDCARD_WARNING); - }); - it('does not warn if no wildcard', () => { - expect(validateWildcardInput('non-wildcard')).toEqual(undefined); - }); -}); - -describe('validateFilePathInput', () => { - describe('windows', () => { - const os = OperatingSystem.WINDOWS; - - it('does not warn on valid filenames', () => { - expect( - validateFilePathInput({ - os, - value: 'C:\\Windows\\*\\FILENAME.EXE-1231205124.gz', - }) - ).not.toBeDefined(); - expect( - validateFilePathInput({ - os, - value: "C:\\Windows\\*\\test$ as2@13---12!@#A,DS.#$^&$!#~ 'as'd.华语.txt", - }) - ).toEqual(undefined); - }); - - it('warns on wildcard in file name at the end of the path', () => { - expect(validateFilePathInput({ os, value: 'c:\\path*.exe' })).toEqual(WILDCARD_WARNING); - expect( - validateFilePathInput({ - os, - value: 'C:\\Windows\\*\\FILENAME.EXE-*.gz', - }) - ).toEqual(WILDCARD_WARNING); - }); - - it('warns on unix paths or non-windows paths', () => { - expect(validateFilePathInput({ os, value: '/opt/bin' })).toEqual(FILEPATH_WARNING); - }); - - it('warns on malformed paths', () => { - expect(validateFilePathInput({ os, value: 'c:\\path/opt' })).toEqual(FILEPATH_WARNING); - expect(validateFilePathInput({ os, value: '1242' })).toEqual(FILEPATH_WARNING); - expect(validateFilePathInput({ os, value: 'w12efdfa' })).toEqual(FILEPATH_WARNING); - expect(validateFilePathInput({ os, value: 'c:\\folder\\' })).toEqual(FILEPATH_WARNING); - }); - }); - describe('unix paths', () => { - const os = - parseInt((Math.random() * 2).toString(), 10) === 1 - ? OperatingSystem.MAC - : OperatingSystem.LINUX; - - it('does not warn on valid filenames', () => { - expect( - validateFilePathInput({ - os, - value: '/opt/*/FILENAME.EXE-1231205124.gz', - }) - ).not.toEqual(WILDCARD_WARNING); - expect( - validateFilePathInput({ - os, - value: "/opt/*/test$ as2@13---12!@#A,DS.#$^&$!#~ 'as'd.华语.txt", - }) - ).not.toEqual(WILDCARD_WARNING); - }); - it('warns on wildcard in file name at the end of the path', () => { - expect(validateFilePathInput({ os, value: '/opt/bin*' })).toEqual(WILDCARD_WARNING); - expect(validateFilePathInput({ os, value: '/opt/FILENAME.EXE-*.gz' })).toEqual( - WILDCARD_WARNING - ); - }); - - it('warns on windows paths', () => { - expect(validateFilePathInput({ os, value: 'd:\\path\\file.exe' })).toEqual(FILEPATH_WARNING); - }); - - it('warns on malformed paths', () => { - expect(validateFilePathInput({ os, value: 'opt/bin\\file.exe' })).toEqual(FILEPATH_WARNING); - expect(validateFilePathInput({ os, value: '1242' })).toEqual(FILEPATH_WARNING); - expect(validateFilePathInput({ os, value: 'w12efdfa' })).toEqual(FILEPATH_WARNING); - expect(validateFilePathInput({ os, value: '/folder/' })).toEqual(FILEPATH_WARNING); - }); - }); -}); - -describe('Wildcard and invalid operator', () => { - it('should return TRUE when operator is not "WILDCARD" and value contains a wildcard', () => { - expect(validateHasWildcardWithWrongOperator({ operator: 'match', value: 'asdf*' })).toEqual( - true - ); - }); - it('should return FALSE when operator is not "WILDCARD" and value does not contain a wildcard', () => { - expect(validateHasWildcardWithWrongOperator({ operator: 'match', value: 'asdf' })).toEqual( - false - ); - }); - it('should return FALSE when operator is "WILDCARD" and value contains a wildcard', () => { - expect(validateHasWildcardWithWrongOperator({ operator: 'wildcard', value: 'asdf*' })).toEqual( - false - ); - }); - it('should return FALSE when operator is "WILDCARD" and value does not contain a wildcard', () => { - expect(validateHasWildcardWithWrongOperator({ operator: 'wildcard', value: 'asdf' })).toEqual( - false - ); - }); -}); - -describe('No Warnings', () => { - it('should not show warnings on non path entries ', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.HASH, - type: 'match', - value: '5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e', - }) - ).toEqual(true); - - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.SIGNER, - type: 'match', - value: '', - }) - ).toEqual(true); - }); -}); - -describe('Unacceptable Windows wildcard paths', () => { - it('should not accept paths that do not have a folder name with a wildcard ', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'c:\\folder', - }) - ).toEqual(false); - }); - - it('should not accept paths that do not have a file name with a wildcard ', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'c:\\path.exe', - }) - ).toEqual(false); - }); - - it('should not accept nested paths that do not have a wildcard', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'c:\\folder\\path.exe', - }) - ).toEqual(false); - }); - - it('should not accept paths with * wildcard and /', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'c:/**/path.exe', - }) - ).toEqual(false); - }); - - it('should not accept paths with ? wildcard and /', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'C:/?indows/pat?', - }) - ).toEqual(false); - }); -}); - -describe('Acceptable Windows wildcard paths', () => { - it('should accept wildcards for folders', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'c:\\**\\path.exe', - }) - ).toEqual(true); - }); - - it('should accept wildcards for folders and files', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'e:\\**\\*.exe', - }) - ).toEqual(true); - }); - - it('should accept paths with single wildcard', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'f:\\*', - }) - ).toEqual(true); - - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'f:\\?', - }) - ).toEqual(true); - }); - - it('should accept paths that have wildcard in filenames', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'a:\\*.*', - }) - ).toEqual(true); - }); - - it('should accept paths with ? as wildcard', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'C:\\?indows\\pat?', - }) - ).toEqual(true); - }); - - it('should accept paths with both ? and * as wildcards', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'C:\\*?', - }) - ).toEqual(true); - }); - - it('should accept paths with multiple wildcards', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'C:\\**', - }) - ).toEqual(true); - - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'C:\\??', - }) - ).toEqual(true); - }); -}); - -describe('Acceptable Windows exact paths', () => { - it('should accept paths when it ends with a folder name', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'match', - value: 'c:\\folder', - }) - ).toEqual(true); - }); - - it('should accept paths when it ends with a file name', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'match', - value: 'c:\\path.exe', - }) - ).toEqual(true); - }); - - it('should accept paths when it ends with a filename in a folder', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'match', - value: 'c:\\folder\\path.exe', - }) - ).toEqual(true); - }); -}); - -describe('Acceptable Windows exact paths with hyphens', () => { - it('should accept paths when paths have folder names with hyphens', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'match', - value: 'c:\\hype-folder-name', - }) - ).toEqual(true); - }); - - it('should accept paths when file names have hyphens', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'match', - value: 'c:\\file-name.exe', - }) - ).toEqual(true); - }); -}); - -describe('Unacceptable Windows exact paths', () => { - it('should not accept paths with /', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'match', - value: 'c:/folder/path.exe', - }) - ).toEqual(false); - }); - - it('should not accept paths not having a <char:> in the suffix', () => { - expect( - isPathValid({ - os: OperatingSystem.WINDOWS, - field: ConditionEntryField.PATH, - type: 'match', - value: '\\folder\\path.exe', - }) - ).toEqual(false); - }); -}); - -/// -describe('Unacceptable Mac/Linux wildcard paths', () => { - it('should not accept paths that do not have a folder name with a wildcard ', () => { - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/folder', - }) - ).toEqual(false); - }); - - it('should not accept paths that do not have a file name with a wildcard ', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/zip.zip', - }) - ).toEqual(false); - }); - - it('should not accept nested paths that do not have a wildcard', () => { - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/opt/pack.tar', - }) - ).toEqual(false); - }); - - it('should not accept paths with * wildcard and \\', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'c:\\**\\path.exe', - }) - ).toEqual(false); - }); - - it('should not accept paths with ? wildcard and \\', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: 'C:\\?indows\\pat?', - }) - ).toEqual(false); - }); -}); - -describe('Acceptable Mac/Linux wildcard paths', () => { - it('should accept wildcards for folders', () => { - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/**/file.', - }) - ).toEqual(true); - }); - - it('should accept wildcards for folders and files', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/usr/bi?/*.js', - }) - ).toEqual(true); - }); - - it('should accept paths with single wildcard', () => { - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/op*', - }) - ).toEqual(true); - - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/op?', - }) - ).toEqual(true); - }); - - it('should accept paths that have wildcard in filenames', () => { - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/*.*', - }) - ).toEqual(true); - }); - - it('should accept paths with ? as wildcard', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/usr/?inux/pat?', - }) - ).toEqual(true); - }); - - it('should accept paths with both ? and * as wildcards', () => { - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/usr/*?', - }) - ).toEqual(true); - }); - - it('should accept paths with multiple wildcards', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/usr/**', - }) - ).toEqual(true); - - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'wildcard', - value: '/opt/??', - }) - ).toEqual(true); - }); -}); - -describe('Acceptable Mac/Linux exact paths', () => { - it('should accept paths when it is the root path', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'match', - value: '/', - }) - ).toEqual(true); - }); - - it('should accept paths when it ends with a file name', () => { - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'match', - value: '/usr/file.ts', - }) - ).toEqual(true); - }); - - it('should accept paths when it ends with a filename in a folder', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'match', - value: '/opt/z.dmg', - }) - ).toEqual(true); - }); -}); - -describe('Acceptable Mac/Linux exact paths with hyphens', () => { - it('should accept paths when paths have folder names with hyphens', () => { - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'match', - value: '/hype-folder-name', - }) - ).toEqual(true); - }); - - it('should accept paths when file names have hyphens', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'match', - value: '/file-name.dmg', - }) - ).toEqual(true); - }); -}); - -describe('Unacceptable Mac/Linux exact paths', () => { - it('should not accept paths with \\', () => { - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'match', - value: 'c:\\folder\\path.exe', - }) - ).toEqual(false); - }); - - it('should not accept paths not starting with /', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'match', - value: 'opt/bin', - }) - ).toEqual(false); - }); - - it('should not accept paths ending with /', () => { - expect( - isPathValid({ - os: OperatingSystem.MAC, - field: ConditionEntryField.PATH, - type: 'match', - value: '/opt/bin/', - }) - ).toEqual(false); - }); - - it('should not accept file extensions with hyphens', () => { - expect( - isPathValid({ - os: OperatingSystem.LINUX, - field: ConditionEntryField.PATH, - type: 'match', - value: '/opt/bin/file.d-mg', - }) - ).toEqual(false); - }); -}); - -describe('hasSimpleExecutableName', () => { - it('should return TRUE when MAC/LINUX wildcard paths have an executable name', () => { - const os = - parseInt((Math.random() * 2).toString(), 10) === 1 - ? OperatingSystem.MAC - : OperatingSystem.LINUX; - - expect( - hasSimpleExecutableName({ - os, - type: 'wildcard', - value: '/opt/*/app', - }) - ).toEqual(true); - expect( - hasSimpleExecutableName({ - os, - type: 'wildcard', - value: '/op*/**/app.dmg', - }) - ).toEqual(true); - expect( - hasSimpleExecutableName({ - os, - type: 'wildcard', - value: "/sy*/test$ as2@13---12!@#A,DS.#$^&$!#~ 'as'd.华语.txt", - }) - ).toEqual(true); - }); - - it('should return FALSE when MAC/LINUX wildcard paths have a wildcard in executable name', () => { - const os = - parseInt((Math.random() * 2).toString(), 10) === 1 - ? OperatingSystem.MAC - : OperatingSystem.LINUX; - - expect( - hasSimpleExecutableName({ - os, - type: 'wildcard', - value: '/op/*/*pp', - }) - ).toEqual(false); - expect( - hasSimpleExecutableName({ - os, - type: 'wildcard', - value: '/op*/b**/ap.m**', - }) - ).toEqual(false); - }); - - it('should return TRUE when WINDOWS wildcards paths have a executable name', () => { - expect( - hasSimpleExecutableName({ - os: OperatingSystem.WINDOWS, - type: 'wildcard', - value: 'c:\\**\\path.exe', - }) - ).toEqual(true); - expect( - hasSimpleExecutableName({ - os: OperatingSystem.WINDOWS, - type: 'wildcard', - value: 'C:\\*\\file-name.path华语 1234.txt', - }) - ).toEqual(true); - expect( - hasSimpleExecutableName({ - os: OperatingSystem.WINDOWS, - type: 'wildcard', - value: "C:\\*\\test$ as2@13---12!@#A,DS.#$^&$!#~ 'as'd.华语.txt", - }) - ).toEqual(true); - }); - - it('should return FALSE when WINDOWS wildcards paths have a wildcard in executable name', () => { - expect( - hasSimpleExecutableName({ - os: OperatingSystem.WINDOWS, - type: 'wildcard', - value: 'c:\\**\\pa*h.exe', - }) - ).toEqual(false); - }); -}); diff --git a/packages/kbn-securitysolution-utils/src/path_validations/index.ts b/packages/kbn-securitysolution-utils/src/path_validations/index.ts deleted file mode 100644 index 1f1eaf0b0142..000000000000 --- a/packages/kbn-securitysolution-utils/src/path_validations/index.ts +++ /dev/null @@ -1,245 +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 { i18n } from '@kbn/i18n'; - -export const WILDCARD_WARNING = i18n.translate('utils.wildcardWarning', { - defaultMessage: `Using wildcards can impact Endpoint performance`, -}); - -export const FILEPATH_WARNING = i18n.translate('utils.filename.pathWarning', { - defaultMessage: `Path may be formed incorrectly; verify value`, -}); - -export enum ConditionEntryField { - HASH = 'process.hash.*', - PATH = 'process.executable.caseless', - SIGNER = 'process.Ext.code_signature', - SIGNER_MAC = 'process.code_signature', -} - -export enum EntryFieldType { - HASH = '.hash.', - EXECUTABLE = '.executable.caseless', - PATH = '.path', - SIGNER = '.code_signature', -} - -export type TrustedAppConditionEntryField = - | 'process.hash.*' - | 'process.executable.caseless' - | 'process.Ext.code_signature' - | 'process.code_signature'; - -export type BlocklistConditionEntryField = - | 'file.hash.*' - | 'file.path' - | 'file.Ext.code_signature' - | 'file.path.caseless'; -export type AllConditionEntryFields = - | TrustedAppConditionEntryField - | BlocklistConditionEntryField - | 'file.path.text'; - -export enum OperatingSystem { - LINUX = 'linux', - MAC = 'macos', - WINDOWS = 'windows', -} - -export type EntryTypes = 'match' | 'wildcard' | 'match_any'; -export type TrustedAppEntryTypes = Extract<EntryTypes, 'match' | 'wildcard'>; -export type EventFiltersTypes = EntryTypes | 'exists' | 'nested'; - -export const validatePotentialWildcardInput = ({ - field = '', - os, - value = '', -}: { - field?: string; - os: OperatingSystem; - value?: string; -}): string | undefined => { - const textInput = value.trim(); - if (field === 'file.path.text') { - return validateFilePathInput({ os, value: textInput }); - } - return validateWildcardInput(textInput); -}; - -export const validateFilePathInput = ({ - os, - value, -}: { - os: OperatingSystem; - value: string; -}): string | undefined => { - const isValidFilePath = isPathValid({ - os, - field: 'file.path.text', - type: 'wildcard', - value, - }); - const hasSimpleFileName = hasSimpleExecutableName({ - os, - type: 'wildcard', - value, - }); - - if (!value.length) { - return FILEPATH_WARNING; - } - - if (isValidFilePath) { - if (hasSimpleFileName !== undefined && !hasSimpleFileName) { - return WILDCARD_WARNING; - } - } else { - return FILEPATH_WARNING; - } -}; - -export const validateWildcardInput = (value: string | string[]): string | undefined => { - const wildcardRegex = /[*?]/; - if (Array.isArray(value)) { - const doesAnyValueContainWildcardInput = value.some((v) => wildcardRegex.test(v)); - if (doesAnyValueContainWildcardInput) { - return WILDCARD_WARNING; - } - } else { - if (wildcardRegex.test(value)) { - return WILDCARD_WARNING; - } - } -}; - -export const validateHasWildcardWithWrongOperator = ({ - operator, - value, -}: { - operator: TrustedAppEntryTypes | EventFiltersTypes; - value: string | string[]; -}): boolean => { - if (operator !== 'wildcard' && validateWildcardInput(value)) { - return true; - } else { - return false; - } -}; - -export const hasSimpleExecutableName = ({ - os, - type, - value, -}: { - os: OperatingSystem; - type: EntryTypes; - value: string; -}): boolean | undefined => { - const separator = os === OperatingSystem.WINDOWS ? '\\' : '/'; - const lastString = value.split(separator).pop(); - if (!lastString) { - return; - } - if (type === 'wildcard') { - return (lastString.split('*').length || lastString.split('?').length) === 1; - } - return true; -}; - -export const isPathValid = ({ - os, - field, - type, - value, -}: { - os: OperatingSystem; - field: AllConditionEntryFields; - type: EntryTypes; - value: string; -}): boolean => { - const pathFields: AllConditionEntryFields[] = [ - 'process.executable.caseless', - 'file.path', - 'file.path.text', - ]; - if (pathFields.includes(field)) { - if (type === 'wildcard') { - return os === OperatingSystem.WINDOWS - ? isWindowsWildcardPathValid(value) - : isLinuxMacWildcardPathValid(value); - } - return doesPathMatchRegex({ value, os }); - } - return true; -}; - -const doesPathMatchRegex = ({ os, value }: { os: OperatingSystem; value: string }): boolean => { - if (os === OperatingSystem.WINDOWS) { - const filePathRegex = - /^[a-z]:(?:|\\\\[^<>:"'/\\|?*]+\\[^<>:"'/\\|?*]+|%\w+%|)[\\](?:[^<>:"'/\\|?*]+[\\/])*([^<>:"'/\\|?*])+$/i; - return filePathRegex.test(value); - } - return /^(\/|(\/[\w\-]+)+|\/[\w\-]+\.[\w]+|(\/[\w-]+)+\/[\w\-]+\.[\w]+)$/i.test(value); -}; - -const isWindowsWildcardPathValid = (path: string): boolean => { - const firstCharacter = path[0]; - const lastCharacter = path.slice(-1); - const trimmedValue = path.trim(); - const hasSlash = /\//.test(trimmedValue); - if (path.length === 0) { - return false; - } else if ( - hasSlash || - trimmedValue.length !== path.length || - firstCharacter === '^' || - lastCharacter === '\\' || - !hasWildcard({ path, isWindowsPath: true }) - ) { - return false; - } else { - return true; - } -}; - -const isLinuxMacWildcardPathValid = (path: string): boolean => { - const firstCharacter = path[0]; - const lastCharacter = path.slice(-1); - const trimmedValue = path.trim(); - if (path.length === 0) { - return false; - } else if ( - trimmedValue.length !== path.length || - firstCharacter !== '/' || - lastCharacter === '/' || - path.length > 1024 === true || - path.includes('//') === true || - !hasWildcard({ path, isWindowsPath: false }) - ) { - return false; - } else { - return true; - } -}; - -const hasWildcard = ({ - path, - isWindowsPath, -}: { - path: string; - isWindowsPath: boolean; -}): boolean => { - for (const pathComponent of path.split(isWindowsPath ? '\\' : '/')) { - if (/[\*|\?]+/.test(pathComponent) === true) { - return true; - } - } - return false; -}; diff --git a/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts b/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts deleted file mode 100644 index 8c4e8289b1dd..000000000000 --- a/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts +++ /dev/null @@ -1,85 +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 { transformDataToNdjson } from '.'; - -export const ANCHOR_DATE = '2020-02-20T03:57:54.037Z'; - -const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE) => ({ - author: [], - id: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9', - created_at: new Date(anchorDate).toISOString(), - updated_at: new Date(anchorDate).toISOString(), - created_by: 'elastic', - description: 'some description', - enabled: true, - false_positives: ['false positive 1', 'false positive 2'], - from: 'now-6m', - immutable: false, - name: 'Query with a rule id', - query: 'user.name: root or user.name: admin', - references: ['test 1', 'test 2'], - severity: 'high', - severity_mapping: [], - updated_by: 'elastic_kibana', - tags: ['some fake tag 1', 'some fake tag 2'], - to: 'now', - type: 'query', - threat: [], - version: 1, - output_index: '.siem-signals-default', - max_signals: 100, - risk_score: 55, - risk_score_mapping: [], - language: 'kuery', - rule_id: 'query-rule-id', - interval: '5m', - exceptions_list: [], -}); - -describe('transformDataToNdjson', () => { - test('if rules are empty it returns an empty string', () => { - const ruleNdjson = transformDataToNdjson([]); - expect(ruleNdjson).toEqual(''); - }); - - test('single rule will transform with new line ending character for ndjson', () => { - const rule = getRulesSchemaMock(); - const ruleNdjson = transformDataToNdjson([rule]); - expect(ruleNdjson.endsWith('\n')).toBe(true); - }); - - test('multiple rules will transform with two new line ending characters for ndjson', () => { - const result1 = getRulesSchemaMock(); - const result2 = getRulesSchemaMock(); - result2.id = 'some other id'; - result2.rule_id = 'some other id'; - result2.name = 'Some other rule'; - - const ruleNdjson = transformDataToNdjson([result1, result2]); - // this is how we count characters in JavaScript :-) - const count = ruleNdjson.split('\n').length - 1; - expect(count).toBe(2); - }); - - test('you can parse two rules back out without errors', () => { - const result1 = getRulesSchemaMock(); - const result2 = getRulesSchemaMock(); - result2.id = 'some other id'; - result2.rule_id = 'some other id'; - result2.name = 'Some other rule'; - - const ruleNdjson = transformDataToNdjson([result1, result2]); - const ruleStrings = ruleNdjson.split('\n'); - const reParsed1 = JSON.parse(ruleStrings[0]); - const reParsed2 = JSON.parse(ruleStrings[1]); - expect(reParsed1).toEqual(result1); - expect(reParsed2).toEqual(result2); - }); -}); diff --git a/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.ts b/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.ts deleted file mode 100644 index b20606c356ef..000000000000 --- a/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.ts +++ /dev/null @@ -1,17 +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". - */ - -export const transformDataToNdjson = (data: unknown[]): string => { - if (data.length !== 0) { - const dataString = data.map((item) => JSON.stringify(item)).join('\n'); - return `${dataString}\n`; - } else { - return ''; - } -}; diff --git a/packages/kbn-securitysolution-utils/tsconfig.json b/packages/kbn-securitysolution-utils/tsconfig.json deleted file mode 100644 index d45b0c973af8..000000000000 --- a/packages/kbn-securitysolution-utils/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "**/*.ts" - ], - "kbn_references": [ - "@kbn/i18n", - "@kbn/esql-utils", - "@kbn/esql-ast", - "@kbn/esql-validation-autocomplete" - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/packages/kbn-xstate-utils/kibana.jsonc b/packages/kbn-xstate-utils/kibana.jsonc index 3b1bcf6bf8d7..5638550a862d 100644 --- a/packages/kbn-xstate-utils/kibana.jsonc +++ b/packages/kbn-xstate-utils/kibana.jsonc @@ -4,6 +4,6 @@ "owner": [ "@elastic/obs-ux-logs-team" ], - "group": "observability", - "visibility": "private" + "group": "platform", + "visibility": "shared" } diff --git a/scripts/stage_by_owner.js b/scripts/stage_by_owner.js new file mode 100644 index 000000000000..0ac33b3901a0 --- /dev/null +++ b/scripts/stage_by_owner.js @@ -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", 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". + */ + +require('../src/setup_node_env'); +require('../src/dev/stage_by_owner'); diff --git a/src/core/public/styles/_index.scss b/src/core/public/styles/_index.scss index cfdb1c7192dc..c8567498b42e 100644 --- a/src/core/public/styles/_index.scss +++ b/src/core/public/styles/_index.scss @@ -1,3 +1 @@ @import './base'; -@import './chrome/index'; -@import './rendering/index'; diff --git a/src/core/public/styles/chrome/_banner.scss b/src/core/public/styles/chrome/_banner.scss deleted file mode 100644 index feb69e54a911..000000000000 --- a/src/core/public/styles/chrome/_banner.scss +++ /dev/null @@ -1,13 +0,0 @@ -.header__topBanner { - position: fixed; - top: 0; - left: 0; - height: var(--kbnHeaderBannerHeight); - width: 100%; - z-index: $euiZHeader; -} - -.header__topBannerContainer { - height: 100%; - width: 100%; -} diff --git a/src/core/public/styles/chrome/_index.scss b/src/core/public/styles/chrome/_index.scss deleted file mode 100644 index 22390aa5a44b..000000000000 --- a/src/core/public/styles/chrome/_index.scss +++ /dev/null @@ -1,45 +0,0 @@ -@import './banner'; - -.euiDataGrid__restrictBody { - .headerGlobalNav, - .kbnQueryBar { - display: none; - } -} - -.euiDataGrid__restrictBody.euiBody--headerIsFixed { - .euiFlyout { - top: 0; - height: 100%; - } -} - -.chrHeaderHelpMenu__version { - text-transform: none; -} - -.chrHeaderBadge__wrapper { - align-self: center; - margin-right: $euiSize; -} - -.header__toggleNavButtonSection { - .euiBody--collapsibleNavIsDocked & { - display: none; - } -} - -.header__breadcrumbsWithExtensionContainer { - overflow: hidden; // enables text-ellipsis in the last breadcrumb - .euiHeaderBreadcrumbs { - // stop breadcrumbs from growing. - // this makes the extension appear right next to the last breadcrumb - flex-grow: 0; - margin-right: 0; - - overflow: hidden; // enables text-ellipsis in the last breadcrumb - } -} -.header__breadcrumbsAppendExtension { - flex-grow: 1; -} diff --git a/src/core/public/styles/rendering/_base.scss b/src/core/public/styles/rendering/_base.scss deleted file mode 100644 index 259115f6a526..000000000000 --- a/src/core/public/styles/rendering/_base.scss +++ /dev/null @@ -1,80 +0,0 @@ -@import '../../mixins'; - -/** - * Stretch the root element of the Kibana application to set the base-size that - * flexed children should keep. Only works when paired with root styles applied - * by core service from new platform - */ - -#kibana-body { - // DO NOT ADD ANY OVERFLOW BEHAVIORS HERE - // It will break the sticky navigation - min-height: 100%; - display: flex; - flex-direction: column; -} - -// Affixes a div to restrict the position of charts tooltip to the visible viewport minus the header -#app-fixed-viewport { - pointer-events: none; - visibility: hidden; - position: fixed; - top: var(--kbnAppHeadersOffset, var(--euiFixedHeadersOffset, 0)); - right: 0; - bottom: 0; - left: 0; -} - -.kbnAppWrapper { - // DO NOT ADD ANY OTHER STYLES TO THIS SELECTOR - // This a very nested dependency happnening in "all" apps - display: flex; - flex-flow: column nowrap; - flex-grow: 1; - z-index: 0; // This effectively puts every high z-index inside the scope of this wrapper to it doesn't interfere with the header and/or overlay mask - position: relative; // This is temporary for apps that relied on this being present on `.application` -} - -.kbnBody { - padding-top: var(--euiFixedHeadersOffset, 0); -} - -// Conditionally override :root CSS fixed header variable. Updating `--euiFixedHeadersOffset` -// on the body will cause all child EUI components to automatically update their offsets -.kbnBody--hasHeaderBanner { - --euiFixedHeadersOffset: var(--kbnHeaderOffsetWithBanner); - - // Offset fixed EuiHeaders by the top banner - .euiHeader[data-fixed-header] { - margin-top: var(--kbnHeaderBannerHeight); - } - - // Prevent banners from covering full screen data grids - .euiDataGrid--fullScreen { - height: calc(100vh - var(--kbnHeaderBannerHeight)); - top: var(--kbnHeaderBannerHeight); - } -} - -// Set a body CSS variable for the app container to use - calculates the total -// height of all fixed headers + the sticky action menu toolbar -.kbnBody--hasProjectActionMenu { - --kbnAppHeadersOffset: calc(var(--kbnHeaderOffset) + var(--kbnProjectHeaderAppActionMenuHeight)); - - &.kbnBody--hasHeaderBanner { - --kbnAppHeadersOffset: calc(var(--kbnHeaderOffsetWithBanner) + var(--kbnProjectHeaderAppActionMenuHeight)); - } -} - -.kbnBody--chromeHidden { - // stylelint-disable-next-line length-zero-no-unit - --euiFixedHeadersOffset: 0px; - - &.kbnBody--hasHeaderBanner { - --euiFixedHeadersOffset: var(--kbnHeaderBannerHeight); - } - - &.kbnBody--hasProjectActionMenu { - --kbnAppHeadersOffset: var(--euiFixedHeadersOffset, 0); - } -} diff --git a/src/core/public/styles/rendering/_index.scss b/src/core/public/styles/rendering/_index.scss deleted file mode 100644 index c8567498b42e..000000000000 --- a/src/core/public/styles/rendering/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './base'; diff --git a/src/core/server/integration_tests/saved_objects/migrations/archives/7.13.0_5k_so_node_01.zip b/src/core/server/integration_tests/saved_objects/migrations/archives/7.13.0_5k_so_node_01.zip index 5a7ee884b5b0..b096874e6ad3 100644 Binary files a/src/core/server/integration_tests/saved_objects/migrations/archives/7.13.0_5k_so_node_01.zip and b/src/core/server/integration_tests/saved_objects/migrations/archives/7.13.0_5k_so_node_01.zip differ diff --git a/src/core/server/integration_tests/saved_objects/migrations/archives/7.13.0_5k_so_node_02.zip b/src/core/server/integration_tests/saved_objects/migrations/archives/7.13.0_5k_so_node_02.zip index 180465269c1f..56f6ab57ca74 100644 Binary files a/src/core/server/integration_tests/saved_objects/migrations/archives/7.13.0_5k_so_node_02.zip and b/src/core/server/integration_tests/saved_objects/migrations/archives/7.13.0_5k_so_node_02.zip differ diff --git a/src/core/server/integration_tests/saved_objects/migrations/archives/8.4.0_with_sample_data_logs.zip b/src/core/server/integration_tests/saved_objects/migrations/archives/8.4.0_with_sample_data_logs.zip index 9a48e2527590..7a93adcc12b3 100644 Binary files a/src/core/server/integration_tests/saved_objects/migrations/archives/8.4.0_with_sample_data_logs.zip and b/src/core/server/integration_tests/saved_objects/migrations/archives/8.4.0_with_sample_data_logs.zip differ diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions_test_suite.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions_test_suite.ts index 3b7aae9183a5..c6995c6e2d4c 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions_test_suite.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions_test_suite.ts @@ -770,9 +770,10 @@ export const runActionTestSuite = ({ }); }); - // Reindex doesn't return any errors on it's own, so we have to test + // Reindex doesn't return any errors on its own, so we have to test // together with waitForReindexTask - // Failing: See https://github.com/elastic/kibana/issues/166190 + // Flaky: https://github.com/elastic/kibana/issues/166190 + // Reported here: https://github.com/elastic/kibana/issues/167273 describe.skip('reindex & waitForReindexTask', () => { it('resolves right when reindex succeeds without reindex script', async () => { const res = (await reindex({ @@ -1444,8 +1445,7 @@ export const runActionTestSuite = ({ }); }); - // FLAKY: https://github.com/elastic/kibana/issues/166199 - describe.skip('waitForPickupUpdatedMappingsTask', () => { + describe('waitForPickupUpdatedMappingsTask', () => { it('rejects if there are failures', async () => { const res = (await pickupUpdatedMappings( client, diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/incompatible_cluster_routing_allocation.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/incompatible_cluster_routing_allocation.test.ts index e8a523b5de50..8213c880c0fa 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/incompatible_cluster_routing_allocation.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/incompatible_cluster_routing_allocation.test.ts @@ -92,8 +92,7 @@ async function updateRoutingAllocations( }); } -// Failing ES promotion: https://github.com/elastic/kibana/issues/158318 -describe.skip('incompatible_cluster_routing_allocation', () => { +describe('incompatible_cluster_routing_allocation', () => { let client: ElasticsearchClient; let root: Root; diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/multiple_es_nodes.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/multiple_es_nodes.test.ts index 5be7b4671ef7..6898962077b9 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/multiple_es_nodes.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/multiple_es_nodes.test.ts @@ -96,8 +96,7 @@ function createRoot({ logFileName, hosts }: RootConfig) { }); } -// Failing ES promotion: https://github.com/elastic/kibana/issues/167676 -describe.skip('migration v2', () => { +describe('migration v2', () => { let esServer: TestElasticsearchUtils; let root: Root; const migratedIndexAlias = `.kibana_${pkg.version}`; diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/read_batch_size.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/read_batch_size.test.ts index 6ebc7ee5a66f..9f970ed234d7 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/read_batch_size.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/read_batch_size.test.ts @@ -19,9 +19,7 @@ import { getFips } from 'crypto'; const logFilePath = join(__dirname, 'read_batch_size.log'); -// Failing ES promotion: https://github.com/elastic/kibana/issues/163254 -// Failing ES promotion: https://github.com/elastic/kibana/issues/163255 -describe.skip('migration v2 - read batch size', () => { +describe('migration v2 - read batch size', () => { let esServer: TestElasticsearchUtils; let root: Root; let logs: string; diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile index e868cb73cb8d..7d9eaac88696 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile @@ -114,7 +114,7 @@ EXPOSE 5601 RUN for iter in {1..10}; do \ microdnf update --setopt=tsflags=nodocs -y && \ microdnf install --setopt=tsflags=nodocs -y \ - fontconfig freetype shadow-utils nss findutils && \ + fontconfig liberation-fonts-common freetype shadow-utils nss findutils && \ microdnf clean all && exit_code=0 && break || exit_code=$? && echo "microdnf error: retry $iter in 10s" && \ sleep 10; \ done; \ @@ -126,7 +126,7 @@ RUN for iter in {1..10}; do \ apt-get update && \ apt-get upgrade -y && \ apt-get install -y --no-install-recommends \ - fontconfig libnss3 curl ca-certificates && \ + fontconfig fonts-liberation libnss3 curl ca-certificates && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && exit_code=0 && break || exit_code=$? && echo "apt-get error: retry $iter in 10s" && \ sleep 10; \ @@ -134,7 +134,7 @@ RUN for iter in {1..10}; do \ (exit $exit_code) {{/ubuntu}} {{#wolfi}} -RUN apk --no-cache add bash curl fontconfig libstdc++ libnss findutils shadow ca-certificates +RUN apk --no-cache add bash curl fontconfig font-liberation libstdc++ libnss findutils shadow ca-certificates {{/wolfi}} # Bring in Kibana from the initial stage. diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js b/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js index 5faeab1acb53..540b51a29498 100644 --- a/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js +++ b/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js @@ -17,13 +17,13 @@ const log = new ToolingLog({ }); describe(`enumeratePatterns`, () => { - it(`should resolve x-pack/plugins/screenshotting/server/browsers/extract/unzip.ts to kibana-screenshotting`, () => { + it(`should resolve x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/unzip.ts to kibana-screenshotting`, () => { const actual = enumeratePatterns(REPO_ROOT)(log)( - new Map([['x-pack/plugins/screenshotting', ['kibana-screenshotting']]]) + new Map([['x-pack/platform/plugins/shared/screenshotting', ['kibana-screenshotting']]]) ); expect(actual.flat()).toContain( - 'x-pack/plugins/screenshotting/server/browsers/extract/unzip.ts kibana-screenshotting' + 'x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/unzip.ts kibana-screenshotting' ); }); it(`should resolve src/plugins/charts/common/static/color_maps/color_maps.ts to kibana-app`, () => { diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 249a271d5a38..30383559e7fe 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -98,7 +98,19 @@ export const IGNORE_FILE_GLOBS = [ * * @type {Array} */ -export const KEBAB_CASE_DIRECTORY_GLOBS = ['packages/*', 'x-pack', 'x-pack/packages/*']; +export const KEBAB_CASE_DIRECTORY_GLOBS = [ + 'packages/*', + 'x-pack', + 'x-pack/packages/*', + 'src/core/packages/*/*', + 'src/platform/packages/private/*', + 'src/platform/packages/shared/*', + 'x-pack/platform/packages/private/*', + 'x-pack/platform/packages/shared/*', + 'x-pack/solutions/observability/packages/*', + 'x-pack/solutions/search/packages/*', + 'x-pack/solutions/security/packages/*', +]; /** * These patterns are matched against directories and indicate @@ -170,10 +182,10 @@ export const TEMPORARILY_IGNORED_PATHS = [ 'x-pack/plugins/monitoring/public/icons/health-green.svg', 'x-pack/plugins/monitoring/public/icons/health-red.svg', 'x-pack/plugins/monitoring/public/icons/health-yellow.svg', - 'x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Medium.ttf', - 'x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Regular.ttf', - 'x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Italic.ttf', - 'x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Medium.ttf', - 'x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Regular.ttf', - 'x-pack/plugins/screenshotting/server/assets/img/logo-grey.png', + 'x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Medium.ttf', + 'x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Regular.ttf', + 'x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/Roboto-Italic.ttf', + 'x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/Roboto-Medium.ttf', + 'x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/Roboto-Regular.ttf', + 'x-pack/platform/plugins/shared/screenshotting/server/assets/img/logo-grey.png', ]; diff --git a/src/dev/stage_by_owner.ts b/src/dev/stage_by_owner.ts new file mode 100644 index 000000000000..7987874da75f --- /dev/null +++ b/src/dev/stage_by_owner.ts @@ -0,0 +1,96 @@ +/* + * 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 simpleGit from 'simple-git'; + +import { run } from '@kbn/dev-cli-runner'; +import { getOwningTeamsForPath, getCodeOwnersEntries, CodeOwnersEntry } from '@kbn/code-owners'; +import { asyncForEach } from '@kbn/std'; +import { inspect } from 'util'; + +const git = simpleGit(); + +interface File { + path: string; + staged: boolean; +} + +// Function to get the list of changed files +const getChangedFiles = async (): Promise<File[]> => { + const { staged, files } = await git.status(); + return files.map((file) => ({ path: file.path, staged: staged.includes(file.path) })); +}; + +run( + async ({ flags, log }) => { + const { + _: [owner], + } = flags; + + const changedFiles = await getChangedFiles(); + const owners: { staged: Record<string, string[]>; unstaged: Record<string, string[]> } = { + staged: {}, + unstaged: {}, + }; + + let codeOwnersEntries: CodeOwnersEntry[] = []; + + try { + codeOwnersEntries = getCodeOwnersEntries(); + } catch (e) { + log.error('CODEOWNERS cannot be read.'); + process.exit(1); + } + + const getOwners = (file: string) => { + const teams = getOwningTeamsForPath(file, codeOwnersEntries); + + if (teams.length === 0) { + log.warning(`No owner found for ${file}`); + return []; + } + + return teams; + }; + + for (const file of changedFiles) { + const fileOwners = getOwners(file.path); + + if (fileOwners) { + await asyncForEach(fileOwners, async (fileOwner) => { + const loc = file.staged ? 'staged' : 'unstaged'; + + owners[loc][fileOwner] = [ + ...(owners[loc][fileOwner] || []), + file.path + (fileOwners.length > 1 ? ` (+${fileOwners.length - 1})` : ''), + ]; + + if (owner && fileOwner === owner) { + await git.add(file.path); + log.info(`Staged ${file.path}`); + } + }); + } + } + + if (!owner) { + log.info(inspect(owners, { colors: true, depth: null })); + } + + log.info('Done.'); + }, + { + usage: 'node src/dev/stage_by_owner.ts [owner]', + description: ` + This script stages files based on the CODEOWNERS file. + If an owner is provided, it stages the files owned by that owner. + Otherwise, it outputs changed files, grouped by owner. + `, + } +); diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 4991ac0dfe44..c2c24528e3dc 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -49,7 +49,7 @@ export const storybookAliases = { inventory: 'x-pack/plugins/observability_solution/inventory/.storybook', investigate: 'x-pack/solutions/observability/plugins/investigate_app/.storybook', kibana_react: 'src/plugins/kibana_react/.storybook', - lists: 'x-pack/plugins/lists/.storybook', + lists: 'x-pack/solutions/security/plugins/lists/.storybook', logs_explorer: 'x-pack/plugins/observability_solution/logs_explorer/.storybook', management: 'packages/kbn-management/storybook/config', observability: 'x-pack/solutions/observability/plugins/observability/.storybook', diff --git a/src/dev/tsconfig.json b/src/dev/tsconfig.json index 87473c1e79e8..0e2e8e94c629 100644 --- a/src/dev/tsconfig.json +++ b/src/dev/tsconfig.json @@ -44,5 +44,6 @@ "@kbn/core-test-helpers-kbn-server", "@kbn/dev-proc-runner", "@kbn/core-i18n-server-internal", + "@kbn/code-owners", ] } diff --git a/src/platform/packages/shared/deeplinks/observability/locators/apm.ts b/src/platform/packages/shared/deeplinks/observability/locators/apm.ts new file mode 100644 index 000000000000..64e446e883f1 --- /dev/null +++ b/src/platform/packages/shared/deeplinks/observability/locators/apm.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", 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 { SerializableRecord } from '@kbn/utility-types'; + +export const TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR = 'TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR'; + +export interface TransactionDetailsByTraceIdLocatorParams extends SerializableRecord { + rangeFrom?: string; + rangeTo?: string; + traceId: string; +} diff --git a/src/platform/packages/shared/deeplinks/observability/locators/index.ts b/src/platform/packages/shared/deeplinks/observability/locators/index.ts index 5d45f66194b6..3317df2268df 100644 --- a/src/platform/packages/shared/deeplinks/observability/locators/index.ts +++ b/src/platform/packages/shared/deeplinks/observability/locators/index.ts @@ -7,6 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +export * from './apm'; export * from './dataset_quality'; export * from './dataset_quality_details'; export * from './logs_explorer'; diff --git a/packages/kbn-securitysolution-es-utils/README.md b/src/platform/packages/shared/kbn-securitysolution-es-utils/README.md similarity index 100% rename from packages/kbn-securitysolution-es-utils/README.md rename to src/platform/packages/shared/kbn-securitysolution-es-utils/README.md diff --git a/packages/kbn-securitysolution-es-utils/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/index.ts diff --git a/src/platform/packages/shared/kbn-securitysolution-es-utils/jest.config.js b/src/platform/packages/shared/kbn-securitysolution-es-utils/jest.config.js new file mode 100644 index 000000000000..3a654a3a269e --- /dev/null +++ b/src/platform/packages/shared/kbn-securitysolution-es-utils/jest.config.js @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/src/platform/packages/shared/kbn-securitysolution-es-utils'], +}; diff --git a/packages/kbn-securitysolution-es-utils/kibana.jsonc b/src/platform/packages/shared/kbn-securitysolution-es-utils/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-es-utils/kibana.jsonc rename to src/platform/packages/shared/kbn-securitysolution-es-utils/kibana.jsonc diff --git a/packages/kbn-securitysolution-es-utils/package.json b/src/platform/packages/shared/kbn-securitysolution-es-utils/package.json similarity index 100% rename from packages/kbn-securitysolution-es-utils/package.json rename to src/platform/packages/shared/kbn-securitysolution-es-utils/package.json diff --git a/packages/kbn-securitysolution-es-utils/src/bad_request_error/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/bad_request_error/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/bad_request_error/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/bad_request_error/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/create_boostrap_index/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/create_boostrap_index/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/create_boostrap_index/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/create_boostrap_index/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/create_data_stream/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/create_data_stream/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/create_data_stream/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/create_data_stream/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/decode_version/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/decode_version/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/decode_version/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/decode_version/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/delete_all_index/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/delete_all_index/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/delete_all_index/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/delete_all_index/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/delete_data_stream/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/delete_data_stream/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/delete_data_stream/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/delete_data_stream/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/delete_index_template/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/delete_index_template/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/delete_index_template/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/delete_index_template/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/delete_policy/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/delete_policy/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/delete_policy/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/delete_policy/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/delete_template/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/delete_template/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/delete_template/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/delete_template/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/elasticsearch_client/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/elasticsearch_client/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/elasticsearch_client/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/elasticsearch_client/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/encode_hit_version/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/encode_hit_version/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/encode_hit_version/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/encode_hit_version/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/get_bootstrap_index_exists/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_bootstrap_index_exists/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/get_bootstrap_index_exists/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_bootstrap_index_exists/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/get_data_stream_exists/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_data_stream_exists/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/get_data_stream_exists/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_data_stream_exists/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/get_index_aliases/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_index_aliases/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/get_index_aliases/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_index_aliases/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/get_index_count/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_index_count/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/get_index_count/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_index_count/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/get_index_exists/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_index_exists/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/get_index_exists/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_index_exists/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/get_index_template_exists/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_index_template_exists/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/get_index_template_exists/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_index_template_exists/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/get_policy_exists/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_policy_exists/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/get_policy_exists/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_policy_exists/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/get_template_exists/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_template_exists/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/get_template_exists/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/get_template_exists/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/migrate_to_data_stream/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/migrate_to_data_stream/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/migrate_to_data_stream/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/migrate_to_data_stream/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/put_mappings/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/put_mappings/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/put_mappings/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/put_mappings/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/read_index/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/read_index/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/read_index/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/read_index/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/read_privileges/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/read_privileges/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/read_privileges/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/read_privileges/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/remove_policy_from_index/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/remove_policy_from_index/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/remove_policy_from_index/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/remove_policy_from_index/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/set_index_template/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/set_index_template/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/set_index_template/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/set_index_template/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/set_policy/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/set_policy/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/set_policy/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/set_policy/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/set_template/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/set_template/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/set_template/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/set_template/index.ts diff --git a/packages/kbn-securitysolution-es-utils/src/transform_error/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/transform_error/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/transform_error/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/transform_error/index.test.ts diff --git a/packages/kbn-securitysolution-es-utils/src/transform_error/index.ts b/src/platform/packages/shared/kbn-securitysolution-es-utils/src/transform_error/index.ts similarity index 100% rename from packages/kbn-securitysolution-es-utils/src/transform_error/index.ts rename to src/platform/packages/shared/kbn-securitysolution-es-utils/src/transform_error/index.ts diff --git a/src/platform/packages/shared/kbn-securitysolution-es-utils/tsconfig.json b/src/platform/packages/shared/kbn-securitysolution-es-utils/tsconfig.json new file mode 100644 index 000000000000..5a16f6bdaff6 --- /dev/null +++ b/src/platform/packages/shared/kbn-securitysolution-es-utils/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "exclude": [ + "target/**/*", + ], + "kbn_references": [ + "@kbn/zod-helpers", + "@kbn/zod", + ] +} diff --git a/packages/kbn-securitysolution-io-ts-types/README.md b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/README.md similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/README.md rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/README.md diff --git a/packages/kbn-securitysolution-io-ts-types/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/index.ts diff --git a/src/platform/packages/shared/kbn-securitysolution-io-ts-types/jest.config.js b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/jest.config.js new file mode 100644 index 000000000000..309c3085d744 --- /dev/null +++ b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/jest.config.js @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/src/platform/packages/shared/kbn-securitysolution-io-ts-types'], +}; diff --git a/packages/kbn-securitysolution-io-ts-types/kibana.jsonc b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/kibana.jsonc rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/kibana.jsonc diff --git a/packages/kbn-securitysolution-io-ts-types/package.json b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/package.json similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/package.json rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/package.json diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_array/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_array/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_array/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_array/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_array/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_array/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_array/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_array/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_csv_array/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_csv_array/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_csv_array/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_csv_array/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_csv_array/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_csv_array/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_csv_array/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_csv_array/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_empty_string/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_empty_string/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_empty_string/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_empty_string/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_empty_string/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_empty_string/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_empty_string/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_empty_string/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_string_array/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_string_array/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_string_array/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_string_array/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_string_array/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_string_array/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_string_array/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_string_array/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.ts diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_uuid/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_uuid/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_uuid/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_uuid/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_uuid/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_uuid/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_value/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_value/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_value/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_value/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_value/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_value/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_value/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_value/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_version_number/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_version_number/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_version_number/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_version_number/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/default_version_number/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_version_number/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/default_version_number/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/default_version_number/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/empty_string_array/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/empty_string_array/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/empty_string_array/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/empty_string_array/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/empty_string_array/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/empty_string_array/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/empty_string_array/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/empty_string_array/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/enumeration/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/enumeration/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/enumeration/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/enumeration/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/enumeration/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/enumeration/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/enumeration/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/enumeration/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/import_query_schema/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/import_query_schema/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/import_query_schema/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/import_query_schema/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/import_query_schema/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/import_query_schema/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/import_query_schema/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/import_query_schema/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/iso_date_string/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/iso_date_string/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/iso_date_string/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/iso_date_string/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/iso_date_string/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/iso_date_string/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/iso_date_string/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/iso_date_string/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/limited_size_array/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/limited_size_array/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/limited_size_array/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/limited_size_array/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/limited_size_array/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/limited_size_array/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/limited_size_array/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/limited_size_array/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/non_empty_array/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_array/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/non_empty_array/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_array/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/non_empty_array/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_array/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/non_empty_array/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_array/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/non_empty_string/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_string/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/non_empty_string/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_string/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/non_empty_string/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_string/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/non_empty_string/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_string/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/number_between_zero_and_one_inclusive/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/number_between_zero_and_one_inclusive/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/number_between_zero_and_one_inclusive/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/number_between_zero_and_one_inclusive/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/number_between_zero_and_one_inclusive/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/number_between_zero_and_one_inclusive/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/number_between_zero_and_one_inclusive/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/number_between_zero_and_one_inclusive/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/operator/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/operator/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/operator/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/operator/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/positive_integer/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/positive_integer/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/positive_integer/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/positive_integer/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/positive_integer/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/positive_integer/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/positive_integer/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/positive_integer/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/string_to_positive_number/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/string_to_positive_number/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/string_to_positive_number/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/string_to_positive_number/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/time_duration/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/time_duration/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/time_duration/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/time_duration/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/uuid/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/uuid/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/uuid/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/uuid/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/uuid/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/uuid/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/uuid/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/uuid/index.ts diff --git a/packages/kbn-securitysolution-io-ts-types/src/version/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/version/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/version/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-types/src/version/index.ts diff --git a/src/platform/packages/shared/kbn-securitysolution-io-ts-types/tsconfig.json b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/tsconfig.json new file mode 100644 index 000000000000..6bc8a84da337 --- /dev/null +++ b/src/platform/packages/shared/kbn-securitysolution-io-ts-types/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "kbn_references": [ + "@kbn/securitysolution-io-ts-utils" + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-securitysolution-io-ts-utils/README.md b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/README.md similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/README.md rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/README.md diff --git a/packages/kbn-securitysolution-io-ts-utils/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/index.ts diff --git a/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/jest.config.js b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/jest.config.js new file mode 100644 index 000000000000..cfa70556f4b8 --- /dev/null +++ b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/jest.config.js @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/src/platform/packages/shared/kbn-securitysolution-io-ts-utils'], +}; diff --git a/packages/kbn-securitysolution-io-ts-utils/kibana.jsonc b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/kibana.jsonc rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/kibana.jsonc diff --git a/packages/kbn-securitysolution-io-ts-utils/package.json b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/package.json similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/package.json rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/package.json diff --git a/packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/exact_check/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/exact_check/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/exact_check/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/src/exact_check/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/exact_check/index.ts diff --git a/packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/format_errors/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/format_errors/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts diff --git a/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts diff --git a/packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts diff --git a/packages/kbn-securitysolution-io-ts-utils/src/validate/index.test.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/validate/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/src/validate/index.test.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/validate/index.test.ts diff --git a/packages/kbn-securitysolution-io-ts-utils/src/validate/index.ts b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/validate/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-utils/src/validate/index.ts rename to src/platform/packages/shared/kbn-securitysolution-io-ts-utils/src/validate/index.ts diff --git a/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/tsconfig.json b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/tsconfig.json new file mode 100644 index 000000000000..ca5354ad56e4 --- /dev/null +++ b/src/platform/packages/shared/kbn-securitysolution-io-ts-utils/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "kbn_references": [ + "@kbn/datemath" + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-securitysolution-rules/README.md b/src/platform/packages/shared/kbn-securitysolution-rules/README.md similarity index 100% rename from packages/kbn-securitysolution-rules/README.md rename to src/platform/packages/shared/kbn-securitysolution-rules/README.md diff --git a/packages/kbn-securitysolution-rules/index.ts b/src/platform/packages/shared/kbn-securitysolution-rules/index.ts similarity index 100% rename from packages/kbn-securitysolution-rules/index.ts rename to src/platform/packages/shared/kbn-securitysolution-rules/index.ts diff --git a/packages/kbn-securitysolution-rules/kibana.jsonc b/src/platform/packages/shared/kbn-securitysolution-rules/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-rules/kibana.jsonc rename to src/platform/packages/shared/kbn-securitysolution-rules/kibana.jsonc diff --git a/packages/kbn-securitysolution-rules/package.json b/src/platform/packages/shared/kbn-securitysolution-rules/package.json similarity index 100% rename from packages/kbn-securitysolution-rules/package.json rename to src/platform/packages/shared/kbn-securitysolution-rules/package.json diff --git a/packages/kbn-securitysolution-rules/src/configuration_constants.ts b/src/platform/packages/shared/kbn-securitysolution-rules/src/configuration_constants.ts similarity index 100% rename from packages/kbn-securitysolution-rules/src/configuration_constants.ts rename to src/platform/packages/shared/kbn-securitysolution-rules/src/configuration_constants.ts diff --git a/packages/kbn-securitysolution-rules/src/rule_type_constants.ts b/src/platform/packages/shared/kbn-securitysolution-rules/src/rule_type_constants.ts similarity index 100% rename from packages/kbn-securitysolution-rules/src/rule_type_constants.ts rename to src/platform/packages/shared/kbn-securitysolution-rules/src/rule_type_constants.ts diff --git a/packages/kbn-securitysolution-rules/src/rule_type_mappings.ts b/src/platform/packages/shared/kbn-securitysolution-rules/src/rule_type_mappings.ts similarity index 100% rename from packages/kbn-securitysolution-rules/src/rule_type_mappings.ts rename to src/platform/packages/shared/kbn-securitysolution-rules/src/rule_type_mappings.ts diff --git a/packages/kbn-securitysolution-rules/src/utils.ts b/src/platform/packages/shared/kbn-securitysolution-rules/src/utils.ts similarity index 100% rename from packages/kbn-securitysolution-rules/src/utils.ts rename to src/platform/packages/shared/kbn-securitysolution-rules/src/utils.ts diff --git a/src/platform/packages/shared/kbn-securitysolution-rules/tsconfig.json b/src/platform/packages/shared/kbn-securitysolution-rules/tsconfig.json new file mode 100644 index 000000000000..131ff3e6bb43 --- /dev/null +++ b/src/platform/packages/shared/kbn-securitysolution-rules/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/src/platform/plugins/shared/esql/jest.integration.config.js b/src/platform/plugins/shared/esql/jest.integration.config.js new file mode 100644 index 000000000000..33f2103af1ad --- /dev/null +++ b/src/platform/plugins/shared/esql/jest.integration.config.js @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test/jest_integration', + rootDir: '../../../../..', + roots: ['<rootDir>/src/platform/plugins/shared/esql'], +}; diff --git a/src/platform/plugins/shared/esql/server/index.ts b/src/platform/plugins/shared/esql/server/index.ts index 775da36c4395..c4b769d83e71 100644 --- a/src/platform/plugins/shared/esql/server/index.ts +++ b/src/platform/plugins/shared/esql/server/index.ts @@ -7,7 +7,9 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export const plugin = async () => { +import type { PluginInitializerContext } from '@kbn/core/server'; + +export const plugin = async (initContext: PluginInitializerContext) => { const { EsqlServerPlugin } = await import('./plugin'); - return new EsqlServerPlugin(); + return new EsqlServerPlugin(initContext); }; diff --git a/src/platform/plugins/shared/esql/server/plugin.ts b/src/platform/plugins/shared/esql/server/plugin.ts index a227c8e95b4a..1b145c2f4341 100644 --- a/src/platform/plugins/shared/esql/server/plugin.ts +++ b/src/platform/plugins/shared/esql/server/plugin.ts @@ -7,13 +7,22 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; +import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { ContentManagementServerSetup } from '@kbn/content-management-plugin/server'; import { getUiSettings } from './ui_settings'; +import { registerRoutes } from './routes'; export class EsqlServerPlugin implements Plugin { + private readonly initContext: PluginInitializerContext; + + constructor(initContext: PluginInitializerContext) { + this.initContext = { ...initContext }; + } + public setup(core: CoreSetup, plugins: { contentManagement: ContentManagementServerSetup }) { + const { initContext } = this; + core.uiSettings.register(getUiSettings()); plugins.contentManagement.favorites.registerFavoriteType('esql_query', { @@ -23,6 +32,9 @@ export class EsqlServerPlugin implements Plugin { status: schema.string(), }), }); + + registerRoutes(core, initContext); + return {}; } diff --git a/src/platform/plugins/shared/esql/server/routes/get_join_indices.ts b/src/platform/plugins/shared/esql/server/routes/get_join_indices.ts new file mode 100644 index 000000000000..8fe1b92259d1 --- /dev/null +++ b/src/platform/plugins/shared/esql/server/routes/get_join_indices.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", 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 { IRouter, PluginInitializerContext } from '@kbn/core/server'; + +import { EsqlService } from '../services/esql_service'; + +export const registerGetJoinIndicesRoute = ( + router: IRouter, + { logger }: PluginInitializerContext +) => { + router.get( + { + path: '/internal/esql/autocomplete/join/indices', + validate: {}, + }, + async (requestHandlerContext, request, response) => { + try { + const core = await requestHandlerContext.core; + const service = new EsqlService({ client: core.elasticsearch.client.asCurrentUser }); + const result = await service.getJoinIndices(); + + return response.ok({ + body: result, + }); + } catch (error) { + logger.get().debug(error); + throw error; + } + } + ); +}; diff --git a/src/platform/plugins/shared/esql/server/routes/index.ts b/src/platform/plugins/shared/esql/server/routes/index.ts new file mode 100644 index 000000000000..93947ee1be43 --- /dev/null +++ b/src/platform/plugins/shared/esql/server/routes/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", 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 { CoreSetup, PluginInitializerContext } from '@kbn/core/server'; + +import { registerGetJoinIndicesRoute } from './get_join_indices'; + +export const registerRoutes = (setup: CoreSetup, initContext: PluginInitializerContext) => { + const router = setup.http.createRouter(); + + registerGetJoinIndicesRoute(router, initContext); +}; diff --git a/src/platform/plugins/shared/esql/server/services/esql_service.ts b/src/platform/plugins/shared/esql/server/services/esql_service.ts new file mode 100644 index 000000000000..2861d7859ae4 --- /dev/null +++ b/src/platform/plugins/shared/esql/server/services/esql_service.ts @@ -0,0 +1,85 @@ +/* + * 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 { ElasticsearchClient } from '@kbn/core/server'; +import type { JoinIndexAutocompleteItem, JoinIndicesAutocompleteResult } from './types'; + +export interface EsqlServiceOptions { + client: ElasticsearchClient; +} + +export class EsqlService { + constructor(public readonly options: EsqlServiceOptions) {} + + protected async getIndexAliases(indices: string[]): Promise<Record<string, string[]>> { + const result: Record<string, string[]> = {}; + const { client } = this.options; + + // Execute: GET /<index1,index2,...>/_alias + interface AliasesResponse { + [indexName: string]: { + aliases: { + [aliasName: string]: {}; + }; + }; + } + const response = (await client.indices.getAlias({ + index: indices, + })) as AliasesResponse; + + for (const [indexName, { aliases }] of Object.entries(response)) { + const aliasNames = Object.keys(aliases ?? {}); + + if (aliasNames.length > 0) { + result[indexName] = aliasNames; + } + } + + return result; + } + + public async getJoinIndices(): Promise<JoinIndicesAutocompleteResult> { + const { client } = this.options; + + // Execute: GET /_all/_settings/index.mode,aliases?flat_settings=true + interface IndexModeResponse { + [indexName: string]: { + settings: { + 'index.mode': string; + }; + }; + } + const queryByIndexModeResponse = (await client.indices.getSettings({ + name: 'index.mode', + flat_settings: true, + })) as IndexModeResponse; + + const indices: JoinIndexAutocompleteItem[] = []; + const indexNames: string[] = []; + + for (const [name, { settings }] of Object.entries(queryByIndexModeResponse)) { + if (settings['index.mode'] === 'lookup') { + indexNames.push(name); + indices.push({ name, mode: 'lookup', aliases: [] }); + } + } + + const aliases = await this.getIndexAliases(indexNames); + + for (const index of indices) { + index.aliases = aliases[index.name] ?? []; + } + + const result: JoinIndicesAutocompleteResult = { + indices, + }; + + return result; + } +} diff --git a/src/platform/plugins/shared/esql/server/services/integration_tests/esql_service.test.ts b/src/platform/plugins/shared/esql/server/services/integration_tests/esql_service.test.ts new file mode 100644 index 000000000000..dc73cfade4e7 --- /dev/null +++ b/src/platform/plugins/shared/esql/server/services/integration_tests/esql_service.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", 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 { EsqlServiceTestbed } from './testbed'; + +describe('EsqlService', () => { + const testbed = new EsqlServiceTestbed(); + + beforeAll(async () => { + await testbed.start(); + await testbed.setupLookupIndices(); + }); + + afterAll(async () => { + await testbed.stop(); + }); + + it('can load ES|QL Autocomplete/Validation indices for JOIN command', async () => { + const url = '/internal/esql/autocomplete/join/indices'; + const result = await testbed.GET(url).send().expect(200); + + const item1 = result.body.indices.find((item: any) => item.name === 'lookup_index1'); + const item2 = result.body.indices.find((item: any) => item.name === 'lookup_index2'); + + expect(item1).toMatchObject({ + name: 'lookup_index1', + mode: 'lookup', + aliases: [], + }); + + item2.aliases.sort(); + + expect(item2).toMatchObject({ + name: 'lookup_index2', + mode: 'lookup', + aliases: ['lookup_index2_alias1', 'lookup_index2_alias2'], + }); + }); +}); diff --git a/src/platform/plugins/shared/esql/server/services/integration_tests/testbed.ts b/src/platform/plugins/shared/esql/server/services/integration_tests/testbed.ts new file mode 100644 index 000000000000..95de7fdf562c --- /dev/null +++ b/src/platform/plugins/shared/esql/server/services/integration_tests/testbed.ts @@ -0,0 +1,86 @@ +/* + * 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 { ElasticsearchClient } from '@kbn/core/server'; +import { + createTestServers, + request, + type TestUtils, + type TestElasticsearchUtils, + type TestKibanaUtils, +} from '@kbn/core-test-helpers-kbn-server'; + +export class EsqlServiceTestbed { + public servers?: TestUtils; + public es?: TestElasticsearchUtils; + public kibana?: TestKibanaUtils; + + public async start() { + this.servers = createTestServers({ adjustTimeout: jest.setTimeout }); + this.es = await this.servers.startES(); + this.kibana = await this.servers.startKibana(); + } + + public async stop() { + await this.kibana?.root?.shutdown(); + await this.kibana?.stop(); + await this.es?.stop(); + } + + public esClient(): ElasticsearchClient { + const client = this.kibana?.coreStart.elasticsearch.client.asInternalUser; + + if (!client) { + throw new Error('ES client not available, make sure to call `.start()`'); + } + + return client; + } + + public async setupLookupIndices() { + const client = this.esClient(); + + await client.indices.create({ + index: 'lookup_index1', + body: { + settings: { + 'index.mode': 'lookup', + }, + mappings: { + properties: { + field1: { type: 'keyword' }, + }, + }, + }, + }); + + // Lookup index with aliases + await client.indices.create({ + index: 'lookup_index2', + body: { + settings: { + 'index.mode': 'lookup', + }, + aliases: { + lookup_index2_alias1: {}, + lookup_index2_alias2: {}, + }, + mappings: { + properties: { + field2: { type: 'keyword' }, + }, + }, + }, + }); + } + + public readonly GET = (path: string) => { + return request.get(this.kibana!.root, path).set('x-elastic-internal-origin', 'esql-test'); + }; +} diff --git a/src/platform/plugins/shared/esql/server/services/types.ts b/src/platform/plugins/shared/esql/server/services/types.ts new file mode 100644 index 000000000000..025a627653d5 --- /dev/null +++ b/src/platform/plugins/shared/esql/server/services/types.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export interface JoinIndicesAutocompleteResult { + indices: JoinIndexAutocompleteItem[]; +} + +export interface JoinIndexAutocompleteItem { + name: string; + mode: 'lookup' | string; + aliases: string[]; +} diff --git a/src/platform/plugins/shared/esql/tsconfig.json b/src/platform/plugins/shared/esql/tsconfig.json index 699fce107f1d..bacd508aa7ea 100644 --- a/src/platform/plugins/shared/esql/tsconfig.json +++ b/src/platform/plugins/shared/esql/tsconfig.json @@ -26,6 +26,7 @@ "@kbn/usage-collection-plugin", "@kbn/content-management-plugin", "@kbn/kibana-utils-plugin", + "@kbn/core-test-helpers-kbn-server", ], "exclude": [ "target/**/*", diff --git a/src/plugins/share/public/components/tabs/embed/embed_content.test.tsx b/src/plugins/share/public/components/tabs/embed/embed_content.test.tsx index ee9fdf01588d..edbb9ebb4799 100644 --- a/src/plugins/share/public/components/tabs/embed/embed_content.test.tsx +++ b/src/plugins/share/public/components/tabs/embed/embed_content.test.tsx @@ -18,12 +18,7 @@ describe('Share modal embed content tab', () => { beforeEach(() => { component = mountWithIntl( - <EmbedContent - isDirty={false} - objectType="dashboard" - setIsNotSaved={() => jest.fn()} - shareableUrl="/home#/" - /> + <EmbedContent isDirty={false} objectType="dashboard" shareableUrl="/home#/" /> ); }); diff --git a/src/plugins/share/public/components/tabs/embed/embed_content.tsx b/src/plugins/share/public/components/tabs/embed/embed_content.tsx index 5a9163097c8d..a3bc6048e64e 100644 --- a/src/plugins/share/public/components/tabs/embed/embed_content.tsx +++ b/src/plugins/share/public/components/tabs/embed/embed_content.tsx @@ -34,7 +34,6 @@ type EmbedProps = Pick< | 'objectType' | 'isDirty' > & { - setIsNotSaved: () => void; objectConfig?: ShareContextObjectTypeConfig; }; @@ -55,7 +54,6 @@ export const EmbedContent = ({ shareableUrl, objectType, objectConfig = {}, - setIsNotSaved, isDirty, }: EmbedProps) => { const isMounted = useMountedState(); @@ -67,10 +65,6 @@ export const EmbedContent = ({ const [anonymousAccessParameters] = useState<AnonymousAccessState['accessURLParameters']>(null); const [usePublicUrl] = useState<boolean>(false); - useEffect(() => { - if (objectType !== 'dashboard') setIsNotSaved(); - }, [url, setIsNotSaved, objectType]); - const makeUrlEmbeddable = useCallback((tempUrl: string): string => { const embedParam = '?embed=true'; const urlHasQueryString = tempUrl.indexOf('?') !== -1; diff --git a/src/plugins/share/public/components/tabs/embed/index.tsx b/src/plugins/share/public/components/tabs/embed/index.tsx index 44d61833268c..3c74ce3fe0b1 100644 --- a/src/plugins/share/public/components/tabs/embed/index.tsx +++ b/src/plugins/share/public/components/tabs/embed/index.tsx @@ -8,35 +8,13 @@ */ import { i18n } from '@kbn/i18n'; -import React, { useCallback } from 'react'; +import React from 'react'; import { type IModalTabDeclaration } from '@kbn/shared-ux-tabbed-modal'; import { EmbedContent } from './embed_content'; import { useShareTabsContext } from '../../context'; -const EMBED_TAB_ACTIONS = { - SET_EMBED_URL: 'SET_EMBED_URL', - SET_IS_NOT_SAVED: 'SET_IS_NOT_SAVED', -}; - type IEmbedTab = IModalTabDeclaration<{ url: string; isNotSaved: boolean }>; -const embedTabReducer: IEmbedTab['reducer'] = (state = { url: '', isNotSaved: false }, action) => { - switch (action.type) { - case EMBED_TAB_ACTIONS.SET_IS_NOT_SAVED: - return { - ...state, - isNotSaved: action.payload, - }; - case EMBED_TAB_ACTIONS.SET_IS_NOT_SAVED: - return { - ...state, - isNotSaved: action.payload, - }; - default: - return state; - } -}; - const EmbedTabContent: NonNullable<IEmbedTab['content']> = ({ state, dispatch }) => { const { embedUrlParamExtensions, @@ -47,13 +25,6 @@ const EmbedTabContent: NonNullable<IEmbedTab['content']> = ({ state, dispatch }) isDirty, } = useShareTabsContext()!; - const setIsNotSaved = useCallback(() => { - dispatch({ - type: EMBED_TAB_ACTIONS.SET_IS_NOT_SAVED, - payload: objectType === 'dashboard' ? isDirty : false, - }); - }, [dispatch, objectType, isDirty]); - return ( <EmbedContent {...{ @@ -62,8 +33,6 @@ const EmbedTabContent: NonNullable<IEmbedTab['content']> = ({ state, dispatch }) shareableUrl, objectType, objectConfig: objectTypeMeta?.config?.embed, - isNotSaved: state?.isNotSaved, - setIsNotSaved, isDirty, }} /> @@ -75,6 +44,5 @@ export const embedTab: IEmbedTab = { name: i18n.translate('share.contextMenu.embedCodeTab', { defaultMessage: 'Embed', }), - reducer: embedTabReducer, content: EmbedTabContent, }; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_flyout/use_flyout_a11y.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_flyout/use_flyout_a11y.tsx index 84c47cc4bd06..18cc98fb94b8 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_flyout/use_flyout_a11y.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_flyout/use_flyout_a11y.tsx @@ -37,7 +37,7 @@ export const useFlyoutA11y = ({ isXlScreen }: { isXlScreen: boolean }) => { a11yProps: { ref: setFlyoutEl, role: isXlScreen ? 'dialog' : undefined, - tabindex: isXlScreen ? 0 : undefined, + tabIndex: isXlScreen ? 0 : undefined, 'aria-describedby': isXlScreen ? descriptionId : undefined, 'data-no-focus-lock': isXlScreen || undefined, }, diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 87b8fe406263..1bda105ba07a 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -128,7 +128,6 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'data_visualizer.resultLinks.fileBeat.enabled (boolean)', 'dev_tools.deeplinks.navLinkStatus (string?)', 'discover.experimental.enabledProfiles (array?)', - 'enterpriseSearch.canDeployEntSearch (boolean?)', 'enterpriseSearch.host (string?)', 'enterpriseSearch.ui.enabled (boolean?)', 'home.disableWelcomeScreen (boolean?)', diff --git a/tsconfig.base.json b/tsconfig.base.json index 9b760a854bb5..15e2e250e0d0 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1160,8 +1160,8 @@ "@kbn/lint-packages-cli/*": ["packages/kbn-lint-packages-cli/*"], "@kbn/lint-ts-projects-cli": ["packages/kbn-lint-ts-projects-cli"], "@kbn/lint-ts-projects-cli/*": ["packages/kbn-lint-ts-projects-cli/*"], - "@kbn/lists-plugin": ["x-pack/plugins/lists"], - "@kbn/lists-plugin/*": ["x-pack/plugins/lists/*"], + "@kbn/lists-plugin": ["x-pack/solutions/security/plugins/lists"], + "@kbn/lists-plugin/*": ["x-pack/solutions/security/plugins/lists/*"], "@kbn/llm-tasks-plugin": ["x-pack/platform/plugins/shared/ai_infra/llm_tasks"], "@kbn/llm-tasks-plugin/*": ["x-pack/platform/plugins/shared/ai_infra/llm_tasks/*"], "@kbn/locator-examples-plugin": ["examples/locator_examples"], @@ -1568,8 +1568,8 @@ "@kbn/screenshot-mode-plugin/*": ["src/plugins/screenshot_mode/*"], "@kbn/screenshotting-example-plugin": ["x-pack/examples/screenshotting_example"], "@kbn/screenshotting-example-plugin/*": ["x-pack/examples/screenshotting_example/*"], - "@kbn/screenshotting-plugin": ["x-pack/plugins/screenshotting"], - "@kbn/screenshotting-plugin/*": ["x-pack/plugins/screenshotting/*"], + "@kbn/screenshotting-plugin": ["x-pack/platform/plugins/shared/screenshotting"], + "@kbn/screenshotting-plugin/*": ["x-pack/platform/plugins/shared/screenshotting/*"], "@kbn/screenshotting-server": ["packages/kbn-screenshotting-server"], "@kbn/screenshotting-server/*": ["packages/kbn-screenshotting-server/*"], "@kbn/search-api-keys-components": ["packages/kbn-search-api-keys-components"], @@ -1656,46 +1656,46 @@ "@kbn/security-test-endpoints-plugin/*": ["x-pack/test/security_functional/plugins/test_endpoints/*"], "@kbn/security-ui-components": ["x-pack/packages/security/ui_components"], "@kbn/security-ui-components/*": ["x-pack/packages/security/ui_components/*"], - "@kbn/securitysolution-autocomplete": ["packages/kbn-securitysolution-autocomplete"], - "@kbn/securitysolution-autocomplete/*": ["packages/kbn-securitysolution-autocomplete/*"], + "@kbn/securitysolution-autocomplete": ["x-pack/solutions/security/packages/kbn-securitysolution-autocomplete"], + "@kbn/securitysolution-autocomplete/*": ["x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/*"], "@kbn/securitysolution-data-table": ["x-pack/solutions/security/packages/data_table"], "@kbn/securitysolution-data-table/*": ["x-pack/solutions/security/packages/data_table/*"], "@kbn/securitysolution-ecs": ["src/platform/packages/shared/kbn-securitysolution-ecs"], "@kbn/securitysolution-ecs/*": ["src/platform/packages/shared/kbn-securitysolution-ecs/*"], - "@kbn/securitysolution-endpoint-exceptions-common": ["packages/kbn-securitysolution-endpoint-exceptions-common"], - "@kbn/securitysolution-endpoint-exceptions-common/*": ["packages/kbn-securitysolution-endpoint-exceptions-common/*"], - "@kbn/securitysolution-es-utils": ["packages/kbn-securitysolution-es-utils"], - "@kbn/securitysolution-es-utils/*": ["packages/kbn-securitysolution-es-utils/*"], - "@kbn/securitysolution-exception-list-components": ["packages/kbn-securitysolution-exception-list-components"], - "@kbn/securitysolution-exception-list-components/*": ["packages/kbn-securitysolution-exception-list-components/*"], - "@kbn/securitysolution-exceptions-common": ["packages/kbn-securitysolution-exceptions-common"], - "@kbn/securitysolution-exceptions-common/*": ["packages/kbn-securitysolution-exceptions-common/*"], - "@kbn/securitysolution-hook-utils": ["packages/kbn-securitysolution-hook-utils"], - "@kbn/securitysolution-hook-utils/*": ["packages/kbn-securitysolution-hook-utils/*"], - "@kbn/securitysolution-io-ts-alerting-types": ["packages/kbn-securitysolution-io-ts-alerting-types"], - "@kbn/securitysolution-io-ts-alerting-types/*": ["packages/kbn-securitysolution-io-ts-alerting-types/*"], - "@kbn/securitysolution-io-ts-list-types": ["packages/kbn-securitysolution-io-ts-list-types"], - "@kbn/securitysolution-io-ts-list-types/*": ["packages/kbn-securitysolution-io-ts-list-types/*"], - "@kbn/securitysolution-io-ts-types": ["packages/kbn-securitysolution-io-ts-types"], - "@kbn/securitysolution-io-ts-types/*": ["packages/kbn-securitysolution-io-ts-types/*"], - "@kbn/securitysolution-io-ts-utils": ["packages/kbn-securitysolution-io-ts-utils"], - "@kbn/securitysolution-io-ts-utils/*": ["packages/kbn-securitysolution-io-ts-utils/*"], - "@kbn/securitysolution-list-api": ["packages/kbn-securitysolution-list-api"], - "@kbn/securitysolution-list-api/*": ["packages/kbn-securitysolution-list-api/*"], - "@kbn/securitysolution-list-constants": ["packages/kbn-securitysolution-list-constants"], - "@kbn/securitysolution-list-constants/*": ["packages/kbn-securitysolution-list-constants/*"], - "@kbn/securitysolution-list-hooks": ["packages/kbn-securitysolution-list-hooks"], - "@kbn/securitysolution-list-hooks/*": ["packages/kbn-securitysolution-list-hooks/*"], - "@kbn/securitysolution-list-utils": ["packages/kbn-securitysolution-list-utils"], - "@kbn/securitysolution-list-utils/*": ["packages/kbn-securitysolution-list-utils/*"], - "@kbn/securitysolution-lists-common": ["packages/kbn-securitysolution-lists-common"], - "@kbn/securitysolution-lists-common/*": ["packages/kbn-securitysolution-lists-common/*"], - "@kbn/securitysolution-rules": ["packages/kbn-securitysolution-rules"], - "@kbn/securitysolution-rules/*": ["packages/kbn-securitysolution-rules/*"], - "@kbn/securitysolution-t-grid": ["packages/kbn-securitysolution-t-grid"], - "@kbn/securitysolution-t-grid/*": ["packages/kbn-securitysolution-t-grid/*"], - "@kbn/securitysolution-utils": ["packages/kbn-securitysolution-utils"], - "@kbn/securitysolution-utils/*": ["packages/kbn-securitysolution-utils/*"], + "@kbn/securitysolution-endpoint-exceptions-common": ["x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common"], + "@kbn/securitysolution-endpoint-exceptions-common/*": ["x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/*"], + "@kbn/securitysolution-es-utils": ["src/platform/packages/shared/kbn-securitysolution-es-utils"], + "@kbn/securitysolution-es-utils/*": ["src/platform/packages/shared/kbn-securitysolution-es-utils/*"], + "@kbn/securitysolution-exception-list-components": ["x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components"], + "@kbn/securitysolution-exception-list-components/*": ["x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/*"], + "@kbn/securitysolution-exceptions-common": ["x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common"], + "@kbn/securitysolution-exceptions-common/*": ["x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/*"], + "@kbn/securitysolution-hook-utils": ["x-pack/solutions/security/packages/kbn-securitysolution-hook-utils"], + "@kbn/securitysolution-hook-utils/*": ["x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/*"], + "@kbn/securitysolution-io-ts-alerting-types": ["x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types"], + "@kbn/securitysolution-io-ts-alerting-types/*": ["x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/*"], + "@kbn/securitysolution-io-ts-list-types": ["x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types"], + "@kbn/securitysolution-io-ts-list-types/*": ["x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/*"], + "@kbn/securitysolution-io-ts-types": ["src/platform/packages/shared/kbn-securitysolution-io-ts-types"], + "@kbn/securitysolution-io-ts-types/*": ["src/platform/packages/shared/kbn-securitysolution-io-ts-types/*"], + "@kbn/securitysolution-io-ts-utils": ["src/platform/packages/shared/kbn-securitysolution-io-ts-utils"], + "@kbn/securitysolution-io-ts-utils/*": ["src/platform/packages/shared/kbn-securitysolution-io-ts-utils/*"], + "@kbn/securitysolution-list-api": ["x-pack/solutions/security/packages/kbn-securitysolution-list-api"], + "@kbn/securitysolution-list-api/*": ["x-pack/solutions/security/packages/kbn-securitysolution-list-api/*"], + "@kbn/securitysolution-list-constants": ["x-pack/solutions/security/packages/kbn-securitysolution-list-constants"], + "@kbn/securitysolution-list-constants/*": ["x-pack/solutions/security/packages/kbn-securitysolution-list-constants/*"], + "@kbn/securitysolution-list-hooks": ["x-pack/solutions/security/packages/kbn-securitysolution-list-hooks"], + "@kbn/securitysolution-list-hooks/*": ["x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/*"], + "@kbn/securitysolution-list-utils": ["x-pack/solutions/security/packages/kbn-securitysolution-list-utils"], + "@kbn/securitysolution-list-utils/*": ["x-pack/solutions/security/packages/kbn-securitysolution-list-utils/*"], + "@kbn/securitysolution-lists-common": ["x-pack/solutions/security/packages/kbn-securitysolution-lists-common"], + "@kbn/securitysolution-lists-common/*": ["x-pack/solutions/security/packages/kbn-securitysolution-lists-common/*"], + "@kbn/securitysolution-rules": ["src/platform/packages/shared/kbn-securitysolution-rules"], + "@kbn/securitysolution-rules/*": ["src/platform/packages/shared/kbn-securitysolution-rules/*"], + "@kbn/securitysolution-t-grid": ["x-pack/solutions/security/packages/kbn-securitysolution-t-grid"], + "@kbn/securitysolution-t-grid/*": ["x-pack/solutions/security/packages/kbn-securitysolution-t-grid/*"], + "@kbn/securitysolution-utils": ["x-pack/solutions/security/packages/kbn-securitysolution-utils"], + "@kbn/securitysolution-utils/*": ["x-pack/solutions/security/packages/kbn-securitysolution-utils/*"], "@kbn/server-http-tools": ["packages/kbn-server-http-tools"], "@kbn/server-http-tools/*": ["packages/kbn-server-http-tools/*"], "@kbn/server-route-repository": ["src/platform/packages/shared/kbn-server-route-repository"], diff --git a/versions.json b/versions.json index 126e39ed0bad..e7ca2b8539bf 100644 --- a/versions.json +++ b/versions.json @@ -19,7 +19,7 @@ "previousMajor": true }, { - "version": "8.16.2", + "version": "8.16.3", "branch": "8.16", "previousMajor": true }, diff --git a/x-pack/.gitignore b/x-pack/.gitignore index 97efbef318c9..918a6a7d1c38 100644 --- a/x-pack/.gitignore +++ b/x-pack/.gitignore @@ -6,7 +6,7 @@ /test/functional/apps/**/reports/session /test/reporting/configs/failure_debug/ /plugins/reporting/.chromium/ -/plugins/screenshotting/chromium/ +/platform/plugins/shared/screenshotting/chromium/ /plugins/reporting/.phantom/ /.aws-config.json /.env diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index a4482fdb8c4a..c01b9ef40aed 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -75,7 +75,7 @@ "xpack.licenseApiGuard": "platform/plugins/private/license_api_guard", "xpack.licenseMgmt": "platform/plugins/shared/license_management", "xpack.licensing": "plugins/licensing", - "xpack.lists": "plugins/lists", + "xpack.lists": "solutions/security/plugins/lists", "xpack.logstash": [ "plugins/logstash" ], @@ -128,7 +128,7 @@ "platform/plugins/private/rollup" ], "xpack.runtimeFields": "platform/plugins/private/runtime_fields", - "xpack.screenshotting": "plugins/screenshotting", + "xpack.screenshotting": "platform/plugins/shared/screenshotting", "xpack.searchSharedUI": "packages/search/shared_ui", "xpack.searchHomepage": "plugins/search_homepage", "xpack.searchIndices": "plugins/search_indices", diff --git a/x-pack/packages/observability/logs_overview/kibana.jsonc b/x-pack/packages/observability/logs_overview/kibana.jsonc index 34d8ac98a525..1709e01f926e 100644 --- a/x-pack/packages/observability/logs_overview/kibana.jsonc +++ b/x-pack/packages/observability/logs_overview/kibana.jsonc @@ -4,6 +4,6 @@ "owner": [ "@elastic/obs-ux-logs-team" ], - "group": "observability", - "visibility": "private" + "group": "platform", + "visibility": "shared" } 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 603192fb96db..134b0f02811f 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 @@ -84,11 +84,14 @@ export { type InferenceTaskErrorEvent, type InferenceTaskInternalError, type InferenceTaskRequestError, + type InferenceTaskAbortedError, createInferenceInternalError, createInferenceRequestError, + createInferenceRequestAbortedError, isInferenceError, isInferenceInternalError, isInferenceRequestError, + isInferenceRequestAbortedError, } from './src/errors'; export { truncateList } from './src/truncate_list'; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/chat_complete/api.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/chat_complete/api.ts index cb91f4e53e8a..4e29d5f7dad0 100644 --- a/x-pack/platform/packages/shared/ai-infra/inference-common/src/chat_complete/api.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/chat_complete/api.ts @@ -93,6 +93,10 @@ export type ChatCompleteOptions< * Function calling mode, defaults to "native". */ functionCalling?: FunctionCallingMode; + /** + * Optional signal that can be used to forcefully abort the request. + */ + abortSignal?: AbortSignal; } & TToolOptions; /** 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 5a99adc4321d..472ed50e231f 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 @@ -13,6 +13,7 @@ import { InferenceTaskEventBase, InferenceTaskEventType } from './inference_task export enum InferenceTaskErrorCode { internalError = 'internalError', requestError = 'requestError', + abortedError = 'requestAborted', } /** @@ -46,16 +47,37 @@ export type InferenceTaskErrorEvent = InferenceTaskEventBase<InferenceTaskEventT }; }; +/** + * Inference error thrown when an unexpected internal error occurs while handling the request. + */ export type InferenceTaskInternalError = InferenceTaskError< InferenceTaskErrorCode.internalError, Record<string, any> >; +/** + * Inference error thrown when the request was considered invalid. + * + * Some example of reasons for invalid requests would be: + * - no connector matching the provided connectorId + * - invalid connector type for the provided connectorId + */ export type InferenceTaskRequestError = InferenceTaskError< InferenceTaskErrorCode.requestError, { status: number } >; +/** + * Inference error thrown when the request was aborted. + * + * Request abortion occurs when providing an abort signal and firing it + * before the call to the LLM completes. + */ +export type InferenceTaskAbortedError = InferenceTaskError< + InferenceTaskErrorCode.abortedError, + { status: number } +>; + export function createInferenceInternalError( message = 'An internal error occurred', meta?: Record<string, any> @@ -72,16 +94,38 @@ export function createInferenceRequestError( }); } +export function createInferenceRequestAbortedError(): InferenceTaskAbortedError { + return new InferenceTaskError(InferenceTaskErrorCode.abortedError, 'Request was aborted', { + status: 499, + }); +} + +/** + * Check if the given error is an {@link InferenceTaskError} + */ export function isInferenceError( error: unknown ): error is InferenceTaskError<string, Record<string, any> | undefined> { return error instanceof InferenceTaskError; } +/** + * Check if the given error is an {@link InferenceTaskInternalError} + */ export function isInferenceInternalError(error: unknown): error is InferenceTaskInternalError { return isInferenceError(error) && error.code === InferenceTaskErrorCode.internalError; } +/** + * Check if the given error is an {@link InferenceTaskRequestError} + */ export function isInferenceRequestError(error: unknown): error is InferenceTaskRequestError { return isInferenceError(error) && error.code === InferenceTaskErrorCode.requestError; } + +/** + * Check if the given error is an {@link InferenceTaskAbortedError} + */ +export function isInferenceRequestAbortedError(error: unknown): error is InferenceTaskAbortedError { + return isInferenceError(error) && error.code === InferenceTaskErrorCode.abortedError; +} diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/output/api.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/output/api.ts index cd90394cd67d..3ae4a6a07ee2 100644 --- a/x-pack/platform/packages/shared/ai-infra/inference-common/src/output/api.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/output/api.ts @@ -96,7 +96,10 @@ export interface OutputOptions< * Defaults to false. */ stream?: TStream; - + /** + * Optional signal that can be used to forcefully abort the request. + */ + abortSignal?: AbortSignal; /** * Optional configuration for retrying the call if an error occurs. */ diff --git a/x-pack/platform/packages/shared/kbn-cloud-security-posture/common/schema/graph/v1.ts b/x-pack/platform/packages/shared/kbn-cloud-security-posture/common/schema/graph/v1.ts index 076c685aca5b..5b1a48cf940b 100644 --- a/x-pack/platform/packages/shared/kbn-cloud-security-posture/common/schema/graph/v1.ts +++ b/x-pack/platform/packages/shared/kbn-cloud-security-posture/common/schema/graph/v1.ts @@ -12,7 +12,9 @@ export const graphRequestSchema = schema.object({ nodesLimit: schema.maybe(schema.number()), showUnknownTarget: schema.maybe(schema.boolean()), query: schema.object({ - eventIds: schema.arrayOf(schema.string()), + originEventIds: schema.arrayOf( + schema.object({ id: schema.string(), isAlert: schema.boolean() }) + ), // TODO: use zod for range validation instead of config schema start: schema.oneOf([schema.number(), schema.string()]), end: schema.oneOf([schema.number(), schema.string()]), diff --git a/x-pack/platform/plugins/private/transform/public/alerting/transform_health_rule_type/transform_selector_control.test.tsx b/x-pack/platform/plugins/private/transform/public/alerting/transform_health_rule_type/transform_selector_control.test.tsx new file mode 100644 index 000000000000..79d21ca95d4e --- /dev/null +++ b/x-pack/platform/plugins/private/transform/public/alerting/transform_health_rule_type/transform_selector_control.test.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import type { TransformSelectorControlProps } from './transform_selector_control'; +import { TransformSelectorControl } from './transform_selector_control'; + +describe('TransformSelectorControl', () => { + const defaultProps: TransformSelectorControlProps = { + label: 'Select Transforms', + errors: [], + onChange: jest.fn(), + selectedOptions: [], + options: ['transform1', 'transform2'], + allowSelectAll: true, + }; + + it('renders without crashing', () => { + const { getByLabelText } = render(<TransformSelectorControl {...defaultProps} />); + expect(getByLabelText('Select Transforms')).toBeInTheDocument(); + }); + + it('displays options correctly', () => { + const { getByText } = render(<TransformSelectorControl {...defaultProps} />); + fireEvent.click(getByText('Select Transforms')); + expect(getByText('transform1')).toBeInTheDocument(); + expect(getByText('transform2')).toBeInTheDocument(); + expect(getByText('*')).toBeInTheDocument(); + }); + + it('calls onChange with selected options', () => { + const { getByText } = render(<TransformSelectorControl {...defaultProps} />); + fireEvent.click(getByText('Select Transforms')); + fireEvent.click(getByText('transform1')); + expect(defaultProps.onChange).toHaveBeenCalledWith(['transform1']); + }); + + it('only allows wildcards as custom options', () => { + const { getByText, getByTestId } = render(<TransformSelectorControl {...defaultProps} />); + fireEvent.click(getByText('Select Transforms')); + const input = getByTestId('comboBoxSearchInput'); + + fireEvent.change(input, { target: { value: 'custom' } }); + fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' }); + expect(defaultProps.onChange).not.toHaveBeenCalledWith(['custom']); + + fireEvent.change(input, { target: { value: 'custom*' } }); + fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' }); + expect(defaultProps.onChange).toHaveBeenCalledWith(['custom*']); + }); + + it('displays errors correctly', () => { + const errorProps = { ...defaultProps, errors: ['Error message'] }; + const { getByText } = render(<TransformSelectorControl {...errorProps} />); + expect(getByText('Error message')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/platform/plugins/private/transform/public/alerting/transform_health_rule_type/transform_selector_control.tsx b/x-pack/platform/plugins/private/transform/public/alerting/transform_health_rule_type/transform_selector_control.tsx index 97c7c74823c0..bbb1c77e50ed 100644 --- a/x-pack/platform/plugins/private/transform/public/alerting/transform_health_rule_type/transform_selector_control.tsx +++ b/x-pack/platform/plugins/private/transform/public/alerting/transform_health_rule_type/transform_selector_control.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import type { EuiComboBoxProps } from '@elastic/eui'; +import type { EuiComboBoxOptionsListProps, EuiComboBoxProps } from '@elastic/eui'; import { EuiComboBox, EuiFormRow } from '@elastic/eui'; import type { FC } from 'react'; -import React, { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; import { isDefined } from '@kbn/ml-is-defined'; +import { i18n } from '@kbn/i18n'; import { ALL_TRANSFORMS_SELECTION } from '../../../common/constants'; export interface TransformSelectorControlProps { @@ -33,6 +34,8 @@ export const TransformSelectorControl: FC<TransformSelectorControlProps> = ({ options, allowSelectAll = false, }) => { + const [allowCustomOptions, setAllowCustomOptions] = useState(false); + const onSelectionChange: EuiComboBoxProps<string>['onChange'] = ((selectionUpdate) => { if (!selectionUpdate?.length) { onChange([]); @@ -50,6 +53,12 @@ export const TransformSelectorControl: FC<TransformSelectorControlProps> = ({ ); }) as Exclude<EuiComboBoxProps<string>['onChange'], undefined>; + const onCreateOption = allowCustomOptions + ? (((searchValue) => { + onChange([...selectedOptions, searchValue]); + }) as EuiComboBoxOptionsListProps<string>['onCreateOption']) + : undefined; + const selectedOptionsEui = useMemo(() => convertToEuiOptions(selectedOptions), [selectedOptions]); const optionsEui = useMemo(() => { return convertToEuiOptions(allowSelectAll ? [ALL_TRANSFORMS_SELECTION, ...options] : options); @@ -58,6 +67,17 @@ export const TransformSelectorControl: FC<TransformSelectorControlProps> = ({ return ( <EuiFormRow fullWidth label={label} isInvalid={!!errors?.length} error={errors}> <EuiComboBox<string> + onSearchChange={(searchValue, hasMatchingOption) => { + setAllowCustomOptions(!hasMatchingOption && searchValue.includes('*')); + }} + onCreateOption={onCreateOption} + customOptionText={i18n.translate( + 'xpack.transform.alertTypes.transformHealth.customOptionText', + { + defaultMessage: 'Include {searchValuePlaceholder} wildcard', + values: { searchValuePlaceholder: '{searchValue}' }, + } + )} singleSelection={false} selectedOptions={selectedOptionsEui} options={optionsEui} diff --git a/x-pack/platform/plugins/private/transform/public/app/sections/transform_management/components/transform_list/expanded_row_json_pane.tsx b/x-pack/platform/plugins/private/transform/public/app/sections/transform_management/components/transform_list/expanded_row_json_pane.tsx index 48e17d915722..60c47ecc81f8 100644 --- a/x-pack/platform/plugins/private/transform/public/app/sections/transform_management/components/transform_list/expanded_row_json_pane.tsx +++ b/x-pack/platform/plugins/private/transform/public/app/sections/transform_management/components/transform_list/expanded_row_json_pane.tsx @@ -16,6 +16,12 @@ interface Props { } export const ExpandedRowJsonPane: FC<Props> = ({ json }) => { + // exclude alerting rules from the JSON + if ('alerting_rules' in json) { + const { alerting_rules: alertingRules, ...rest } = json; + json = rest; + } + return ( <div data-test-subj="transformJsonTabContent"> <EuiFlexGroup> diff --git a/x-pack/platform/plugins/private/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.test.ts b/x-pack/platform/plugins/private/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.test.ts index ff502c9fcdc2..a799fbe0499f 100644 --- a/x-pack/platform/plugins/private/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.test.ts +++ b/x-pack/platform/plugins/private/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.test.ts @@ -5,16 +5,18 @@ * 2.0. */ -import { transformHealthServiceProvider } from './transform_health_service'; -import type { ElasticsearchClient } from '@kbn/core/server'; -import type { RulesClient } from '@kbn/alerting-plugin/server'; -import type { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; -import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; -import { rulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; import type { TransformGetTransformResponse, TransformGetTransformStatsResponse, + TransformGetTransformTransformSummary, } from '@elastic/elasticsearch/lib/api/types'; +import type { FindResult, RulesClient } from '@kbn/alerting-plugin/server'; +import { rulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; +import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +import type { ElasticsearchClient } from '@kbn/core/server'; +import type { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; +import { transformHealthServiceProvider } from './transform_health_service'; +import type { TransformHealthRuleParams } from './schema'; describe('transformHealthServiceProvider', () => { let esClient: jest.Mocked<ElasticsearchClient>; @@ -24,20 +26,48 @@ describe('transformHealthServiceProvider', () => { beforeEach(() => { esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - (esClient.transform.getTransform as jest.Mock).mockResolvedValue({ - count: 3, - transforms: [ - // Mock continuous transforms - ...new Array(102).fill(null).map((_, i) => ({ - id: `transform${i}`, - sync: true, - })), - { - id: 'transform102', - sync: false, - }, - ], - } as unknown as TransformGetTransformResponse); + (esClient.transform.getTransform as jest.Mock).mockImplementation( + async ({ transform_id: transformId }) => { + if (transformId === 'transform4,transform6,transform6*') { + // arrangement for exclude transforms + return { + transforms: [ + { + id: `transform4`, + sync: true, + }, + { + id: `transform6`, + sync: true, + }, + ...new Array(10).fill(null).map((_, i) => ({ + id: `transform6${i}`, + sync: true, + })), + ], + } as unknown as TransformGetTransformResponse; + } else { + return { + transforms: [ + // Mock continuous transforms + ...new Array(102).fill(null).map((_, i) => ({ + id: `transform${i}`, + sync: { + time: { + field: 'order_date', + delay: '60s', + }, + }, + })), + { + id: 'transform102', + }, + ], + } as unknown as TransformGetTransformResponse; + } + } + ); + (esClient.transform.getTransformStats as jest.Mock).mockResolvedValue({ count: 2, transforms: [{}], @@ -57,19 +87,27 @@ describe('transformHealthServiceProvider', () => { const service = transformHealthServiceProvider({ esClient, rulesClient, fieldFormatsRegistry }); const result = await service.getHealthChecksResults({ includeTransforms: ['*'], - excludeTransforms: ['transform4', 'transform6', 'transform62'], + excludeTransforms: ['transform4', 'transform6', 'transform6*'], testsConfig: null, }); + expect(esClient.transform.getTransform).toHaveBeenCalledTimes(2); + + expect(esClient.transform.getTransform).toHaveBeenCalledWith({ + allow_no_match: true, + size: 1000, + }); expect(esClient.transform.getTransform).toHaveBeenCalledWith({ + transform_id: 'transform4,transform6,transform6*', allow_no_match: true, size: 1000, }); + expect(esClient.transform.getTransformStats).toHaveBeenCalledTimes(1); expect(esClient.transform.getTransformStats).toHaveBeenNthCalledWith(1, { basic: true, transform_id: - 'transform0,transform1,transform2,transform3,transform5,transform7,transform8,transform9,transform10,transform11,transform12,transform13,transform14,transform15,transform16,transform17,transform18,transform19,transform20,transform21,transform22,transform23,transform24,transform25,transform26,transform27,transform28,transform29,transform30,transform31,transform32,transform33,transform34,transform35,transform36,transform37,transform38,transform39,transform40,transform41,transform42,transform43,transform44,transform45,transform46,transform47,transform48,transform49,transform50,transform51,transform52,transform53,transform54,transform55,transform56,transform57,transform58,transform59,transform60,transform61,transform63,transform64,transform65,transform66,transform67,transform68,transform69,transform70,transform71,transform72,transform73,transform74,transform75,transform76,transform77,transform78,transform79,transform80,transform81,transform82,transform83,transform84,transform85,transform86,transform87,transform88,transform89,transform90,transform91,transform92,transform93,transform94,transform95,transform96,transform97,transform98,transform99,transform100,transform101', + 'transform0,transform1,transform2,transform3,transform5,transform7,transform8,transform9,transform10,transform11,transform12,transform13,transform14,transform15,transform16,transform17,transform18,transform19,transform20,transform21,transform22,transform23,transform24,transform25,transform26,transform27,transform28,transform29,transform30,transform31,transform32,transform33,transform34,transform35,transform36,transform37,transform38,transform39,transform40,transform41,transform42,transform43,transform44,transform45,transform46,transform47,transform48,transform49,transform50,transform51,transform52,transform53,transform54,transform55,transform56,transform57,transform58,transform59,transform70,transform71,transform72,transform73,transform74,transform75,transform76,transform77,transform78,transform79,transform80,transform81,transform82,transform83,transform84,transform85,transform86,transform87,transform88,transform89,transform90,transform91,transform92,transform93,transform94,transform95,transform96,transform97,transform98,transform99,transform100,transform101', }); expect(result).toBeDefined(); @@ -126,4 +164,131 @@ describe('transformHealthServiceProvider', () => { 'Transform transform_with_a_very_long_id_that_result_in_long_url_for_sure_0, transform_with_a_very_long_id_that_result_in_long_url_for_sure_1, transform_with_a_very_long_id_that_result_in_long_url_for_sure_2, transform_with_a_very_long_id_that_result_in_long_url_for_sure_3, transform_with_a_very_long_id_that_result_in_long_url_for_sure_4, transform_with_a_very_long_id_that_result_in_long_url_for_sure_5, transform_with_a_very_long_id_that_result_in_long_url_for_sure_6, transform_with_a_very_long_id_that_result_in_long_url_for_sure_7, transform_with_a_very_long_id_that_result_in_long_url_for_sure_8, transform_with_a_very_long_id_that_result_in_long_url_for_sure_9, transform_with_a_very_long_id_that_result_in_long_url_for_sure_10, transform_with_a_very_long_id_that_result_in_long_url_for_sure_11, transform_with_a_very_long_id_that_result_in_long_url_for_sure_12, transform_with_a_very_long_id_that_result_in_long_url_for_sure_13, transform_with_a_very_long_id_that_result_in_long_url_for_sure_14, transform_with_a_very_long_id_that_result_in_long_url_for_sure_15, transform_with_a_very_long_id_that_result_in_long_url_for_sure_16, transform_with_a_very_long_id_that_result_in_long_url_for_sure_17, transform_with_a_very_long_id_that_result_in_long_url_for_sure_18, transform_with_a_very_long_id_that_result_in_long_url_for_sure_19, transform_with_a_very_long_id_that_result_in_long_url_for_sure_20, transform_with_a_very_long_id_that_result_in_long_url_for_sure_21, transform_with_a_very_long_id_that_result_in_long_url_for_sure_22, transform_with_a_very_long_id_that_result_in_long_url_for_sure_23, transform_with_a_very_long_id_that_result_in_long_url_for_sure_24, transform_with_a_very_long_id_that_result_in_long_url_for_sure_25, transform_with_a_very_long_id_that_result_in_long_url_for_sure_26, transform_with_a_very_long_id_that_result_in_long_url_for_sure_27, transform_with_a_very_long_id_that_result_in_long_url_for_sure_28, transform_with_a_very_long_id_that_result_in_long_url_for_sure_29, transform_with_a_very_long_id_that_result_in_long_url_for_sure_30, transform_with_a_very_long_id_that_result_in_long_url_for_sure_31, transform_with_a_very_long_id_that_result_in_long_url_for_sure_32, transform_with_a_very_long_id_that_result_in_long_url_for_sure_33, transform_with_a_very_long_id_that_result_in_long_url_for_sure_34, transform_with_a_very_long_id_that_result_in_long_url_for_sure_35, transform_with_a_very_long_id_that_result_in_long_url_for_sure_36, transform_with_a_very_long_id_that_result_in_long_url_for_sure_37, transform_with_a_very_long_id_that_result_in_long_url_for_sure_38, transform_with_a_very_long_id_that_result_in_long_url_for_sure_39, transform_with_a_very_long_id_that_result_in_long_url_for_sure_40, transform_with_a_very_long_id_that_result_in_long_url_for_sure_41, transform_with_a_very_long_id_that_result_in_long_url_for_sure_42, transform_with_a_very_long_id_that_result_in_long_url_for_sure_43, transform_with_a_very_long_id_that_result_in_long_url_for_sure_44, transform_with_a_very_long_id_that_result_in_long_url_for_sure_45, transform_with_a_very_long_id_that_result_in_long_url_for_sure_46, transform_with_a_very_long_id_that_result_in_long_url_for_sure_47, transform_with_a_very_long_id_that_result_in_long_url_for_sure_48, transform_with_a_very_long_id_that_result_in_long_url_for_sure_49, transform_with_a_very_long_id_that_result_in_long_url_for_sure_50, transform_with_a_very_long_id_that_result_in_long_url_for_sure_51, transform_with_a_very_long_id_that_result_in_long_url_for_sure_52, transform_with_a_very_long_id_that_result_in_long_url_for_sure_53, transform_with_a_very_long_id_that_result_in_long_url_for_sure_54, transform_with_a_very_long_id_that_result_in_long_url_for_sure_55, transform_with_a_very_long_id_that_result_in_long_url_for_sure_56, transform_with_a_very_long_id_that_result_in_long_url_for_sure_57, transform_with_a_very_long_id_that_result_in_long_url_for_sure_58, transform_with_a_very_long_id_that_result_in_long_url_for_sure_59 are not started.' ); }); + + describe('populateTransformsWithAssignedRules', () => { + it('should throw an error if rulesClient is missing', async () => { + const service = transformHealthServiceProvider({ esClient, fieldFormatsRegistry }); + + await expect(service.populateTransformsWithAssignedRules([])).rejects.toThrow( + 'Rules client is missing' + ); + }); + + it('should return an empty list if no transforms are provided', async () => { + const service = transformHealthServiceProvider({ + esClient, + rulesClient, + fieldFormatsRegistry, + }); + + const result = await service.populateTransformsWithAssignedRules([]); + expect(result).toEqual([]); + }); + + it('should return transforms with associated alerting rules', async () => { + const transforms = [ + { id: 'transform1', sync: {} }, + { id: 'transform2', sync: {} }, + { id: 'transform3', sync: {} }, + ] as TransformGetTransformTransformSummary[]; + + const rules = [ + { + id: 'rule1', + params: { + includeTransforms: ['transform1', 'transform2'], + excludeTransforms: [], + }, + }, + { + id: 'rule2', + params: { + includeTransforms: ['transform3'], + excludeTransforms: null, + }, + }, + ]; + + rulesClient.find.mockResolvedValue({ data: rules } as FindResult<TransformHealthRuleParams>); + + const service = transformHealthServiceProvider({ + esClient, + rulesClient, + fieldFormatsRegistry, + }); + + const result = await service.populateTransformsWithAssignedRules(transforms); + + expect(result).toEqual([ + { + id: 'transform1', + sync: {}, + alerting_rules: [rules[0]], + }, + { + id: 'transform2', + sync: {}, + alerting_rules: [rules[0]], + }, + { + id: 'transform3', + sync: {}, + alerting_rules: [rules[1]], + }, + ]); + }); + + it('should exclude transforms based on excludeTransforms parameter', async () => { + const transforms = [ + { id: 'transform1', sync: {} }, + { id: 'transform2', sync: {} }, + { id: 'transform3', sync: {} }, + ] as TransformGetTransformTransformSummary[]; + + const rules = [ + { + id: 'rule1', + params: { + includeTransforms: ['transform*'], + excludeTransforms: ['transform2'], + }, + }, + { + id: 'rule2', + params: { + includeTransforms: ['*'], + excludeTransforms: [], + }, + }, + ]; + + rulesClient.find.mockResolvedValue({ data: rules } as FindResult<TransformHealthRuleParams>); + + const service = transformHealthServiceProvider({ + esClient, + rulesClient, + fieldFormatsRegistry, + }); + + const result = await service.populateTransformsWithAssignedRules(transforms); + + expect(result).toEqual([ + { + id: 'transform1', + sync: {}, + alerting_rules: [rules[0], rules[1]], + }, + { + id: 'transform2', + sync: {}, + alerting_rules: [rules[1]], + }, + { + id: 'transform3', + sync: {}, + alerting_rules: [rules[0], rules[1]], + }, + ]); + }); + }); }); diff --git a/x-pack/platform/plugins/private/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts b/x-pack/platform/plugins/private/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts index 939d4e01a6f1..288cb86842db 100644 --- a/x-pack/platform/plugins/private/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts +++ b/x-pack/platform/plugins/private/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts @@ -38,11 +38,7 @@ interface TestResult { context: TransformHealthAlertContext; } -type Transform = estypes.TransformGetTransformTransformSummary & { - id: string; - description?: string; - sync: object; -}; +type Transform = estypes.TransformGetTransformTransformSummary; type TransformWithAlertingRules = Transform & { alerting_rules: TransformHealthAlertRule[] }; @@ -63,40 +59,44 @@ export function transformHealthServiceProvider({ * Resolves result transform selection. Only continuously running transforms are included. * @param includeTransforms * @param excludeTransforms - * @param skipIDsCheck */ const getResultsTransformIds = async ( includeTransforms: string[], - excludeTransforms: string[] | null, - skipIDsCheck = false + excludeTransforms: string[] | null ): Promise<Set<string>> => { const includeAll = includeTransforms.some((id) => id === ALL_TRANSFORMS_SELECTION); let resultTransformIds: string[] = []; - if (skipIDsCheck) { - resultTransformIds = includeTransforms; - } else { - // Fetch transforms to make sure assigned transforms exists. - const transformsResponse = ( - await esClient.transform.getTransform({ - ...(includeAll ? {} : { transform_id: includeTransforms.join(',') }), - allow_no_match: true, - size: 1000, - }) - ).transforms as Transform[]; - - transformsResponse.forEach((t) => { - transformsDict.set(t.id, t); - // Include only continuously running transforms. - if (t.sync) { - resultTransformIds.push(t.id); - } - }); - } + // Fetch transforms to make sure assigned transforms exists. + const transformsResponse = ( + await esClient.transform.getTransform({ + ...(includeAll ? {} : { transform_id: includeTransforms.join(',') }), + allow_no_match: true, + size: 1000, + }) + ).transforms as Transform[]; + + transformsResponse.forEach((t) => { + transformsDict.set(t.id, t); + // Include only continuously running transforms. + if (isContinuousTransform(t)) { + resultTransformIds.push(t.id); + } + }); if (excludeTransforms && excludeTransforms.length > 0) { - const excludeIdsSet = new Set(excludeTransforms); + let excludeIdsSet = new Set(excludeTransforms); + if (excludeTransforms.some((id) => id.includes('*'))) { + const excludeTransformResponse = ( + await esClient.transform.getTransform({ + transform_id: excludeTransforms.join(','), + allow_no_match: true, + size: 1000, + }) + ).transforms as Transform[]; + excludeIdsSet = new Set(excludeTransformResponse.map((t) => t.id)); + } resultTransformIds = resultTransformIds.filter((id) => !excludeIdsSet.has(id)); } @@ -381,13 +381,19 @@ export function transformHealthServiceProvider({ async populateTransformsWithAssignedRules( transforms: Transform[] ): Promise<TransformWithAlertingRules[]> { - const newList = transforms.filter(isContinuousTransform) as TransformWithAlertingRules[]; + const continuousTransforms = transforms.filter( + isContinuousTransform + ) as TransformWithAlertingRules[]; if (!rulesClient) { throw new Error('Rules client is missing'); } - const transformMap = keyBy(newList, 'id'); + if (!continuousTransforms.length) { + return transforms as TransformWithAlertingRules[]; + } + + const transformMap = keyBy(continuousTransforms, 'id'); const transformAlertingRules = await rulesClient.find<TransformHealthRuleParams>({ options: { @@ -398,12 +404,23 @@ export function transformHealthServiceProvider({ for (const ruleInstance of transformAlertingRules.data) { // Retrieve result transform IDs - const resultTransformIds = await getResultsTransformIds( - ruleInstance.params.includeTransforms.includes(ALL_TRANSFORMS_SELECTION) - ? Object.keys(transformMap) - : ruleInstance.params.includeTransforms, - ruleInstance.params.excludeTransforms, - true + const { includeTransforms, excludeTransforms } = ruleInstance.params; + + const resultTransformIds = new Set( + transforms + .filter( + (t) => + includeTransforms.some((includedTransformId) => + new RegExp(includedTransformId.replace(/\*/g, '.*')).test(t.id) + ) && + (Array.isArray(excludeTransforms) && excludeTransforms.length > 0 + ? excludeTransforms.every( + (excludedTransformId) => + new RegExp(excludedTransformId.replace(/\*/g, '.*')).test(t.id) === false + ) + : true) + ) + .map((t) => t.id) ); resultTransformIds.forEach((transformId) => { @@ -419,7 +436,7 @@ export function transformHealthServiceProvider({ }); } - return newList; + return continuousTransforms; }, }; } diff --git a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/instance_as_filter.test.ts b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/instance_as_filter.test.ts new file mode 100644 index 000000000000..bd39257ac72e --- /dev/null +++ b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/instance_as_filter.test.ts @@ -0,0 +1,183 @@ +/* + * 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 { EntityV2 } from '@kbn/entities-schema'; +import { instanceAsFilter } from './instance_as_filter'; +import { readSourceDefinitions } from './source_definition'; +import { loggerMock } from '@kbn/logging-mocks'; +import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +import { EntitySourceDefinition } from '../types'; +import { UnknownEntityType } from '../errors/unknown_entity_type'; +import { InvalidEntityInstance } from '../errors/invalid_entity_instance'; + +const readSourceDefinitionsMock = readSourceDefinitions as jest.Mock; +jest.mock('./source_definition', () => ({ + readSourceDefinitions: jest.fn(), +})); +const esClientMock = elasticsearchServiceMock.createClusterClient(); +const logger = loggerMock.create(); + +describe('instanceAsFilter', () => { + it('throws if no sources are found for the type', async () => { + const instance: EntityV2 = { + 'entity.type': 'my_type', + 'entity.id': 'whatever', + 'entity.display_name': 'Whatever', + }; + + const sources: EntitySourceDefinition[] = []; + readSourceDefinitionsMock.mockResolvedValue(sources); + + await expect(instanceAsFilter(instance, esClientMock, logger)).rejects.toThrowError( + UnknownEntityType + ); + }); + + it('throws if the instance cannot match any sources due to missing identity fields', async () => { + const instance: EntityV2 = { + 'entity.type': 'my_type', + 'entity.id': 'whatever', + 'entity.display_name': 'Whatever', + }; + + const sources: EntitySourceDefinition[] = [ + { + id: 'my_source', + type_id: 'my_type', + identity_fields: ['host.name'], + index_patterns: [], + metadata_fields: [], + filters: [], + }, + ]; + readSourceDefinitionsMock.mockResolvedValue(sources); + + await expect(instanceAsFilter(instance, esClientMock, logger)).rejects.toThrowError( + InvalidEntityInstance + ); + }); + + it('creates a single source filter for a single identity field', async () => { + const instance: EntityV2 = { + 'entity.type': 'my_type', + 'entity.id': 'whatever', + 'entity.display_name': 'Whatever', + 'host.name': 'my_host', + }; + + const sources: EntitySourceDefinition[] = [ + { + id: 'my_source', + type_id: 'my_type', + identity_fields: ['host.name'], + index_patterns: [], + metadata_fields: [], + filters: [], + }, + ]; + readSourceDefinitionsMock.mockResolvedValue(sources); + + await expect(instanceAsFilter(instance, esClientMock, logger)).resolves.toBe( + '(host.name: "my_host")' + ); + }); + + it('creates a single source filter for multiple identity field', async () => { + const instance: EntityV2 = { + 'entity.type': 'my_type', + 'entity.id': 'whatever', + 'entity.display_name': 'Whatever', + 'host.name': 'my_host', + 'host.os': 'my_os', + }; + + const sources: EntitySourceDefinition[] = [ + { + id: 'my_source', + type_id: 'my_type', + identity_fields: ['host.name', 'host.os'], + index_patterns: [], + metadata_fields: [], + filters: [], + }, + ]; + readSourceDefinitionsMock.mockResolvedValue(sources); + + await expect(instanceAsFilter(instance, esClientMock, logger)).resolves.toBe( + '(host.name: "my_host" AND host.os: "my_os")' + ); + }); + + it('creates multiple source filters for a single identity field', async () => { + const instance: EntityV2 = { + 'entity.type': 'my_type', + 'entity.id': 'whatever', + 'entity.display_name': 'Whatever', + 'host.name': 'my_host', + 'host.os': 'my_os', + }; + + const sources: EntitySourceDefinition[] = [ + { + id: 'my_source_host', + type_id: 'my_type', + identity_fields: ['host.name'], + index_patterns: [], + metadata_fields: [], + filters: [], + }, + { + id: 'my_source_os', + type_id: 'my_type', + identity_fields: ['host.os'], + index_patterns: [], + metadata_fields: [], + filters: [], + }, + ]; + readSourceDefinitionsMock.mockResolvedValue(sources); + + await expect(instanceAsFilter(instance, esClientMock, logger)).resolves.toBe( + '(host.name: "my_host") OR (host.os: "my_os")' + ); + }); + + it('creates multiple source filters for multiple identity field', async () => { + const instance: EntityV2 = { + 'entity.type': 'my_type', + 'entity.id': 'whatever', + 'entity.display_name': 'Whatever', + 'host.name': 'my_host', + 'host.os': 'my_os', + 'host.arch': 'my_arch', + }; + + const sources: EntitySourceDefinition[] = [ + { + id: 'my_source_host', + type_id: 'my_type', + identity_fields: ['host.name', 'host.arch'], + index_patterns: [], + metadata_fields: [], + filters: [], + }, + { + id: 'my_source_os', + type_id: 'my_type', + identity_fields: ['host.os', 'host.arch'], + index_patterns: [], + metadata_fields: [], + filters: [], + }, + ]; + readSourceDefinitionsMock.mockResolvedValue(sources); + + await expect(instanceAsFilter(instance, esClientMock, logger)).resolves.toBe( + '(host.name: "my_host" AND host.arch: "my_arch") OR (host.os: "my_os" AND host.arch: "my_arch")' + ); + }); +}); diff --git a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/instance_as_filter.ts b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/instance_as_filter.ts new file mode 100644 index 000000000000..c936277db8e2 --- /dev/null +++ b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/instance_as_filter.ts @@ -0,0 +1,56 @@ +/* + * 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 { EntityV2 } from '@kbn/entities-schema'; +import { Logger } from '@kbn/core/server'; +import { compact } from 'lodash'; +import { readSourceDefinitions } from './source_definition'; +import { InternalClusterClient } from '../types'; +import { UnknownEntityType } from '../errors/unknown_entity_type'; +import { InvalidEntityInstance } from '../errors/invalid_entity_instance'; + +export async function instanceAsFilter( + instance: EntityV2, + clusterClient: InternalClusterClient, + logger: Logger +) { + const sources = await readSourceDefinitions(clusterClient, logger, { + type: instance['entity.type'], + }); + + if (sources.length === 0) { + throw new UnknownEntityType(`No sources found for type ${instance['entity.type']}`); + } + + const sourceFilters = compact<string>( + sources.map((source) => { + const { identity_fields: identityFields } = source; + + const instanceHasRequiredFields = identityFields.every((identityField) => + instance[identityField] ? true : false + ); + + if (!instanceHasRequiredFields) { + return undefined; + } + + const fieldFilters = identityFields.map( + (identityField) => `${identityField}: "${instance[identityField]}"` + ); + + return `(${fieldFilters.join(' AND ')})`; + }) + ); + + if (sourceFilters.length === 0) { + throw new InvalidEntityInstance( + `Entity ${instance['entity.id']} of type ${instance['entity.type']} is missing some identity fields, no sources could match` + ); + } + + return sourceFilters.join(' OR '); +} diff --git a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/errors/invalid_entity_instance.ts b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/errors/invalid_entity_instance.ts new file mode 100644 index 000000000000..12b0a94fe3eb --- /dev/null +++ b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/errors/invalid_entity_instance.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 class InvalidEntityInstance extends Error { + constructor(message: string) { + super(message); + this.name = 'InvalidEntityInstance'; + } +} diff --git a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_instances.test.ts b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_instances.test.ts index e7fa8882bfcb..8836b7635ff3 100644 --- a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_instances.test.ts +++ b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_instances.test.ts @@ -28,7 +28,7 @@ describe('getEntityInstancesQuery', () => { expect(query).toEqual( 'FROM logs-*, metrics-* | ' + - 'STATS host.name = TOP(host.name::keyword, 10, "ASC"), entity.last_seen_timestamp = MAX(custom_timestamp_field), service.id = MAX(service.id::keyword) BY service.name::keyword | ' + + 'STATS host.name = VALUES(host.name::keyword), entity.last_seen_timestamp = MAX(custom_timestamp_field), service.id = MAX(service.id::keyword) BY service.name::keyword | ' + 'RENAME `service.name::keyword` AS service.name | ' + 'EVAL entity.type = "service", entity.id = service.name, entity.display_name = COALESCE(service.id, entity.id) | ' + 'SORT entity.id DESC | ' + diff --git a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_instances.ts b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_instances.ts index dc79d815abd3..c9a5948b55dc 100644 --- a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_instances.ts +++ b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_instances.ts @@ -46,7 +46,7 @@ const dslFilter = ({ const statsCommand = ({ source }: { source: EntitySourceDefinition }) => { const aggs = source.metadata_fields .filter((field) => !source.identity_fields.some((idField) => idField === field)) - .map((field) => `${field} = TOP(${asKeyword(field)}, 10, "ASC")`); + .map((field) => `${field} = VALUES(${asKeyword(field)})`); if (source.timestamp_field) { aggs.push(`entity.last_seen_timestamp = MAX(${source.timestamp_field})`); diff --git a/x-pack/platform/plugins/shared/entity_manager/server/plugin.ts b/x-pack/platform/plugins/shared/entity_manager/server/plugin.ts index fed5b1c4df45..8799c7f365bf 100644 --- a/x-pack/platform/plugins/shared/entity_manager/server/plugin.ts +++ b/x-pack/platform/plugins/shared/entity_manager/server/plugin.ts @@ -40,11 +40,15 @@ import { READ_ENTITIES_PRIVILEGE, } from './lib/v2/constants'; import { installBuiltInDefinitions } from './lib/v2/definitions/install_built_in_definitions'; +import { instanceAsFilter } from './lib/v2/definitions/instance_as_filter'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface EntityManagerServerPluginSetup {} export interface EntityManagerServerPluginStart { getScopedClient: (options: { request: KibanaRequest }) => Promise<EntityClient>; + v2: { + instanceAsFilter: typeof instanceAsFilter; + }; } export const config: PluginConfigDescriptor<EntityManagerConfig> = { @@ -197,6 +201,9 @@ export class EntityManagerServerPlugin getScopedClient: async ({ request }: { request: KibanaRequest }) => { return this.getScopedClient({ request, coreStart: core }); }, + v2: { + instanceAsFilter, + }, }; } diff --git a/x-pack/platform/plugins/shared/entity_manager/tsconfig.json b/x-pack/platform/plugins/shared/entity_manager/tsconfig.json index beb8097502b2..0fc46870ea47 100644 --- a/x-pack/platform/plugins/shared/entity_manager/tsconfig.json +++ b/x-pack/platform/plugins/shared/entity_manager/tsconfig.json @@ -38,5 +38,6 @@ "@kbn/es-types", "@kbn/apm-utils", "@kbn/features-plugin", + "@kbn/core-elasticsearch-server-mocks", ] } diff --git a/x-pack/platform/plugins/shared/inference/README.md b/x-pack/platform/plugins/shared/inference/README.md index bba5b4cdcfc2..a52e589a9dea 100644 --- a/x-pack/platform/plugins/shared/inference/README.md +++ b/x-pack/platform/plugins/shared/inference/README.md @@ -221,6 +221,75 @@ const toolCall = toolCalls[0]; // process the tool call and eventually continue the conversation with the LLM ``` +#### Request cancellation + +Request cancellation can be done by passing an abort signal when calling the API. Firing the signal +before the request completes will cause the abortion, and the API call will throw an error. + +```ts +const abortController = new AbortController(); + +const chatResponse = await inferenceClient.chatComplete({ + connectorId: 'some-gen-ai-connector', + abortSignal: abortController.signal, + messages: [{ role: MessageRole.User, content: 'Do something' }], +}); + +// from elsewhere / before the request completes and the promise resolves: + +abortController.abort(); +``` + +The `isInferenceRequestAbortedError` helper function, exposed from `@kbn/inference-common`, can be used easily identify those errors: + +```ts +import { isInferenceRequestAbortedError } from '@kbn/inference-common'; + +try { + const abortController = new AbortController(); + const chatResponse = await inferenceClient.chatComplete({ + connectorId: 'some-gen-ai-connector', + abortSignal: abortController.signal, + messages: [{ role: MessageRole.User, content: 'Do something' }], + }); +} catch(e) { + if(isInferenceRequestAbortedError(e)) { + // request was aborted, do something + } else { + // was another error, do something else + } +} +``` + +The approach is very similar for stream mode: + +```ts +import { isInferenceRequestAbortedError } from '@kbn/inference-common'; + +const abortController = new AbortController(); +const events$ = inferenceClient.chatComplete({ + stream: true, + connectorId: 'some-gen-ai-connector', + abortSignal: abortController.signal, + messages: [{ role: MessageRole.User, content: 'Do something' }], +}); + +events$.subscribe({ + next: (event) => { + // do something + }, + error: (err) => { + if(isInferenceRequestAbortedError(e)) { + // request was aborted, do something + } else { + // was another error, do something else + } + } +}); + +abortController.abort(); +``` + ### `output` API `output` is a wrapper around the `chatComplete` API that is catered towards a specific use case: having the LLM output a structured response, based on a schema. diff --git a/x-pack/platform/plugins/shared/inference/common/output/create_output_api.test.ts b/x-pack/platform/plugins/shared/inference/common/output/create_output_api.test.ts index c65720aae2e4..d29f88009f8e 100644 --- a/x-pack/platform/plugins/shared/inference/common/output/create_output_api.test.ts +++ b/x-pack/platform/plugins/shared/inference/common/output/create_output_api.test.ts @@ -196,4 +196,26 @@ describe('createOutputApi', () => { ).toThrowError('Retry options are not supported in streaming mode'); }); }); + + it('propagates the abort signal when provided', async () => { + chatComplete.mockResolvedValue(Promise.resolve({ content: 'content', toolCalls: [] })); + + const output = createOutputApi(chatComplete); + + const abortController = new AbortController(); + + await output({ + id: 'id', + connectorId: '.my-connector', + input: 'input message', + abortSignal: abortController.signal, + }); + + expect(chatComplete).toHaveBeenCalledTimes(1); + expect(chatComplete).toHaveBeenCalledWith( + expect.objectContaining({ + abortSignal: abortController.signal, + }) + ); + }); }); diff --git a/x-pack/platform/plugins/shared/inference/common/output/create_output_api.ts b/x-pack/platform/plugins/shared/inference/common/output/create_output_api.ts index 3e65cb283dd4..7cd7e9cad144 100644 --- a/x-pack/platform/plugins/shared/inference/common/output/create_output_api.ts +++ b/x-pack/platform/plugins/shared/inference/common/output/create_output_api.ts @@ -34,6 +34,7 @@ export function createOutputApi(chatCompleteApi: ChatCompleteAPI) { previousMessages, functionCalling, stream, + abortSignal, retry, }: DefaultOutputOptions): OutputCompositeResponse<string, ToolSchema | undefined, boolean> { if (stream && retry !== undefined) { @@ -52,6 +53,7 @@ export function createOutputApi(chatCompleteApi: ChatCompleteAPI) { connectorId, stream, functionCalling, + abortSignal, system, messages, ...(schema @@ -113,6 +115,7 @@ export function createOutputApi(chatCompleteApi: ChatCompleteAPI) { input, schema, system, + abortSignal, previousMessages: messages.concat( { role: MessageRole.Assistant as const, diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.test.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.test.ts index 565727b7f57f..c6114c3b09e9 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.test.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.test.ts @@ -325,5 +325,24 @@ describe('bedrockClaudeAdapter', () => { expect(tools).toEqual([]); expect(system).toEqual(addNoToolUsageDirective('some system instruction')); }); + + it('propagates the abort signal when provided', () => { + const abortController = new AbortController(); + + bedrockClaudeAdapter.chatComplete({ + logger, + executor: executorMock, + messages: [{ role: MessageRole.User, content: 'question' }], + abortSignal: abortController.signal, + }); + + expect(executorMock.invoke).toHaveBeenCalledTimes(1); + expect(executorMock.invoke).toHaveBeenCalledWith({ + subAction: 'invokeStream', + subActionParams: expect.objectContaining({ + signal: abortController.signal, + }), + }); + }); }); }); 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 e73d9c9344c9..e34605a4c96a 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 @@ -26,7 +26,7 @@ import { processCompletionChunks } from './process_completion_chunks'; import { addNoToolUsageDirective } from './prompts'; export const bedrockClaudeAdapter: InferenceConnectorAdapter = { - chatComplete: ({ executor, system, messages, toolChoice, tools }) => { + chatComplete: ({ executor, system, messages, toolChoice, tools, abortSignal }) => { const noToolUsage = toolChoice === ToolChoiceType.none; const subActionParams = { @@ -36,6 +36,7 @@ export const bedrockClaudeAdapter: InferenceConnectorAdapter = { toolChoice: toolChoiceToBedrock(toolChoice), temperature: 0, stopSequences: ['\n\nHuman:'], + signal: abortSignal, }; return from( diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.test.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.test.ts index 95a46f73d5d1..5024bd1f4c87 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.test.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.test.ts @@ -402,5 +402,24 @@ describe('geminiAdapter', () => { expect(tapFn).toHaveBeenCalledWith({ chunk: 1 }); expect(tapFn).toHaveBeenCalledWith({ chunk: 2 }); }); + + it('propagates the abort signal when provided', () => { + const abortController = new AbortController(); + + geminiAdapter.chatComplete({ + logger, + executor: executorMock, + messages: [{ role: MessageRole.User, content: 'question' }], + abortSignal: abortController.signal, + }); + + expect(executorMock.invoke).toHaveBeenCalledTimes(1); + expect(executorMock.invoke).toHaveBeenCalledWith({ + subAction: 'invokeStream', + subActionParams: expect.objectContaining({ + signal: abortController.signal, + }), + }); + }); }); }); 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 80d043944906..aa62f7006eac 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 @@ -22,7 +22,7 @@ import { processVertexStream } from './process_vertex_stream'; import type { GenerateContentResponseChunk, GeminiMessage, GeminiToolConfig } from './types'; export const geminiAdapter: InferenceConnectorAdapter = { - chatComplete: ({ executor, system, messages, toolChoice, tools }) => { + chatComplete: ({ executor, system, messages, toolChoice, tools, abortSignal }) => { return from( executor.invoke({ subAction: 'invokeStream', @@ -32,6 +32,7 @@ export const geminiAdapter: InferenceConnectorAdapter = { tools: toolsToGemini(tools), toolConfig: toolChoiceToConfig(toolChoice), temperature: 0, + signal: abortSignal, stopSequences: ['\n\nHuman:'], }, }) diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/openai_adapter.test.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/openai_adapter.test.ts index 48544f1bb0fb..9b7fbc388024 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/openai_adapter.test.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/openai_adapter.test.ts @@ -77,6 +77,7 @@ describe('openAIAdapter', () => { }; }); }); + it('correctly formats messages ', () => { openAIAdapter.chatComplete({ ...defaultArgs, @@ -254,6 +255,25 @@ describe('openAIAdapter', () => { expect(getRequest().stream).toBe(true); expect(getRequest().body.stream).toBe(true); }); + + it('propagates the abort signal when provided', () => { + const abortController = new AbortController(); + + openAIAdapter.chatComplete({ + logger, + executor: executorMock, + messages: [{ role: MessageRole.User, content: 'question' }], + abortSignal: abortController.signal, + }); + + expect(executorMock.invoke).toHaveBeenCalledTimes(1); + expect(executorMock.invoke).toHaveBeenCalledWith({ + subAction: 'stream', + subActionParams: expect.objectContaining({ + signal: abortController.signal, + }), + }); + }); }); describe('when handling the response', () => { 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 49b6bb514202..0529820b1bfb 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 @@ -43,7 +43,16 @@ import { } from '../../simulated_function_calling'; export const openAIAdapter: InferenceConnectorAdapter = { - chatComplete: ({ executor, system, messages, toolChoice, tools, functionCalling, logger }) => { + chatComplete: ({ + executor, + system, + messages, + toolChoice, + tools, + functionCalling, + logger, + abortSignal, + }) => { const stream = true; const simulatedFunctionCalling = functionCalling === 'simulated'; @@ -73,6 +82,7 @@ export const openAIAdapter: InferenceConnectorAdapter = { subAction: 'stream', subActionParams: { body: JSON.stringify(request), + signal: abortSignal, stream, }, }) diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/api.test.mocks.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/api.test.mocks.ts new file mode 100644 index 000000000000..e3248b79af40 --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/api.test.mocks.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const getInferenceAdapterMock = jest.fn(); + +jest.doMock('./adapters', () => { + const actual = jest.requireActual('./adapters'); + return { + ...actual, + getInferenceAdapter: getInferenceAdapterMock, + }; +}); + +export const getInferenceExecutorMock = jest.fn(); + +jest.doMock('./utils', () => { + const actual = jest.requireActual('./utils'); + return { + ...actual, + getInferenceExecutor: getInferenceExecutorMock, + }; +}); diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/api.test.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/api.test.ts new file mode 100644 index 000000000000..7d557ec512fc --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/api.test.ts @@ -0,0 +1,237 @@ +/* + * 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 { getInferenceExecutorMock, getInferenceAdapterMock } from './api.test.mocks'; + +import { of, Subject, isObservable, toArray, firstValueFrom } from 'rxjs'; +import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; +import { httpServerMock } from '@kbn/core/server/mocks'; +import { actionsMock } from '@kbn/actions-plugin/server/mocks'; +import { + type ChatCompleteAPI, + type ChatCompletionChunkEvent, + MessageRole, +} from '@kbn/inference-common'; +import { + createInferenceConnectorAdapterMock, + createInferenceConnectorMock, + createInferenceExecutorMock, + chunkEvent, +} from '../test_utils'; +import { createChatCompleteApi } from './api'; + +describe('createChatCompleteApi', () => { + let request: ReturnType<typeof httpServerMock.createKibanaRequest>; + let logger: MockedLogger; + let actions: ReturnType<typeof actionsMock.createStart>; + let inferenceAdapter: ReturnType<typeof createInferenceConnectorAdapterMock>; + let inferenceConnector: ReturnType<typeof createInferenceConnectorMock>; + let inferenceExecutor: ReturnType<typeof createInferenceExecutorMock>; + + let chatComplete: ChatCompleteAPI; + + beforeEach(() => { + request = httpServerMock.createKibanaRequest(); + logger = loggerMock.create(); + actions = actionsMock.createStart(); + + chatComplete = createChatCompleteApi({ request, actions, logger }); + + inferenceAdapter = createInferenceConnectorAdapterMock(); + inferenceAdapter.chatComplete.mockReturnValue(of(chunkEvent('chunk-1'))); + getInferenceAdapterMock.mockReturnValue(inferenceAdapter); + + inferenceConnector = createInferenceConnectorMock(); + + inferenceExecutor = createInferenceExecutorMock({ connector: inferenceConnector }); + getInferenceExecutorMock.mockResolvedValue(inferenceExecutor); + }); + + afterEach(() => { + getInferenceExecutorMock.mockReset(); + getInferenceAdapterMock.mockReset(); + }); + + it('calls `getInferenceExecutor` with the right parameters', async () => { + await chatComplete({ + connectorId: 'connectorId', + messages: [{ role: MessageRole.User, content: 'question' }], + }); + + expect(getInferenceExecutorMock).toHaveBeenCalledTimes(1); + expect(getInferenceExecutorMock).toHaveBeenCalledWith({ + connectorId: 'connectorId', + request, + actions, + }); + }); + + it('calls `getInferenceAdapter` with the right parameters', async () => { + await chatComplete({ + connectorId: 'connectorId', + messages: [{ role: MessageRole.User, content: 'question' }], + }); + + expect(getInferenceAdapterMock).toHaveBeenCalledTimes(1); + expect(getInferenceAdapterMock).toHaveBeenCalledWith(inferenceConnector.type); + }); + + it('calls `inferenceAdapter.chatComplete` with the right parameters', async () => { + await chatComplete({ + connectorId: 'connectorId', + messages: [{ role: MessageRole.User, content: 'question' }], + }); + + expect(inferenceAdapter.chatComplete).toHaveBeenCalledTimes(1); + expect(inferenceAdapter.chatComplete).toHaveBeenCalledWith({ + messages: [{ role: MessageRole.User, content: 'question' }], + executor: inferenceExecutor, + logger, + }); + }); + + it('throws if the connector is not compatible', async () => { + getInferenceAdapterMock.mockReturnValue(undefined); + + await expect( + chatComplete({ + connectorId: 'connectorId', + messages: [{ role: MessageRole.User, content: 'question' }], + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Adapter for type .gen-ai not implemented"`); + }); + + describe('response mode', () => { + it('returns a promise resolving with the response', async () => { + inferenceAdapter.chatComplete.mockReturnValue( + of(chunkEvent('chunk-1'), chunkEvent('chunk-2')) + ); + + const response = await chatComplete({ + connectorId: 'connectorId', + messages: [{ role: MessageRole.User, content: 'question' }], + }); + + expect(response).toEqual({ + content: 'chunk-1chunk-2', + toolCalls: [], + }); + }); + + describe('request cancellation', () => { + it('passes the abortSignal down to `inferenceAdapter.chatComplete`', async () => { + const abortController = new AbortController(); + + await chatComplete({ + connectorId: 'connectorId', + messages: [{ role: MessageRole.User, content: 'question' }], + abortSignal: abortController.signal, + }); + + expect(inferenceAdapter.chatComplete).toHaveBeenCalledTimes(1); + expect(inferenceAdapter.chatComplete).toHaveBeenCalledWith({ + messages: [{ role: MessageRole.User, content: 'question' }], + executor: inferenceExecutor, + abortSignal: abortController.signal, + logger, + }); + }); + + it('throws an error when the signal is triggered', async () => { + const abortController = new AbortController(); + + const subject = new Subject<ChatCompletionChunkEvent>(); + inferenceAdapter.chatComplete.mockReturnValue(subject.asObservable()); + + subject.next(chunkEvent('chunk-1')); + + let caughtError: any; + + const promise = chatComplete({ + connectorId: 'connectorId', + messages: [{ role: MessageRole.User, content: 'question' }], + abortSignal: abortController.signal, + }).catch((err) => { + caughtError = err; + }); + + abortController.abort(); + + await promise; + + expect(caughtError).toBeInstanceOf(Error); + expect(caughtError.message).toContain('Request was aborted'); + }); + }); + }); + + describe('stream mode', () => { + it('returns an observable of events', async () => { + inferenceAdapter.chatComplete.mockReturnValue( + of(chunkEvent('chunk-1'), chunkEvent('chunk-2')) + ); + + const events$ = chatComplete({ + stream: true, + connectorId: 'connectorId', + messages: [{ role: MessageRole.User, content: 'question' }], + }); + + expect(isObservable(events$)).toBe(true); + + const events = await firstValueFrom(events$.pipe(toArray())); + expect(events).toEqual([ + { + content: 'chunk-1', + tool_calls: [], + type: 'chatCompletionChunk', + }, + { + content: 'chunk-2', + tool_calls: [], + type: 'chatCompletionChunk', + }, + { + content: 'chunk-1chunk-2', + toolCalls: [], + type: 'chatCompletionMessage', + }, + ]); + }); + + describe('request cancellation', () => { + it('throws an error when the signal is triggered', async () => { + const abortController = new AbortController(); + + const subject = new Subject<ChatCompletionChunkEvent>(); + inferenceAdapter.chatComplete.mockReturnValue(subject.asObservable()); + + subject.next(chunkEvent('chunk-1')); + + let caughtError: any; + + const events$ = chatComplete({ + stream: true, + connectorId: 'connectorId', + messages: [{ role: MessageRole.User, content: 'question' }], + abortSignal: abortController.signal, + }); + + events$.subscribe({ + error: (err: any) => { + caughtError = err; + }, + }); + + abortController.abort(); + + expect(caughtError).toBeInstanceOf(Error); + expect(caughtError.message).toContain('Request was aborted'); + }); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/api.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/api.ts index e58c94759e16..0e58c255bd60 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/api.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/api.ts @@ -6,7 +6,7 @@ */ import { last, omit } from 'lodash'; -import { defer, switchMap, throwError } from 'rxjs'; +import { defer, switchMap, throwError, identity } from 'rxjs'; import type { Logger } from '@kbn/logging'; import type { KibanaRequest } from '@kbn/core-http-server'; import { @@ -17,9 +17,13 @@ import { ChatCompleteOptions, } from '@kbn/inference-common'; import type { PluginStartContract as ActionsPluginStart } from '@kbn/actions-plugin/server'; -import { getConnectorById } from '../util/get_connector_by_id'; import { getInferenceAdapter } from './adapters'; -import { createInferenceExecutor, chunksIntoMessage, streamToResponse } from './utils'; +import { + getInferenceExecutor, + chunksIntoMessage, + streamToResponse, + handleCancellation, +} from './utils'; interface CreateChatCompleteApiOptions { request: KibanaRequest; @@ -37,18 +41,16 @@ export function createChatCompleteApi({ request, actions, logger }: CreateChatCo system, functionCalling, stream, + abortSignal, }: ChatCompleteOptions<ToolOptions, boolean>): ChatCompleteCompositeResponse< ToolOptions, boolean > => { - const obs$ = defer(async () => { - const actionsClient = await actions.getActionsClientWithRequest(request); - const connector = await getConnectorById({ connectorId, actionsClient }); - const executor = createInferenceExecutor({ actionsClient, connector }); - return { executor, connector }; + const inference$ = defer(async () => { + return await getInferenceExecutor({ connectorId, request, actions }); }).pipe( - switchMap(({ executor, connector }) => { - const connectorType = connector.type; + switchMap((executor) => { + const connectorType = executor.getConnector().type; const inferenceAdapter = getInferenceAdapter(connectorType); const messagesWithoutData = messages.map((message) => omit(message, 'data')); @@ -80,21 +82,20 @@ export function createChatCompleteApi({ request, actions, logger }: CreateChatCo tools, logger, functionCalling, + abortSignal, }); }), chunksIntoMessage({ - toolOptions: { - toolChoice, - tools, - }, + toolOptions: { toolChoice, tools }, logger, - }) + }), + abortSignal ? handleCancellation(abortSignal) : identity ); if (stream) { - return obs$; + return inference$; } else { - return streamToResponse(obs$); + return streamToResponse(inference$); } }; } diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/types.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/types.ts index 64cc542ff611..498afb9a2a17 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/types.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/types.ts @@ -29,6 +29,7 @@ export interface InferenceConnectorAdapter { messages: Message[]; system?: string; functionCalling?: FunctionCallingMode; + abortSignal?: AbortSignal; logger: Logger; } & ToolOptions ) => Observable<InferenceConnectorAdapterChatCompleteEvent>; diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/handle_cancellation.test.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/handle_cancellation.test.ts new file mode 100644 index 000000000000..7fd464a7051c --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/handle_cancellation.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { of, Subject, toArray, firstValueFrom } from 'rxjs'; +import { InferenceTaskError, InferenceTaskErrorCode } from '@kbn/inference-common'; +import { handleCancellation } from './handle_cancellation'; + +describe('handleCancellation', () => { + it('mirrors the source when the abort signal is not triggered', async () => { + const abortController = new AbortController(); + + const source$ = of(1, 2, 3); + + const output$ = source$.pipe(handleCancellation(abortController.signal)); + + const events = await firstValueFrom(output$.pipe(toArray())); + expect(events).toEqual([1, 2, 3]); + }); + + it('causes the observable to error when the signal fires', () => { + const abortController = new AbortController(); + + const source$ = new Subject<number>(); + + const output$ = source$.pipe(handleCancellation(abortController.signal)); + + let thrownError: any; + const values: number[] = []; + + output$.subscribe({ + next: (value) => { + values.push(value); + }, + error: (err) => { + thrownError = err; + }, + }); + + source$.next(1); + source$.next(2); + abortController.abort(); + source$.next(3); + + expect(values).toEqual([1, 2]); + expect(thrownError).toBeInstanceOf(InferenceTaskError); + expect(thrownError.code).toBe(InferenceTaskErrorCode.abortedError); + expect(thrownError.message).toContain('Request was aborted'); + }); +}); diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/handle_cancellation.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/handle_cancellation.ts new file mode 100644 index 000000000000..640172b150e4 --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/handle_cancellation.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 { OperatorFunction, Observable, Subject, takeUntil } from 'rxjs'; +import { createInferenceRequestAbortedError } from '@kbn/inference-common'; + +export function handleCancellation<T>(abortSignal: AbortSignal): OperatorFunction<T, T> { + return (source$) => { + const stop$ = new Subject<void>(); + if (abortSignal.aborted) { + stop$.next(); + } + abortSignal.addEventListener('abort', () => { + stop$.next(); + }); + + return new Observable<T>((subscriber) => { + return source$.pipe(takeUntil(stop$)).subscribe({ + next: (value) => { + subscriber.next(value); + }, + error: (err) => { + subscriber.error(err); + }, + complete: () => { + if (abortSignal.aborted) { + subscriber.error(createInferenceRequestAbortedError()); + } else { + subscriber.complete(); + } + }, + }); + }); + }; +} 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 d3dc2010cba3..4314a554589d 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 @@ -6,10 +6,11 @@ */ export { - createInferenceExecutor, + getInferenceExecutor, type InferenceInvokeOptions, type InferenceInvokeResult, type InferenceExecutor, } from './inference_executor'; export { chunksIntoMessage } from './chunks_into_message'; export { streamToResponse } from './stream_to_response'; +export { handleCancellation } from './handle_cancellation'; diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/inference_executor.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/inference_executor.ts index 736beb82aa68..c461e6b6cdfb 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/inference_executor.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/inference_executor.ts @@ -5,9 +5,14 @@ * 2.0. */ +import type { KibanaRequest } from '@kbn/core-http-server'; import type { ActionTypeExecutorResult } from '@kbn/actions-plugin/common'; -import type { ActionsClient } from '@kbn/actions-plugin/server'; +import type { + ActionsClient, + PluginStartContract as ActionsPluginStart, +} from '@kbn/actions-plugin/server'; import type { InferenceConnector } from '../../../common/connectors'; +import { getConnectorById } from '../../util/get_connector_by_id'; export interface InferenceInvokeOptions { subAction: string; @@ -22,6 +27,7 @@ export type InferenceInvokeResult<Data = unknown> = ActionTypeExecutorResult<Dat * In practice, for now it's just a thin abstraction around the action client. */ export interface InferenceExecutor { + getConnector: () => InferenceConnector; invoke(params: InferenceInvokeOptions): Promise<InferenceInvokeResult>; } @@ -33,6 +39,7 @@ export const createInferenceExecutor = ({ actionsClient: ActionsClient; }): InferenceExecutor => { return { + getConnector: () => connector, async invoke({ subAction, subActionParams }): Promise<InferenceInvokeResult> { return await actionsClient.execute({ actionId: connector.connectorId, @@ -44,3 +51,17 @@ export const createInferenceExecutor = ({ }, }; }; + +export const getInferenceExecutor = async ({ + connectorId, + actions, + request, +}: { + connectorId: string; + actions: ActionsPluginStart; + request: KibanaRequest; +}) => { + const actionsClient = await actions.getActionsClientWithRequest(request); + const connector = await getConnectorById({ connectorId, actionsClient }); + return createInferenceExecutor({ actionsClient, connector }); +}; diff --git a/x-pack/platform/plugins/shared/inference/server/routes/chat_complete.ts b/x-pack/platform/plugins/shared/inference/server/routes/chat_complete.ts index 84e3dd57cded..06ca5381cd83 100644 --- a/x-pack/platform/plugins/shared/inference/server/routes/chat_complete.ts +++ b/x-pack/platform/plugins/shared/inference/server/routes/chat_complete.ts @@ -109,6 +109,9 @@ export function registerChatCompleteRoute({ .getStartServices() .then(([coreStart, pluginsStart]) => pluginsStart.actions); + const abortController = new AbortController(); + request.events.aborted$.subscribe(() => abortController.abort()); + const client = createInferenceClient({ request, actions, logger }); const { connectorId, messages, system, toolChoice, tools, functionCalling } = request.body; @@ -121,6 +124,7 @@ export function registerChatCompleteRoute({ tools, functionCalling, stream, + abortSignal: abortController.signal, }); } diff --git a/x-pack/platform/plugins/shared/inference/server/test_utils/index.ts b/x-pack/platform/plugins/shared/inference/server/test_utils/index.ts new file mode 100644 index 000000000000..2eafe20bfdca --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/test_utils/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 { chunkEvent, tokensEvent, messageEvent } from './chat_complete_events'; +export { createInferenceConnectorMock } from './inference_connector'; +export { createInferenceConnectorAdapterMock } from './inference_connector_adapter'; +export { createInferenceExecutorMock } from './inference_executor'; diff --git a/x-pack/platform/plugins/shared/inference/server/test_utils/inference_connector.ts b/x-pack/platform/plugins/shared/inference/server/test_utils/inference_connector.ts new file mode 100644 index 000000000000..af7f35115325 --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/test_utils/inference_connector.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 { InferenceConnector, InferenceConnectorType } from '../../common/connectors'; + +export const createInferenceConnectorMock = ( + parts: Partial<InferenceConnector> = {} +): InferenceConnector => { + return { + type: InferenceConnectorType.OpenAI, + name: 'Inference connector', + connectorId: 'connector-id', + ...parts, + }; +}; diff --git a/x-pack/platform/plugins/shared/inference/server/test_utils/inference_connector_adapter.ts b/x-pack/platform/plugins/shared/inference/server/test_utils/inference_connector_adapter.ts new file mode 100644 index 000000000000..9e2c4516f4f1 --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/test_utils/inference_connector_adapter.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { InferenceConnectorAdapter } from '../chat_complete/types'; + +export const createInferenceConnectorAdapterMock = (): jest.Mocked<InferenceConnectorAdapter> => { + return { + chatComplete: jest.fn(), + }; +}; diff --git a/x-pack/platform/plugins/shared/inference/server/test_utils/inference_executor.ts b/x-pack/platform/plugins/shared/inference/server/test_utils/inference_executor.ts new file mode 100644 index 000000000000..64b5100a9db3 --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/test_utils/inference_executor.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 type { InferenceConnector } from '../../common/connectors'; +import { InferenceExecutor } from '../chat_complete/utils'; +import { createInferenceConnectorMock } from './inference_connector'; + +export const createInferenceExecutorMock = ({ + connector = createInferenceConnectorMock(), +}: { connector?: InferenceConnector } = {}): jest.Mocked<InferenceExecutor> => { + return { + getConnector: jest.fn().mockReturnValue(connector), + invoke: jest.fn(), + }; +}; diff --git a/x-pack/platform/plugins/shared/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx b/x-pack/platform/plugins/shared/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx index 4d4e244ac25a..31dec6ab573d 100644 --- a/x-pack/platform/plugins/shared/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx +++ b/x-pack/platform/plugins/shared/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx @@ -481,7 +481,7 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { ip_location: { category: processorCategories.DATA_ENRICHMENT, FieldsComponent: IpLocation, - docLinkPath: '/geoip-processor.html', + docLinkPath: '/ip-location-processor.html', label: i18n.translate('xpack.ingestPipelines.processors.label.ipLocation', { defaultMessage: 'IP Location', }), diff --git a/x-pack/platform/plugins/shared/ml/public/application/services/ml_api_service/trained_models.ts b/x-pack/platform/plugins/shared/ml/public/application/services/ml_api_service/trained_models.ts index 9e473658bc76..04d815082c54 100644 --- a/x-pack/platform/plugins/shared/ml/public/application/services/ml_api_service/trained_models.ts +++ b/x-pack/platform/plugins/shared/ml/public/application/services/ml_api_service/trained_models.ts @@ -31,7 +31,6 @@ import type { export interface InferenceQueryParams { from?: number; - include_model_definition?: boolean; size?: number; tags?: string; include?: 'total_feature_importance' | 'feature_importance_baseline' | string; diff --git a/x-pack/platform/plugins/shared/observability_solution/observability_ai_assistant/server/utils/recall/score_suggestions.ts b/x-pack/platform/plugins/shared/observability_solution/observability_ai_assistant/server/utils/recall/score_suggestions.ts index 7d1a19463cce..b57e5928ce0b 100644 --- a/x-pack/platform/plugins/shared/observability_solution/observability_ai_assistant/server/utils/recall/score_suggestions.ts +++ b/x-pack/platform/plugins/shared/observability_solution/observability_ai_assistant/server/utils/recall/score_suggestions.ts @@ -102,7 +102,7 @@ export async function scoreSuggestions({ type: 'string', }, }, - required: ['score'], + required: ['scores'], } as const, }; diff --git a/x-pack/platform/plugins/shared/screenshotting/README.md b/x-pack/platform/plugins/shared/screenshotting/README.md new file mode 100644 index 000000000000..b129bbb5ad9a --- /dev/null +++ b/x-pack/platform/plugins/shared/screenshotting/README.md @@ -0,0 +1,156 @@ +# Kibana Screenshotting + +This plugin provides functionality to take screenshots of the Kibana pages. +It uses Chromium and Puppeteer underneath to run the browser in headless mode. + +## Capabilities +- Canvas workpads screenshots. +- Dashboards screenshots. +- Expressions screenshots. +- PDF generation. +- Batch screenshotting. + +## Usage + +### Getting started +After listing the `screenshotting` plugin in your dependencies, the plugin will be intitalized on the setup stage. +The intitalization process downloads (if it is not already present) and verifies the Chromium build. + +The start contract exposes a public API to interact with the plugin. +Apart from the actual screenshotting functionality, it also provides a way for self-diagnostics. + +Here is an example of how you can take a screenshot of a Kibana URL. + +```typescript +import { lastValueFrom } from 'rxjs'; +import type { CoreSetup, Plugin } from 'src/core/server'; +import type { ScreenshottingStart } from 'x-pack/platform/plugins/shared/screenshotting/server'; + + +interface StartDeps { + screenshotting: ScreenshottingStart; +} + +class ExamplePlugin implements Plugin<void, void, void, StartDeps> { + setup({ http, getStartServices }: CoreSetup<StartDeps>) { + const router = http.createRouter(); + + router.get( + { + path: '/api/capture', + validate: { + query: schema.object({ + id: schema.string(), + }), + }, + }, + async (context, request, response) => { + const [, { screenshotting }] = await getStartServices(); + const { metrics, results } = await lastValueFrom( + screenshotting.getScreenshots({ + request, + urls: [`http://localhost/app/canvas#/workpad/workpad-${request.query.id}`], + }) + ); + + return response.ok({ + body: JSON.stringify({ + metrics, + image: results[0]?.screenshots[0]?.data.toString('base64'), + errors: results[0]?.renderErrors, + } as ScreenshottingExpressionResponse), + }); + } + ); + } + + start() {} +} + +export function plugin() { + return new ExamplePlugin(); +} +``` + +### API +Please use automatically generated API reference or generated TypeDoc comments to find the complete documentation. + +#### `getScreenshots(options): Observable` +Takes screenshots of multiple pages or an expression and returns an observable with the screenshotting results. + +The `options` parameter is an object with parameters of the screenshotting session. +Option | Required | Default | Description +--- | :---: | --- | --- +`browserTimezone` | no | _none_ | The browser timezone that will be emulated in the browser instance. This option should be used to keep timezone on server and client in sync. +`expression` | no | _none_ | An expression to capture screenshot of. Mutually exclusive with the `urls` parameter. +`format` | no | `'png'` | An output format. It can either be PDF or PNG. In case of capturing multiple URLs, all the screenshots will be combined into one document for PDF format. For PNG format, an array of screenshots will be returned. +`headers` | no | _none_ | Custom headers to be sent with each request. The headers will be used for authorization. +`input` | no | `undefined` | The expression input. +`layout` | no | `{}` | Page layout parameters describing characteristics of the capturing screenshot (e.g., dimensions, zoom, etc.). +`request` | no | _none_ | Kibana Request reference to extract headers from. +`timeouts` | no | _none_ | Timeouts for each phase of the screenshot. +`timeouts.openUrl` | no | (kibana.yml setting) | The timeout in milliseconds to allow the Chromium browser to wait for the "Loading…" screen to dismiss and find the initial data for the page. If the time is exceeded, a screenshot is captured showing the current page, and the result structure contains an error message. +`timeouts.renderComplete` | no | (kibana.yml setting) | The timeout in milliseconds to allow the Chromium browser to wait for all visualizations to fetch and render the data. If the time is exceeded, a screenshot is captured showing the current page, and the result structure contains an error message. +`timeouts.waitForElements` | no | (kibana.yml setting) | The timeout in milliseconds to allow the Chromium browser to wait for all visualization panels to load on the page. If the time is exceeded, a screenshot is captured showing the current page, and the result structure contains an error message. +`urls` | no | `[]` | The list or URL to take screenshots of. Every item can either be a string or a tuple containing a URL and a context. The contextual data can be gathered using the screenshot mode plugin. Mutually exclusive with the `expression` parameter. + +#### `diagnose(flags?: string[]): Observable` +Runs browser diagnostics. +The diagnostic implementation launches Chromium and emits the output in the resulting observable. + +There is a way to override some Chromium command line arguments using the `flags` parameter. + +### Configuration +Option | Default | Description +--- | --- | --- +`xpack.screenshotting.networkPolicy.enabled` | `true` | Capturing a screenshot from a Kibana page involves sending out requests for all the linked web assets. For example, a Markdown visualization can show an image from a remote server. +`xpack.screenshotting.networkPolicy.rules` | Allow http, https, ws, wss, and data. | A policy is specified as an array of objects that describe what to allow or deny based on a host or protocol. If a host or protocol is not specified, the rule matches any host or protocol. +`xpack.screenshotting.browser.autoDownload` | Depends on the `dist` parameter. | Flag to automatically download chromium distribution. +`xpack.screenshotting.browser.chromium.disableSandbox` | Defaults to `false` for all operating systems except Debian and Red Hat Linux, which use `true`. | It is recommended that you research the feasibility of enabling unprivileged user namespaces. An exception is if you are running Kibana in Docker because the container runs in a user namespace with the built-in seccomp/bpf filters. For more information, refer to [Chromium sandbox](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/linux/sandboxing.md). +`xpack.screenshotting.browser.chromium.proxy.enabled` | `false` | Enables the proxy for Chromium to use. +`xpack.screenshotting.browser.chromium.proxy.server` | _none_ | The uri for the proxy server. Providing the username and password for the proxy server via the uri is not supported. +`xpack.screenshotting.browser.chromium.proxy.bypass` | `[]` | An array of hosts that should not go through the proxy server and should use a direct connection instead. Examples of valid entries are "elastic.co", "*.elastic.co", ".elastic.co", ".elastic.co:5601". + +## How It Works +```mermaid +sequenceDiagram + participant User + participant Screenshotting + participant Browser + + User ->> Screenshotting: API call + Screenshotting ->> Browser: Launch browser + activate Browser + Screenshotting ->> Browser: Create page + Screenshotting ->> Browser: Set parameters + Note over Screenshotting,Browser: timezone + + Screenshotting ->> Browser: Open URL + Screenshotting ->> Browser: Set contextual data + Note over Screenshotting,Browser: custom context, screenshot mode flag + Browser ->> Screenshotting: Rendering + + Screenshotting ->> Browser: Wait for visualizations + Note over Screenshotting,Browser: poll for a number of DOM nodes to match <br> the number of dashboard elements + Screenshotting ->> Browser: Wait for render completion + Note over Screenshotting,Browser: poll for selectors indicating rendering completion + Browser ->> Screenshotting: Page is ready + + Screenshotting ->> Browser: Take screenshot + Browser ->> Screenshotting: Return PNG buffer + Screenshotting ->> User: Return screenshot +``` + +## Testing +### Integration +There is an example plugin that demonstrates integration with the screenshotting plugin. That plugin utilizes expression capturing. + +### Chromium Downloads +To download all Chromium browsers for all platforms and architectures: + +```bash +cd x-pack +npx gulp downloadChromium +``` + +This command is used to provision CI workspaces so that Chromium does not need to be downloaded for every CI run. diff --git a/x-pack/plugins/screenshotting/common/errors.ts b/x-pack/platform/plugins/shared/screenshotting/common/errors.ts similarity index 100% rename from x-pack/plugins/screenshotting/common/errors.ts rename to x-pack/platform/plugins/shared/screenshotting/common/errors.ts diff --git a/x-pack/plugins/screenshotting/common/expression.ts b/x-pack/platform/plugins/shared/screenshotting/common/expression.ts similarity index 100% rename from x-pack/plugins/screenshotting/common/expression.ts rename to x-pack/platform/plugins/shared/screenshotting/common/expression.ts diff --git a/x-pack/plugins/screenshotting/common/index.ts b/x-pack/platform/plugins/shared/screenshotting/common/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/common/index.ts rename to x-pack/platform/plugins/shared/screenshotting/common/index.ts diff --git a/x-pack/plugins/screenshotting/common/layout.ts b/x-pack/platform/plugins/shared/screenshotting/common/layout.ts similarity index 100% rename from x-pack/plugins/screenshotting/common/layout.ts rename to x-pack/platform/plugins/shared/screenshotting/common/layout.ts diff --git a/x-pack/plugins/screenshotting/common/types.ts b/x-pack/platform/plugins/shared/screenshotting/common/types.ts similarity index 100% rename from x-pack/plugins/screenshotting/common/types.ts rename to x-pack/platform/plugins/shared/screenshotting/common/types.ts diff --git a/x-pack/platform/plugins/shared/screenshotting/jest.config.js b/x-pack/platform/plugins/shared/screenshotting/jest.config.js new file mode 100644 index 000000000000..0fd6d74a1d65 --- /dev/null +++ b/x-pack/platform/plugins/shared/screenshotting/jest.config.js @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/platform/plugins/shared/screenshotting'], + coverageDirectory: + '<rootDir>/target/kibana-coverage/jest/x-pack/platform/plugins/shared/screenshotting', + coverageReporters: ['text', 'html'], + collectCoverageFrom: ['<rootDir>/x-pack/platform/plugins/shared/screenshotting/server/**/*.{ts}'], +}; diff --git a/x-pack/platform/plugins/shared/screenshotting/jest.integration.config.js b/x-pack/platform/plugins/shared/screenshotting/jest.integration.config.js new file mode 100644 index 000000000000..d0694061ed92 --- /dev/null +++ b/x-pack/platform/plugins/shared/screenshotting/jest.integration.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test/jest_integration', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/platform/plugins/shared/screenshotting'], +}; diff --git a/x-pack/plugins/screenshotting/kibana.jsonc b/x-pack/platform/plugins/shared/screenshotting/kibana.jsonc similarity index 100% rename from x-pack/plugins/screenshotting/kibana.jsonc rename to x-pack/platform/plugins/shared/screenshotting/kibana.jsonc diff --git a/x-pack/plugins/screenshotting/public/app/app.scss b/x-pack/platform/plugins/shared/screenshotting/public/app/app.scss similarity index 100% rename from x-pack/plugins/screenshotting/public/app/app.scss rename to x-pack/platform/plugins/shared/screenshotting/public/app/app.scss diff --git a/x-pack/plugins/screenshotting/public/app/app.tsx b/x-pack/platform/plugins/shared/screenshotting/public/app/app.tsx similarity index 100% rename from x-pack/plugins/screenshotting/public/app/app.tsx rename to x-pack/platform/plugins/shared/screenshotting/public/app/app.tsx diff --git a/x-pack/plugins/screenshotting/public/app/index.ts b/x-pack/platform/plugins/shared/screenshotting/public/app/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/public/app/index.ts rename to x-pack/platform/plugins/shared/screenshotting/public/app/index.ts diff --git a/x-pack/plugins/screenshotting/public/app/screenshot_mode_context.ts b/x-pack/platform/plugins/shared/screenshotting/public/app/screenshot_mode_context.ts similarity index 100% rename from x-pack/plugins/screenshotting/public/app/screenshot_mode_context.ts rename to x-pack/platform/plugins/shared/screenshotting/public/app/screenshot_mode_context.ts diff --git a/x-pack/plugins/screenshotting/public/index.ts b/x-pack/platform/plugins/shared/screenshotting/public/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/public/index.ts rename to x-pack/platform/plugins/shared/screenshotting/public/index.ts diff --git a/x-pack/plugins/screenshotting/public/plugin.tsx b/x-pack/platform/plugins/shared/screenshotting/public/plugin.tsx similarity index 100% rename from x-pack/plugins/screenshotting/public/plugin.tsx rename to x-pack/platform/plugins/shared/screenshotting/public/plugin.tsx diff --git a/x-pack/plugins/screenshotting/server/__mocks__/puppeteer.ts b/x-pack/platform/plugins/shared/screenshotting/server/__mocks__/puppeteer.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/__mocks__/puppeteer.ts rename to x-pack/platform/plugins/shared/screenshotting/server/__mocks__/puppeteer.ts diff --git a/x-pack/plugins/screenshotting/server/assets/fonts/noto/LICENSE_OFL.txt b/x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/noto/LICENSE_OFL.txt similarity index 100% rename from x-pack/plugins/screenshotting/server/assets/fonts/noto/LICENSE_OFL.txt rename to x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/noto/LICENSE_OFL.txt diff --git a/x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Medium.ttf b/x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Medium.ttf similarity index 100% rename from x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Medium.ttf rename to x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Medium.ttf diff --git a/x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Regular.ttf b/x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Regular.ttf similarity index 100% rename from x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Regular.ttf rename to x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Regular.ttf diff --git a/x-pack/plugins/screenshotting/server/assets/fonts/noto/index.js b/x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/noto/index.js similarity index 100% rename from x-pack/plugins/screenshotting/server/assets/fonts/noto/index.js rename to x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/noto/index.js diff --git a/x-pack/plugins/screenshotting/server/assets/fonts/roboto/LICENSE.txt b/x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/LICENSE.txt similarity index 100% rename from x-pack/plugins/screenshotting/server/assets/fonts/roboto/LICENSE.txt rename to x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/LICENSE.txt diff --git a/x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Italic.ttf b/x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/Roboto-Italic.ttf similarity index 100% rename from x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Italic.ttf rename to x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/Roboto-Italic.ttf diff --git a/x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Medium.ttf b/x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/Roboto-Medium.ttf similarity index 100% rename from x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Medium.ttf rename to x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/Roboto-Medium.ttf diff --git a/x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Regular.ttf b/x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/Roboto-Regular.ttf similarity index 100% rename from x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Regular.ttf rename to x-pack/platform/plugins/shared/screenshotting/server/assets/fonts/roboto/Roboto-Regular.ttf diff --git a/x-pack/plugins/screenshotting/server/assets/img/logo-grey.png b/x-pack/platform/plugins/shared/screenshotting/server/assets/img/logo-grey.png similarity index 100% rename from x-pack/plugins/screenshotting/server/assets/img/logo-grey.png rename to x-pack/platform/plugins/shared/screenshotting/server/assets/img/logo-grey.png diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/driver.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver_factory/index.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver_factory/index.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver_factory/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver_factory/index.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/metrics.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver_factory/metrics.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/metrics.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver_factory/metrics.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/metrics.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver_factory/metrics.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/metrics.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver_factory/metrics.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/index.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/integration_tests/downloads.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/integration_tests/downloads.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/integration_tests/downloads.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/integration_tests/downloads.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/strip_unsafe_headers.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/strip_unsafe_headers.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/strip_unsafe_headers.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/strip_unsafe_headers.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/strip_unsafe_headers.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/strip_unsafe_headers.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/strip_unsafe_headers.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/strip_unsafe_headers.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/templates/footer.handlebars.html b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/footer.handlebars.html similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/templates/footer.handlebars.html rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/footer.handlebars.html diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/templates/header.handlebars.html b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/header.handlebars.html similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/templates/header.handlebars.html rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/header.handlebars.html diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/templates/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/chromium/templates/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/index.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/download/checksum.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/download/checksum.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/download/checksum.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/download/checksum.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/download/checksum.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/download/checksum.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/download/checksum.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/download/checksum.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/download/fetch.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/download/fetch.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/download/fetch.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/download/fetch.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/download/fetch.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/download/fetch.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/download/fetch.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/download/fetch.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/download/index.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/download/index.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/download/index.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/download/index.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/download/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/download/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/download/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/download/index.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/extract/__fixtures__/file.md b/x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/__fixtures__/file.md similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/extract/__fixtures__/file.md rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/__fixtures__/file.md diff --git a/x-pack/plugins/screenshotting/server/browsers/extract/__fixtures__/file.md.zip b/x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/__fixtures__/file.md.zip similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/extract/__fixtures__/file.md.zip rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/__fixtures__/file.md.zip diff --git a/x-pack/plugins/screenshotting/server/browsers/extract/extract.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/extract.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/extract/extract.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/extract.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/extract/extract.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/extract.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/extract/extract.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/extract.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/extract/extract_error.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/extract_error.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/extract/extract_error.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/extract_error.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/extract/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/extract/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/index.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/extract/unzip.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/unzip.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/extract/unzip.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/unzip.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/extract/unzip.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/unzip.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/extract/unzip.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/extract/unzip.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/index.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/install.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/install.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/install.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/install.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/mock.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/mock.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/mock.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/mock.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/network_policy.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/network_policy.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/network_policy.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/network_policy.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/network_policy.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/network_policy.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/network_policy.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/network_policy.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/safe_child_process.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/safe_child_process.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/browsers/safe_child_process.ts rename to x-pack/platform/plugins/shared/screenshotting/server/browsers/safe_child_process.ts diff --git a/x-pack/plugins/screenshotting/server/cloud/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/cloud/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/cloud/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/cloud/index.ts diff --git a/x-pack/plugins/screenshotting/server/config/schema.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/config/schema.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/config/schema.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/config/schema.test.ts diff --git a/x-pack/plugins/screenshotting/server/constants.ts b/x-pack/platform/plugins/shared/screenshotting/server/constants.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/constants.ts rename to x-pack/platform/plugins/shared/screenshotting/server/constants.ts diff --git a/x-pack/plugins/screenshotting/server/formats/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/index.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/index.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/README.md b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/README.md similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/README.md rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/README.md diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/constants.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/constants.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/constants.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/constants.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/get_doc_options.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/get_doc_options.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/get_doc_options.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/get_doc_options.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/get_font.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/get_font.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/get_font.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/get_font.test.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/get_font.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/get_font.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/get_font.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/get_font.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/get_template.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/get_template.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/get_template.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/get_template.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/index.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/integration_tests/buggy_worker.js b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/integration_tests/buggy_worker.js similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/integration_tests/buggy_worker.js rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/integration_tests/buggy_worker.js diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/integration_tests/memory_leak_worker.js b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/integration_tests/memory_leak_worker.js similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/integration_tests/memory_leak_worker.js rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/integration_tests/memory_leak_worker.js diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/integration_tests/pdfmaker.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/integration_tests/pdfmaker.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/integration_tests/pdfmaker.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/integration_tests/pdfmaker.test.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/types.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/types.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/types.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/types.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/worker.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/worker.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker_dependencies.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/worker_dependencies.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker_dependencies.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/worker_dependencies.ts diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker_src_harness.js b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/worker_src_harness.js similarity index 90% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker_src_harness.js rename to x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/worker_src_harness.js index ca319ace9d02..05a6c8eaa975 100644 --- a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker_src_harness.js +++ b/x-pack/platform/plugins/shared/screenshotting/server/formats/pdf/pdf_maker/worker_src_harness.js @@ -11,6 +11,6 @@ */ // eslint-disable-next-line @kbn/imports/no_boundary_crossing -require('../../../../../../../src/setup_node_env'); +require('../../../../../../../../../src/setup_node_env'); // eslint-disable-next-line @kbn/imports/uniform_imports require('./worker.ts'); diff --git a/x-pack/plugins/screenshotting/server/formats/png.ts b/x-pack/platform/plugins/shared/screenshotting/server/formats/png.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/png.ts rename to x-pack/platform/plugins/shared/screenshotting/server/formats/png.ts diff --git a/x-pack/plugins/screenshotting/server/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/index.ts diff --git a/x-pack/plugins/screenshotting/server/layouts/base_layout.ts b/x-pack/platform/plugins/shared/screenshotting/server/layouts/base_layout.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/layouts/base_layout.ts rename to x-pack/platform/plugins/shared/screenshotting/server/layouts/base_layout.ts diff --git a/x-pack/plugins/screenshotting/server/layouts/canvas_layout.ts b/x-pack/platform/plugins/shared/screenshotting/server/layouts/canvas_layout.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/layouts/canvas_layout.ts rename to x-pack/platform/plugins/shared/screenshotting/server/layouts/canvas_layout.ts diff --git a/x-pack/plugins/screenshotting/server/layouts/create_layout.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/layouts/create_layout.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/layouts/create_layout.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/layouts/create_layout.test.ts diff --git a/x-pack/plugins/screenshotting/server/layouts/create_layout.ts b/x-pack/platform/plugins/shared/screenshotting/server/layouts/create_layout.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/layouts/create_layout.ts rename to x-pack/platform/plugins/shared/screenshotting/server/layouts/create_layout.ts diff --git a/x-pack/plugins/screenshotting/server/layouts/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/layouts/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/layouts/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/layouts/index.ts diff --git a/x-pack/plugins/screenshotting/server/layouts/mock.ts b/x-pack/platform/plugins/shared/screenshotting/server/layouts/mock.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/layouts/mock.ts rename to x-pack/platform/plugins/shared/screenshotting/server/layouts/mock.ts diff --git a/x-pack/plugins/screenshotting/server/layouts/preserve_layout.css b/x-pack/platform/plugins/shared/screenshotting/server/layouts/preserve_layout.css similarity index 100% rename from x-pack/plugins/screenshotting/server/layouts/preserve_layout.css rename to x-pack/platform/plugins/shared/screenshotting/server/layouts/preserve_layout.css diff --git a/x-pack/plugins/screenshotting/server/layouts/preserve_layout.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/layouts/preserve_layout.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/layouts/preserve_layout.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/layouts/preserve_layout.test.ts diff --git a/x-pack/plugins/screenshotting/server/layouts/preserve_layout.ts b/x-pack/platform/plugins/shared/screenshotting/server/layouts/preserve_layout.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/layouts/preserve_layout.ts rename to x-pack/platform/plugins/shared/screenshotting/server/layouts/preserve_layout.ts diff --git a/x-pack/plugins/screenshotting/server/layouts/print_layout.ts b/x-pack/platform/plugins/shared/screenshotting/server/layouts/print_layout.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/layouts/print_layout.ts rename to x-pack/platform/plugins/shared/screenshotting/server/layouts/print_layout.ts diff --git a/x-pack/plugins/screenshotting/server/mock.ts b/x-pack/platform/plugins/shared/screenshotting/server/mock.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/mock.ts rename to x-pack/platform/plugins/shared/screenshotting/server/mock.ts diff --git a/x-pack/plugins/screenshotting/server/plugin.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/plugin.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/plugin.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/plugin.test.ts diff --git a/x-pack/plugins/screenshotting/server/plugin.ts b/x-pack/platform/plugins/shared/screenshotting/server/plugin.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/plugin.ts rename to x-pack/platform/plugins/shared/screenshotting/server/plugin.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/__snapshots__/index.test.ts.snap b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/__snapshots__/index.test.ts.snap similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/__snapshots__/index.test.ts.snap rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/__snapshots__/index.test.ts.snap diff --git a/x-pack/plugins/screenshotting/server/screenshots/constants.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/constants.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/constants.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/constants.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/event_logger/index.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/event_logger/index.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/event_logger/index.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/event_logger/index.test.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/event_logger/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/event_logger/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/event_logger/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/event_logger/index.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_element_position_data.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_element_position_data.test.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_element_position_data.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_element_position_data.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_number_of_items.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_number_of_items.test.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_number_of_items.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_number_of_items.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_pdf.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_pdf.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_pdf.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_pdf.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_render_errors.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_render_errors.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_render_errors.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_render_errors.test.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_render_errors.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_render_errors.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_render_errors.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_render_errors.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_screenshots.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_screenshots.test.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_screenshots.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_screenshots.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_time_range.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_time_range.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_time_range.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_time_range.test.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_time_range.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_time_range.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/get_time_range.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/get_time_range.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/index.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/index.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/index.test.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/index.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/index.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/inject_css.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/inject_css.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/inject_css.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/inject_css.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/mock.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/mock.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/mock.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/mock.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/observable.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/observable.test.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/observable.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/observable.test.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/observable.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/observable.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/observable.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/observable.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/open_url.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/open_url.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/open_url.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/open_url.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/screenshots.test.ts similarity index 98% rename from x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/screenshots.test.ts index 4f217ef96c61..95261626e15f 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts +++ b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/screenshots.test.ts @@ -30,7 +30,8 @@ describe('class Screenshots', () => { let mockLogger: Logger; let mockScreenshotModeSetup: ScreenshotModePluginSetup; - const mockBinaryPath = '/kibana/x-pack/plugins/screenshotting/chromium/linux/headless_shell'; + const mockBinaryPath = + '/kibana/x-pack/platform/plugins/shared/screenshotting/chromium/linux/headless_shell'; const mockBasePath = '/kibanaTest1'; beforeEach(() => { diff --git a/x-pack/plugins/screenshotting/server/screenshots/screenshots.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/screenshots.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/screenshots.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/screenshots.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/types.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/types.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/types.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/types.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/wait_for_render.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/wait_for_render.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/wait_for_render.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/wait_for_render.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/wait_for_visualizations.ts b/x-pack/platform/plugins/shared/screenshotting/server/screenshots/wait_for_visualizations.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/wait_for_visualizations.ts rename to x-pack/platform/plugins/shared/screenshotting/server/screenshots/wait_for_visualizations.ts diff --git a/x-pack/plugins/screenshotting/server/utils.ts b/x-pack/platform/plugins/shared/screenshotting/server/utils.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/utils.ts rename to x-pack/platform/plugins/shared/screenshotting/server/utils.ts diff --git a/x-pack/platform/plugins/shared/screenshotting/tsconfig.json b/x-pack/platform/plugins/shared/screenshotting/tsconfig.json new file mode 100644 index 000000000000..200c6d9c2592 --- /dev/null +++ b/x-pack/platform/plugins/shared/screenshotting/tsconfig.json @@ -0,0 +1,33 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + "../../../../../typings/**/*" + ], + "kbn_references": [ + "@kbn/core", + { "path": "../../../../../src/setup_node_env/tsconfig.json" }, + "@kbn/expressions-plugin", + "@kbn/screenshot-mode-plugin", + "@kbn/cloud-plugin", + "@kbn/utility-types", + "@kbn/logging", + "@kbn/std", + "@kbn/i18n", + "@kbn/utils", + "@kbn/core-logging-server-mocks", + "@kbn/logging-mocks", + "@kbn/core-http-server", + "@kbn/core-plugins-server", + "@kbn/task-manager-plugin", + "@kbn/screenshotting-server", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/x-pack/plugins/actions/server/lib/gen_ai_token_tracking.ts b/x-pack/plugins/actions/server/lib/gen_ai_token_tracking.ts index d73610892098..ff73095ac242 100644 --- a/x-pack/plugins/actions/server/lib/gen_ai_token_tracking.ts +++ b/x-pack/plugins/actions/server/lib/gen_ai_token_tracking.ts @@ -295,4 +295,7 @@ export const getGenAiTokenTracking = async ({ }; export const shouldTrackGenAiToken = (actionTypeId: string) => - actionTypeId === '.gen-ai' || actionTypeId === '.bedrock' || actionTypeId === '.gemini'; + actionTypeId === '.gen-ai' || + actionTypeId === '.bedrock' || + actionTypeId === '.gemini' || + actionTypeId === '.inference'; diff --git a/x-pack/plugins/enterprise_search/common/types/index.ts b/x-pack/plugins/enterprise_search/common/types/index.ts index 1d9fb9b2cb0c..364008ef18b4 100644 --- a/x-pack/plugins/enterprise_search/common/types/index.ts +++ b/x-pack/plugins/enterprise_search/common/types/index.ts @@ -62,7 +62,6 @@ export interface Meta { } export interface ClientConfigType { - canDeployEntSearch: boolean; host?: string; ui: { enabled: boolean; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/authentication_panel/authentication_panel_actions.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/authentication_panel/authentication_panel_actions.tsx index ba6e9d2c1931..49d15c041f14 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/authentication_panel/authentication_panel_actions.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/authentication_panel/authentication_panel_actions.tsx @@ -61,7 +61,7 @@ export const AuthenticationPanelActions: React.FC = () => { ) : currentAuth === null ? ( <EuiButton data-telemetry-id="entSearchContent-crawler-domainDetail-authentication-addCredentials" - color="success" + color="primary" iconType="plusInCircle" size="s" onClick={() => enableEditing(currentAuth)} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/domain_management/domains_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/domain_management/domains_panel.tsx index 41af7db78b6b..60f2e5221197 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/domain_management/domains_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/domain_management/domains_panel.tsx @@ -47,7 +47,7 @@ export const DomainsPanel: React.FC = () => { data-telemetry-id="entSearchContent-crawler-domainManagement-addDomain-addDomain" onClick={openFlyout} size="s" - color="success" + color="primary" iconType="plusInCircle" > {i18n.translate('xpack.enterpriseSearch.crawler.addDomainFlyout.openButtonLabel', { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_ml_inference_button.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_ml_inference_button.tsx index 1f8bc30e2903..c7c296a443eb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_ml_inference_button.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_ml_inference_button.tsx @@ -79,7 +79,7 @@ const AddButton: React.FC<{ <EuiButton fullWidth data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-addInferencePipeline`} - color={disabled ? undefined : 'success'} + color={disabled ? undefined : 'primary'} disabled={disabled} iconType={disabled ? 'lock' : 'plusInCircle'} onClick={onClick} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/deploy_model.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/deploy_model.tsx index 018ee2750094..c666b5f5adaf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/deploy_model.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/deploy_model.tsx @@ -38,12 +38,12 @@ export const DeployModel = ({ const { createTextExpansionModel } = useActions(TextExpansionCalloutLogic); return ( - <EuiCallOut color="success"> + <EuiCallOut color="primary"> <EuiFlexGroup direction="column" gutterSize="s"> <EuiFlexItem> <EuiFlexGroup direction="row" gutterSize="s" alignItems="center"> <EuiFlexItem grow={false}> - <EuiBadge color="success"> + <EuiBadge color="primary"> <FormattedMessage id="xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.titleBadge" defaultMessage="New" @@ -51,7 +51,7 @@ export const DeployModel = ({ </EuiBadge> </EuiFlexItem> <EuiFlexItem grow> - <EuiText color="success" size="xs"> + <EuiText color="primary" size="xs"> <h3> {i18n.translate( 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.title', @@ -87,7 +87,7 @@ export const DeployModel = ({ > <EuiFlexItem grow={false}> <EuiButton - color="success" + color="primary" data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-textExpansionCallOut-deployModel`} disabled={isCreateButtonDisabled} iconType="launch" diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx index 40b7c9596cb3..4ea807262d20 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx @@ -190,7 +190,7 @@ export const SearchIndexPipelines: React.FC = () => { hasIndexIngestionPipeline ? ( <EuiFlexGroup alignItems="center" gutterSize="xs" justifyContent="center"> <EuiFlexItem grow={false}> - <EuiBadge color="success"> + <EuiBadge color="accent"> {i18n.translate( 'xpack.enterpriseSearch.content.indices.pipelines.ingestionPipeline.customBadge', { defaultMessage: 'Custom' } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/header_actions/syncs_context_menu.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/header_actions/syncs_context_menu.tsx index bdae6e0e2853..b0c887a4c222 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/header_actions/syncs_context_menu.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/header_actions/syncs_context_menu.tsx @@ -80,9 +80,7 @@ export const SyncsContextMenu: React.FC<SyncsContextMenuProps> = ({ disabled = f const shouldShowIncrementalSync = productFeatures.hasIncrementalSyncEnabled && hasIncrementalSyncFeature; - const isEnterpriseSearchNotAvailable = Boolean( - config.host && config.canDeployEntSearch && errorConnectingMessage - ); + const isEnterpriseSearchNotAvailable = Boolean(config.host && errorConnectingMessage); const isSyncsDisabled = (connector?.is_native && isEnterpriseSearchNotAvailable) || ingestionStatus === IngestionStatus.INCOMPLETE || diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/index.test.tsx index a32062256eec..dbbc6a746ee5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/index.test.tsx @@ -29,20 +29,13 @@ describe('EnterpriseSearchContent', () => { it('renders EnterpriseSearchContentConfigured when config.host is set & available', () => { setMockValues({ - config: { canDeployEntSearch: true, host: 'some.url' }, + config: { host: 'some.url' }, errorConnectingMessage: '', }); const wrapper = shallow(<EnterpriseSearchContent />); expect(wrapper.find(EnterpriseSearchContentConfigured)).toHaveLength(1); }); - - it('renders EnterpriseSearchContentConfigured when config.host is not set & Ent Search cannot be deployed', () => { - setMockValues({ config: { canDeployEntSearch: false, host: '' }, errorConnectingMessage: '' }); - const wrapper = shallow(<EnterpriseSearchContent />); - - expect(wrapper.find(EnterpriseSearchContentConfigured)).toHaveLength(1); - }); }); describe('EnterpriseSearchContentConfigured', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.test.tsx index 74be43ab3225..b1c0394e8023 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/enterprise_search_product_card.test.tsx @@ -17,7 +17,7 @@ import { WorkplaceSearchProductCard } from './workplace_search_product_card'; describe('EnterpriseSearchProductCard', () => { beforeEach(() => { - setMockValues({ config: { canDeployEntSearch: true, host: 'localhost' } }); + setMockValues({ config: { host: 'localhost' } }); }); it('renders both services with access', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.test.tsx index 3718a495cd17..b8f7ba1d354d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.test.tsx @@ -19,14 +19,14 @@ import { ProductSelector } from '.'; describe('ProductSelector', () => { it('renders the overview page, product cards, & setup guide CTAs with no host set', () => { - setMockValues({ config: { canDeployEntSearch: true, host: '' } }); + setMockValues({ config: { host: '' } }); const wrapper = shallow(<ProductSelector />); expect(wrapper.find(ElasticsearchProductCard)).toHaveLength(1); }); it('renders the trial callout', () => { - setMockValues({ config: { canDeployEntSearch: true, host: 'localhost' } }); + setMockValues({ config: { host: 'localhost' } }); const wrapper = shallow(<ProductSelector />); expect(wrapper.find(TrialCallout)).toHaveLength(1); @@ -34,7 +34,7 @@ describe('ProductSelector', () => { describe('access checks when host is set', () => { beforeEach(() => { - setMockValues({ config: { canDeployEntSearch: true, host: 'localhost' } }); + setMockValues({ config: { host: 'localhost' } }); }); it('does not render the Setup CTA when there is a host', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_banner.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_banner.tsx index d9f1be314a56..a1b3302cd35a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_banner.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_banner.tsx @@ -36,10 +36,11 @@ export const SearchLabsBanner: React.FC = () => { <EuiPanel hasBorder hasShadow - color="success" + color="accentSecondary" css={css` background-image: url(${backgroundImagePath}); background-repeat: no-repeat; + background-size: cover; `} > <SearchLabsLogo diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/tables/inline_editable_table/inline_editable_table.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/tables/inline_editable_table/inline_editable_table.tsx index d14acc91ac58..df53a0dc26a3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/tables/inline_editable_table/inline_editable_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/tables/inline_editable_table/inline_editable_table.tsx @@ -140,7 +140,7 @@ export const InlineEditableTableContents = <Item extends ItemWithAnID>({ iconType="plusInCircle" disabled={isEditing} onClick={editNewItem} - color="success" + color="primary" data-test-subj="inlineEditableTableActionButton" > {addButtonText || diff --git a/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx b/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx index e046bfa904e5..13694a3f7db6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx @@ -44,7 +44,6 @@ export const mockKibanaProps: KibanaLogicProps = { isCloudEnabled: false, }, config: { - canDeployEntSearch: true, host: 'http://localhost:3002', ui: { enabled: true, diff --git a/x-pack/plugins/enterprise_search/public/applications/vector_search/components/dev_tools_console_code_block/dev_tools_console_code_block.tsx b/x-pack/plugins/enterprise_search/public/applications/vector_search/components/dev_tools_console_code_block/dev_tools_console_code_block.tsx index d48011d54cdf..46a60456be7c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/vector_search/components/dev_tools_console_code_block/dev_tools_console_code_block.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/vector_search/components/dev_tools_console_code_block/dev_tools_console_code_block.tsx @@ -39,7 +39,7 @@ export const DevToolsConsoleCodeBlock: React.FC<DevToolsConsoleCodeBlockProps> = return ( <EuiThemeProvider colorMode="dark"> - <EuiPanel hasShadow={false}> + <EuiPanel hasShadow={false} hasBorder> <EuiFlexGroup direction="column" gutterSize="xs"> <EuiFlexGroup direction="rowReverse" diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index 0f90f60d2974..0a472cd2aa56 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -441,55 +441,53 @@ export class EnterpriseSearchPlugin implements Plugin { registerLocators(share!); - if (config.canDeployEntSearch) { - core.application.register({ - appRoute: APP_SEARCH_PLUGIN.URL, - category: DEFAULT_APP_CATEGORIES.enterpriseSearch, - deepLinks: appSearchLinks, - euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO, - id: APP_SEARCH_PLUGIN.ID, - mount: async (params: AppMountParameters) => { - const kibanaDeps = await this.getKibanaDeps(core, params, cloud); - const { chrome, http } = kibanaDeps.core; - chrome.docTitle.change(APP_SEARCH_PLUGIN.NAME); + core.application.register({ + appRoute: APP_SEARCH_PLUGIN.URL, + category: DEFAULT_APP_CATEGORIES.enterpriseSearch, + deepLinks: appSearchLinks, + euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO, + id: APP_SEARCH_PLUGIN.ID, + mount: async (params: AppMountParameters) => { + const kibanaDeps = await this.getKibanaDeps(core, params, cloud); + const { chrome, http } = kibanaDeps.core; + chrome.docTitle.change(APP_SEARCH_PLUGIN.NAME); - await this.getInitialData(http); - const pluginData = this.getPluginData(); + await this.getInitialData(http); + const pluginData = this.getPluginData(); - const { renderApp } = await import('./applications'); - const { AppSearch } = await import('./applications/app_search'); + const { renderApp } = await import('./applications'); + const { AppSearch } = await import('./applications/app_search'); - return renderApp(AppSearch, kibanaDeps, pluginData); - }, - title: APP_SEARCH_PLUGIN.NAME, - visibleIn: [], - }); + return renderApp(AppSearch, kibanaDeps, pluginData); + }, + title: APP_SEARCH_PLUGIN.NAME, + visibleIn: [], + }); - core.application.register({ - appRoute: WORKPLACE_SEARCH_PLUGIN.URL, - category: DEFAULT_APP_CATEGORIES.enterpriseSearch, - euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO, - id: WORKPLACE_SEARCH_PLUGIN.ID, - mount: async (params: AppMountParameters) => { - const kibanaDeps = await this.getKibanaDeps(core, params, cloud); - const { chrome, http } = kibanaDeps.core; - chrome.docTitle.change(WORKPLACE_SEARCH_PLUGIN.NAME); - - // The Workplace Search Personal dashboard needs the chrome hidden. We hide it globally - // here first to prevent a flash of chrome on the Personal dashboard and unhide it for admin routes. - if (this.config.host) chrome.setIsVisible(false); - await this.getInitialData(http); - const pluginData = this.getPluginData(); - - const { renderApp } = await import('./applications'); - const { WorkplaceSearch } = await import('./applications/workplace_search'); - - return renderApp(WorkplaceSearch, kibanaDeps, pluginData); - }, - title: WORKPLACE_SEARCH_PLUGIN.NAME, - visibleIn: [], - }); - } + core.application.register({ + appRoute: WORKPLACE_SEARCH_PLUGIN.URL, + category: DEFAULT_APP_CATEGORIES.enterpriseSearch, + euiIconType: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.LOGO, + id: WORKPLACE_SEARCH_PLUGIN.ID, + mount: async (params: AppMountParameters) => { + const kibanaDeps = await this.getKibanaDeps(core, params, cloud); + const { chrome, http } = kibanaDeps.core; + chrome.docTitle.change(WORKPLACE_SEARCH_PLUGIN.NAME); + + // The Workplace Search Personal dashboard needs the chrome hidden. We hide it globally + // here first to prevent a flash of chrome on the Personal dashboard and unhide it for admin routes. + if (this.config.host) chrome.setIsVisible(false); + await this.getInitialData(http); + const pluginData = this.getPluginData(); + + const { renderApp } = await import('./applications'); + const { WorkplaceSearch } = await import('./applications/workplace_search'); + + return renderApp(WorkplaceSearch, kibanaDeps, pluginData); + }, + title: WORKPLACE_SEARCH_PLUGIN.NAME, + visibleIn: [], + }); if (plugins.home) { plugins.home.featureCatalogue.registerSolution({ @@ -511,27 +509,25 @@ export class EnterpriseSearchPlugin implements Plugin { title: ANALYTICS_PLUGIN.NAME, }); - if (config.canDeployEntSearch) { - plugins.home.featureCatalogue.register({ - category: 'data', - description: APP_SEARCH_PLUGIN.DESCRIPTION, - icon: 'appSearchApp', - id: APP_SEARCH_PLUGIN.ID, - path: APP_SEARCH_PLUGIN.URL, - showOnHomePage: false, - title: APP_SEARCH_PLUGIN.NAME, - }); + plugins.home.featureCatalogue.register({ + category: 'data', + description: APP_SEARCH_PLUGIN.DESCRIPTION, + icon: 'appSearchApp', + id: APP_SEARCH_PLUGIN.ID, + path: APP_SEARCH_PLUGIN.URL, + showOnHomePage: false, + title: APP_SEARCH_PLUGIN.NAME, + }); - plugins.home.featureCatalogue.register({ - category: 'data', - description: WORKPLACE_SEARCH_PLUGIN.DESCRIPTION, - icon: 'workplaceSearchApp', - id: WORKPLACE_SEARCH_PLUGIN.ID, - path: WORKPLACE_SEARCH_PLUGIN.URL, - showOnHomePage: false, - title: WORKPLACE_SEARCH_PLUGIN.NAME, - }); - } + plugins.home.featureCatalogue.register({ + category: 'data', + description: WORKPLACE_SEARCH_PLUGIN.DESCRIPTION, + icon: 'workplaceSearchApp', + id: WORKPLACE_SEARCH_PLUGIN.ID, + path: WORKPLACE_SEARCH_PLUGIN.URL, + showOnHomePage: false, + title: WORKPLACE_SEARCH_PLUGIN.NAME, + }); plugins.home.featureCatalogue.register({ category: 'data', diff --git a/x-pack/plugins/enterprise_search/server/index.ts b/x-pack/plugins/enterprise_search/server/index.ts index 53d8cbca7f54..9efaa123ea99 100644 --- a/x-pack/plugins/enterprise_search/server/index.ts +++ b/x-pack/plugins/enterprise_search/server/index.ts @@ -16,7 +16,6 @@ export const plugin = async (initializerContext: PluginInitializerContext) => { export const configSchema = schema.object({ accessCheckTimeout: schema.number({ defaultValue: 5000 }), accessCheckTimeoutWarning: schema.number({ defaultValue: 300 }), - canDeployEntSearch: schema.boolean({ defaultValue: true }), customHeaders: schema.maybe(schema.object({}, { unknowns: 'allow' })), enabled: schema.boolean({ defaultValue: true }), hasConnectors: schema.boolean({ defaultValue: true }), @@ -44,8 +43,8 @@ export const configSchema = schema.object({ export type ConfigType = TypeOf<typeof configSchema>; export const config: PluginConfigDescriptor<ConfigType> = { + deprecations: ({ unused }) => [unused('canDeployEntSearch', { level: 'warning' })], exposeToBrowser: { - canDeployEntSearch: true, host: true, ui: true, }, diff --git a/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts b/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts index ab4b27ed1f1c..ef956ae85f68 100644 --- a/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts @@ -50,7 +50,6 @@ describe('checkAccess', () => { const mockDependencies = { request: { auth: { isAuthenticated: true } }, config: { - canDeployEntSearch: true, host: 'http://localhost:3002', }, globalConfigService: new GlobalConfigService(), diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index 9120db1b93ac..1d4ba5e6dfd1 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -58,10 +58,19 @@ import { databaseSearchGuideConfig, } from '../common/guided_onboarding/search_guide_config'; -import { registerTelemetryUsageCollector as registerASTelemetryUsageCollector } from './collectors/app_search/telemetry'; +import { + AS_TELEMETRY_NAME, + registerTelemetryUsageCollector as registerASTelemetryUsageCollector, +} from './collectors/app_search/telemetry'; import { registerTelemetryUsageCollector as registerCNTelemetryUsageCollector } from './collectors/connectors/telemetry'; -import { registerTelemetryUsageCollector as registerESTelemetryUsageCollector } from './collectors/enterprise_search/telemetry'; -import { registerTelemetryUsageCollector as registerWSTelemetryUsageCollector } from './collectors/workplace_search/telemetry'; +import { + ES_TELEMETRY_NAME, + registerTelemetryUsageCollector as registerESTelemetryUsageCollector, +} from './collectors/enterprise_search/telemetry'; +import { + WS_TELEMETRY_NAME, + registerTelemetryUsageCollector as registerWSTelemetryUsageCollector, +} from './collectors/workplace_search/telemetry'; import { registerEnterpriseSearchIntegrations } from './integrations'; import { entSearchHttpAgent } from './lib/enterprise_search_http_agent'; @@ -160,7 +169,8 @@ export class EnterpriseSearchPlugin implements Plugin { ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.ID, ENTERPRISE_SEARCH_CONTENT_PLUGIN.ID, ELASTICSEARCH_PLUGIN.ID, - ...(config.canDeployEntSearch ? [APP_SEARCH_PLUGIN.ID, WORKPLACE_SEARCH_PLUGIN.ID] : []), + APP_SEARCH_PLUGIN.ID, + WORKPLACE_SEARCH_PLUGIN.ID, SEARCH_EXPERIENCES_PLUGIN.ID, VECTOR_SEARCH_PLUGIN.ID, SEMANTIC_SEARCH_PLUGIN.ID, @@ -194,8 +204,8 @@ export class EnterpriseSearchPlugin implements Plugin { api: [], catalogue: PLUGIN_IDS, savedObject: { - all: [], - read: [], + all: [ES_TELEMETRY_NAME, AS_TELEMETRY_NAME, WS_TELEMETRY_NAME], + read: [ES_TELEMETRY_NAME, AS_TELEMETRY_NAME, WS_TELEMETRY_NAME], }, ui: [], }, @@ -291,9 +301,9 @@ export class EnterpriseSearchPlugin implements Plugin { }; registerConfigDataRoute(dependencies); - if (config.canDeployEntSearch) registerAppSearchRoutes(dependencies); + registerAppSearchRoutes(dependencies); registerEnterpriseSearchRoutes(dependencies); - if (config.canDeployEntSearch) registerWorkplaceSearchRoutes(dependencies); + registerWorkplaceSearchRoutes(dependencies); // Enterprise Search Routes if (config.hasConnectors) registerConnectorRoutes(dependencies); if (config.hasWebCrawler) registerCrawlerRoutes(dependencies); @@ -310,10 +320,8 @@ export class EnterpriseSearchPlugin implements Plugin { * Bootstrap the routes, saved objects, and collector for telemetry */ savedObjects.registerType(enterpriseSearchTelemetryType); - if (config.canDeployEntSearch) { - savedObjects.registerType(appSearchTelemetryType); - savedObjects.registerType(workplaceSearchTelemetryType); - } + savedObjects.registerType(appSearchTelemetryType); + savedObjects.registerType(workplaceSearchTelemetryType); let savedObjectsStarted: SavedObjectsServiceStart; void getStartServices().then(([coreStart]) => { @@ -322,10 +330,8 @@ export class EnterpriseSearchPlugin implements Plugin { if (usageCollection) { registerESTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); registerCNTelemetryUsageCollector(usageCollection, this.logger); - if (config.canDeployEntSearch) { - registerASTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); - registerWSTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); - } + registerASTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); + registerWSTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); } }); registerTelemetryRoute({ ...dependencies, getSavedObjectsService: () => savedObjectsStarted }); @@ -361,9 +367,7 @@ export class EnterpriseSearchPlugin implements Plugin { /** * Register a config for the search guide */ - if (config.canDeployEntSearch) { - guidedOnboarding?.registerGuideConfig(appSearchGuideId, appSearchGuideConfig); - } + guidedOnboarding?.registerGuideConfig(appSearchGuideId, appSearchGuideConfig); if (config.hasWebCrawler) { guidedOnboarding?.registerGuideConfig(websiteSearchGuideId, websiteSearchGuideConfig); } diff --git a/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts index 3e7a0777dad2..b3709df9ef5b 100644 --- a/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts +++ b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts @@ -292,7 +292,6 @@ describe('Search search provider', () => { it('does not return results for legacy app search', () => { const searchProvider = getSearchResultProvider( { - canDeployEntSearch: true, hasConnectors: false, hasWebCrawler: false, } as any, @@ -315,7 +314,6 @@ describe('Search search provider', () => { it('does not return results for legacy workplace search', () => { const searchProvider = getSearchResultProvider( { - canDeployEntSearch: true, hasConnectors: false, hasWebCrawler: false, } as any, diff --git a/x-pack/plugins/fleet/common/constants/plugin.ts b/x-pack/plugins/fleet/common/constants/plugin.ts index f9f71fb1608f..2efb4ab11b94 100644 --- a/x-pack/plugins/fleet/common/constants/plugin.ts +++ b/x-pack/plugins/fleet/common/constants/plugin.ts @@ -8,3 +8,4 @@ export const PLUGIN_ID = 'fleet' as const; export const INTEGRATIONS_PLUGIN_ID = 'integrations' as const; export const TRANSFORM_PLUGIN_ID = 'transform' as const; +export const ELASTICSEARCH_PLUGIN_ID = 'elasticsearch' as const; diff --git a/x-pack/plugins/fleet/common/experimental_features.ts b/x-pack/plugins/fleet/common/experimental_features.ts index 1c42bf3ab2af..1cf35952904a 100644 --- a/x-pack/plugins/fleet/common/experimental_features.ts +++ b/x-pack/plugins/fleet/common/experimental_features.ts @@ -10,7 +10,6 @@ export type ExperimentalFeatures = typeof allowedExperimentalValues; const _allowedExperimentalValues = { createPackagePolicyMultiPageLayout: true, packageVerification: true, - showDevtoolsRequest: true, diagnosticFileUploadEnabled: true, displayAgentMetrics: true, showIntegrationsSubcategories: true, diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index 36a83c1c0a09..bcae5230aab5 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -207,6 +207,15 @@ export interface DeploymentsModes { default?: DeploymentsModesDefault; } +type Action = 'action'; +type NextStep = 'next_step'; +export interface ConfigurationLink { + title: string; + url: string; + type: Action | NextStep; + content?: string; +} + export enum RegistryPolicyTemplateKeys { categories = 'categories', data_streams = 'data_streams', @@ -223,6 +232,7 @@ export enum RegistryPolicyTemplateKeys { icons = 'icons', screenshots = 'screenshots', deployment_modes = 'deployment_modes', + configuration_links = 'configuration_links', } interface BaseTemplate { [RegistryPolicyTemplateKeys.name]: string; @@ -232,6 +242,7 @@ interface BaseTemplate { [RegistryPolicyTemplateKeys.screenshots]?: RegistryImage[]; [RegistryPolicyTemplateKeys.multiple]?: boolean; [RegistryPolicyTemplateKeys.deployment_modes]?: DeploymentsModes; + [RegistryPolicyTemplateKeys.configuration_links]?: ConfigurationLink[]; } export interface RegistryPolicyIntegrationTemplate extends BaseTemplate { [RegistryPolicyTemplateKeys.categories]?: Array<PackageSpecCategory | undefined>; diff --git a/x-pack/plugins/fleet/public/applications/fleet/app.tsx b/x-pack/plugins/fleet/public/applications/fleet/app.tsx index 8a4edef8dc87..0041ffa09105 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/app.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/app.tsx @@ -247,7 +247,7 @@ const FleetTopNav = memo( const readOnlyBtnClass = React.useMemo(() => { return css` - color: ${euiTheme.colors.text}; + color: ${euiTheme.colors.textParagraph}; `; }, [euiTheme]); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx index dd3945664bd6..a0329a1c9ce0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx @@ -42,7 +42,8 @@ jest.mock('../../../../../hooks', () => { }; }); -describe('StepSelectHosts', () => { +// FLAKY: https://github.com/elastic/kibana/issues/203307 +describe.skip('StepSelectHosts', () => { const packageInfo: PackageInfo = { name: 'apache', version: '1.0.0', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/devtools_request.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/devtools_request.tsx index 91ca089f2ffe..81f2304efee9 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/devtools_request.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/devtools_request.tsx @@ -11,7 +11,6 @@ import { i18n } from '@kbn/i18n'; import { omit } from 'lodash'; import { set } from '@kbn/safer-lodash-set'; -import { ExperimentalFeaturesService } from '../../../../../services'; import { generateCreatePackagePolicyDevToolsRequest, generateCreateAgentPolicyDevToolsRequest, @@ -39,12 +38,7 @@ export function useDevToolsRequest({ packageInfo?: PackageInfo; packagePolicyId?: string; }) { - const { showDevtoolsRequest: isShowDevtoolRequestExperimentEnabled } = - ExperimentalFeaturesService.get(); - - const showDevtoolsRequest = - !HIDDEN_API_REFERENCE_PACKAGES.includes(packageInfo?.name ?? '') && - isShowDevtoolRequestExperimentEnabled; + const showDevtoolsRequest = !HIDDEN_API_REFERENCE_PACKAGES.includes(packageInfo?.name ?? ''); const [devtoolRequest, devtoolRequestDescription] = useMemo(() => { if (selectedPolicyTab === SelectedPolicyTab.NEW) { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx index 50baac84ff4a..efb7066b0d5e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx @@ -40,7 +40,6 @@ import { ConfirmDeployAgentPolicyModal, } from '../../../components'; import { DevtoolsRequestFlyoutButton } from '../../../../../components'; -import { ExperimentalFeaturesService } from '../../../../../services'; import { generateUpdateAgentPolicyDevToolsRequest } from '../../../services'; import { UNKNOWN_SPACE } from '../../../../../../../../common/constants'; @@ -155,7 +154,6 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>( setIsLoading(false); }; - const { showDevtoolsRequest } = ExperimentalFeaturesService.get(); const devtoolRequest = useMemo( () => generateUpdateAgentPolicyDevToolsRequest( @@ -235,28 +233,26 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>( /> </EuiButtonEmpty> </EuiFlexItem> - {showDevtoolsRequest ? ( - <EuiFlexItem grow={false}> - <DevtoolsRequestFlyoutButton - isDisabled={ - isLoading || - Object.keys(validation).length > 0 || - hasAdvancedSettingsErrors || - hasInvalidSpaceError + <EuiFlexItem grow={false}> + <DevtoolsRequestFlyoutButton + isDisabled={ + isLoading || + Object.keys(validation).length > 0 || + hasAdvancedSettingsErrors || + hasInvalidSpaceError + } + btnProps={{ + color: 'text', + }} + description={i18n.translate( + 'xpack.fleet.editAgentPolicy.devtoolsRequestDescription', + { + defaultMessage: 'This Kibana request updates an agent policy.', } - btnProps={{ - color: 'text', - }} - description={i18n.translate( - 'xpack.fleet.editAgentPolicy.devtoolsRequestDescription', - { - defaultMessage: 'This Kibana request updates an agent policy.', - } - )} - request={devtoolRequest} - /> - </EuiFlexItem> - ) : null} + )} + request={devtoolRequest} + /> + </EuiFlexItem> <EuiFlexItem grow={false}> <EuiButton onClick={onSubmit} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx index a5538e7e0fa3..f7a624664340 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx @@ -31,7 +31,6 @@ import { useAuthz, useStartServices, sendCreateAgentPolicy } from '../../../../h import { AgentPolicyForm, agentPolicyFormValidation } from '../../components'; import { DevtoolsRequestFlyoutButton } from '../../../../components'; import { generateCreateAgentPolicyDevToolsRequest } from '../../services'; -import { ExperimentalFeaturesService } from '../../../../services'; import { generateNewAgentPolicyWithDefaults } from '../../../../../../../common/services/generate_new_agent_policy'; const FlyoutWithHigherZIndex = styled(EuiFlyout)` @@ -109,7 +108,6 @@ export const CreateAgentPolicyFlyout: React.FunctionComponent<Props> = ({ /> </EuiFlyoutBody> ); - const { showDevtoolsRequest } = ExperimentalFeaturesService.get(); const agentPolicyContent = useMemo( () => generateCreateAgentPolicyDevToolsRequest(agentPolicy, withSysMonitoring), [agentPolicy, withSysMonitoring] @@ -128,25 +126,23 @@ export const CreateAgentPolicyFlyout: React.FunctionComponent<Props> = ({ </EuiFlexItem> <EuiFlexItem grow={false}> <EuiFlexGroup gutterSize="none"> - {showDevtoolsRequest ? ( - <EuiFlexItem grow={false}> - <DevtoolsRequestFlyoutButton - isDisabled={ - isLoading || - Object.keys(validation).length > 0 || - hasAdvancedSettingsErrors || - hasInvalidSpaceError + <EuiFlexItem grow={false}> + <DevtoolsRequestFlyoutButton + isDisabled={ + isLoading || + Object.keys(validation).length > 0 || + hasAdvancedSettingsErrors || + hasInvalidSpaceError + } + description={i18n.translate( + 'xpack.fleet.createAgentPolicy.devtoolsRequestDescription', + { + defaultMessage: 'This Kibana request creates a new agent policy.', } - description={i18n.translate( - 'xpack.fleet.createAgentPolicy.devtoolsRequestDescription', - { - defaultMessage: 'This Kibana request creates a new agent policy.', - } - )} - request={agentPolicyContent} - /> - </EuiFlexItem> - ) : null} + )} + request={agentPolicyContent} + /> + </EuiFlexItem> <EuiFlexItem grow={false}> <EuiButton fill diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_badges.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_badges.tsx index 44363d12088d..21ff0e22505b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_badges.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_badges.tsx @@ -5,7 +5,13 @@ * 2.0. */ -import { EuiFlexGroup, EuiHealth, EuiNotificationBadge, EuiFlexItem } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiHealth, + EuiNotificationBadge, + EuiFlexItem, + useEuiTheme, +} from '@elastic/eui'; import React, { memo } from 'react'; import { @@ -31,9 +37,10 @@ export const AgentStatusBadges: React.FC<{ const AgentStatusBadge: React.FC<{ status: SimplifiedAgentStatus; count: number }> = memo( ({ status, count }) => { + const { euiTheme } = useEuiTheme(); return ( <> - <EuiHealth color={getColorForAgentStatus(status)}> + <EuiHealth color={getColorForAgentStatus(status, euiTheme)}> <EuiFlexGroup alignItems="center" gutterSize="s"> <EuiFlexItem grow={false}>{getLabelForAgentStatus(status)}</EuiFlexItem> <EuiFlexItem grow={false}> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.tsx index f4fc01204267..429702e6b7dc 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/status_bar.tsx @@ -6,7 +6,7 @@ */ import styled from 'styled-components'; -import { EuiColorPaletteDisplay, EuiSpacer } from '@elastic/eui'; +import { EuiColorPaletteDisplay, EuiSpacer, useEuiTheme } from '@elastic/eui'; import React, { useMemo } from 'react'; import { AGENT_STATUSES, getColorForAgentStatus } from '../../services/agent_status'; @@ -25,16 +25,17 @@ const StyledEuiColorPaletteDisplay = styled(EuiColorPaletteDisplay)` export const AgentStatusBar: React.FC<{ agentStatus: { [k in SimplifiedAgentStatus]: number }; }> = ({ agentStatus }) => { + const { euiTheme } = useEuiTheme(); const palette = useMemo(() => { return AGENT_STATUSES.reduce((acc, status) => { const previousStop = acc.length > 0 ? acc[acc.length - 1].stop : 0; acc.push({ stop: previousStop + (agentStatus[status] || 0), - color: getColorForAgentStatus(status), + color: getColorForAgentStatus(status, euiTheme), }); return acc; }, [] as Array<{ stop: number; color: string }>); - }, [agentStatus]); + }, [agentStatus, euiTheme]); const hasNoAgent = palette[palette.length - 1].stop === 0; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/agent_status.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/agent_status.tsx index b69da0810575..6b12331d7034 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/agent_status.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/agent_status.tsx @@ -5,24 +5,11 @@ * 2.0. */ -import { euiPaletteColorBlindBehindText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { euiLightVars } from '@kbn/ui-theme'; -import type { SimplifiedAgentStatus } from '../../../types'; +import type { EuiThemeComputed } from '@elastic/eui-theme-common'; -const visColors = euiPaletteColorBlindBehindText(); -const colorToHexMap = { - // using variables as mentioned here https://elastic.github.io/eui/#/guidelines/getting-started - default: euiLightVars.euiColorLightShade, - primary: visColors[1], - success: visColors[0], - accent: visColors[2], - warning: visColors[5], - danger: visColors[9], - inactive: euiLightVars.euiColorDarkShade, - lightest: euiLightVars.euiColorDisabled, -}; +import type { SimplifiedAgentStatus } from '../../../types'; export const AGENT_STATUSES: SimplifiedAgentStatus[] = [ 'healthy', @@ -33,20 +20,23 @@ export const AGENT_STATUSES: SimplifiedAgentStatus[] = [ 'unenrolled', ]; -export function getColorForAgentStatus(agentStatus: SimplifiedAgentStatus): string { +export function getColorForAgentStatus( + agentStatus: SimplifiedAgentStatus, + euiTheme: EuiThemeComputed<{}> +): string { switch (agentStatus) { case 'healthy': - return colorToHexMap.success; + return euiTheme.colors.backgroundFilledSuccess; case 'offline': - return colorToHexMap.default; + return euiTheme.colors.lightShade; case 'inactive': - return colorToHexMap.inactive; + return euiTheme.colors.darkShade; case 'unhealthy': - return colorToHexMap.warning; + return euiTheme.colors.backgroundFilledWarning; case 'updating': - return colorToHexMap.primary; + return euiTheme.colors.backgroundFilledPrimary; case 'unenrolled': - return colorToHexMap.lightest; + return euiTheme.colors.backgroundBaseDisabled; default: throw new Error(`Unsupported Agent status ${agentStatus}`); } diff --git a/x-pack/plugins/fleet/public/applications/integrations/components/header/header.tsx b/x-pack/plugins/fleet/public/applications/integrations/components/header/header.tsx index 30fc0f0d4965..f9a8ad22cddc 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/components/header/header.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/components/header/header.tsx @@ -34,7 +34,7 @@ export const IntegrationsHeader = ({ const { euiTheme } = useEuiTheme(); const readOnlyBtnClass = React.useMemo(() => { return css` - color: ${euiTheme.colors.text}; + color: ${euiTheme.colors.textParagraph}; `; }, [euiTheme]); const isReadOnly = useIsReadOnly(); diff --git a/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/index.tsx index 0fbf8d278fab..da08a8ea0d74 100644 --- a/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/index.tsx @@ -185,6 +185,7 @@ export const AgentlessEnrollmentFlyout = ({ <AgentlessStepConfirmData agent={agentData} packagePolicy={packagePolicy} + policyTemplates={packageInfoData?.item.policy_templates} setConfirmDataStatus={setConfirmDataStatus} /> ) : ( diff --git a/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/next_steps.tsx b/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/next_steps.tsx new file mode 100644 index 000000000000..e068dc5311ea --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/next_steps.tsx @@ -0,0 +1,150 @@ +/* + * 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, useMemo } from 'react'; +import { + EuiSpacer, + EuiFlexItem, + EuiCard, + EuiFlexGroup, + EuiButton, + EuiHorizontalRule, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { useStartServices } from '../../hooks'; +import type { PackagePolicy, RegistryPolicyTemplate } from '../../types'; +import { ELASTICSEARCH_PLUGIN_ID } from '../../../common/constants/plugin'; + +export const NextSteps = ({ + packagePolicy, + policyTemplates, +}: { + packagePolicy: PackagePolicy; + policyTemplates?: RegistryPolicyTemplate[]; +}) => { + const { application } = useStartServices(); + + const configurationLinks = useMemo(() => { + if (policyTemplates) { + return policyTemplates + ?.filter( + (template) => template?.configuration_links && template.configuration_links.length > 0 + ) + .flatMap((template) => template.configuration_links); + } + return []; + }, [policyTemplates]); + + const parseKbnLink = (url: string) => { + // matching strings with format kbn:/app/appId/path/optionalsubpath + const matches = url.match(/kbn:\/app\/(\w*)\/(\w*\/*)*/); + if (matches && matches.length > 0) { + const appId = matches[1]; + const path = matches[2]; + return { appId, path }; + } + return undefined; + }; + + const isExternal = (url: string) => url.startsWith('http') || url.startsWith('https'); + const onClickLink = useCallback( + (url?: string) => { + if (!url) return undefined; + + if (isExternal(url)) { + application.navigateToUrl(`${url}`); + } else if (url.startsWith('kbn:/')) { + const parsedLink = parseKbnLink(url); + if (parsedLink) { + const { appId, path } = parsedLink; + application.navigateToApp(appId, { + path, + }); + } + } + }, + [application] + ); + + const nextStepsCards = configurationLinks + .filter((link) => link?.type === 'next_step') + .map((link, index) => { + return ( + <EuiFlexItem key={index}> + <EuiCard + data-test-subj={`agentlessStepConfirmData.connectorCard.${link?.title}`} + title={`${link?.title}`} + description={`${link?.content}`} + onClick={() => onClickLink(link?.url)} + /> + </EuiFlexItem> + ); + }); + + const connectorCards = packagePolicy.inputs + .filter((input) => !!input?.vars?.connector_id.value || !!input?.vars?.connector_name.value) + .map((input, index) => { + return ( + <EuiFlexItem key={index}> + <EuiCard + data-test-subj={`agentlessStepConfirmData.connectorCard.${input?.vars?.connector_name.value}`} + title={`${input?.vars?.connector_name.value}`} + description={i18n.translate( + 'xpack.fleet.agentlessStepConfirmData.connectorCard.description', + { + defaultMessage: 'Configure Connector', + } + )} + onClick={() => { + application.navigateToApp(ELASTICSEARCH_PLUGIN_ID, { + path: input?.vars?.connector_id.value + ? `content/connectors/${input?.vars?.connector_id.value}` + : `content/connectors`, + }); + }} + /> + </EuiFlexItem> + ); + }); + + const actionButtons = configurationLinks + .filter((link) => !!link && link?.type === 'action') + .map((link, index) => { + return ( + <EuiFlexItem key={index} grow={false}> + <EuiButton + data-test-subj={`agentlessStepConfirmData.connectorCard.${link?.title}`} + iconType="link" + onClick={() => onClickLink(link?.url)} + > + {link?.title} + </EuiButton> + </EuiFlexItem> + ); + }); + + return ( + <> + <EuiSpacer size="m" /> + {nextStepsCards.length > 0 && ( + <EuiFlexGroup alignItems="center" direction="row" wrap={true}> + {nextStepsCards} + {connectorCards} + </EuiFlexGroup> + )} + <EuiSpacer size="m" /> + <EuiHorizontalRule /> + {actionButtons.length > 0 && ( + <EuiFlexGroup alignItems="center" direction="row" wrap={true}> + {actionButtons} + </EuiFlexGroup> + )} + </> + ); +}; diff --git a/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/step_confirm_data.tsx b/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/step_confirm_data.tsx index 34e69d8ef839..0775716eefd1 100644 --- a/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/step_confirm_data.tsx +++ b/x-pack/plugins/fleet/public/components/agentless_enrollment_flyout/step_confirm_data.tsx @@ -12,20 +12,24 @@ import type { EuiStepStatus } from '@elastic/eui'; import { EuiText, EuiLink, EuiSpacer, EuiCallOut } from '@elastic/eui'; import { useStartServices } from '../../hooks'; -import type { Agent, PackagePolicy } from '../../types'; +import type { Agent, PackagePolicy, RegistryPolicyTemplate } from '../../types'; import { usePollingIncomingData, POLLING_TIMEOUT_MS, } from '../agent_enrollment_flyout/use_get_agent_incoming_data'; +import { NextSteps } from './next_steps'; + export const AgentlessStepConfirmData = ({ agent, packagePolicy, setConfirmDataStatus, + policyTemplates, }: { agent: Agent; packagePolicy: PackagePolicy; setConfirmDataStatus: (status: EuiStepStatus) => void; + policyTemplates?: RegistryPolicyTemplate[]; }) => { const { docLinks } = useStartServices(); const [overallState, setOverallState] = useState<'pending' | 'success' | 'failure'>('pending'); @@ -53,13 +57,17 @@ export const AgentlessStepConfirmData = ({ if (overallState === 'success') { return ( - <EuiCallOut - color="success" - title={i18n.translate('xpack.fleet.agentlessEnrollmentFlyout.confirmData.successText', { - defaultMessage: 'Incoming data received from agentless integration', - })} - iconType="check" - /> + <> + <EuiCallOut + color="success" + title={i18n.translate('xpack.fleet.agentlessEnrollmentFlyout.confirmData.successText', { + defaultMessage: 'Incoming data received from agentless integration', + })} + iconType="check" + /> + <EuiSpacer size="m" /> + <NextSteps packagePolicy={packagePolicy} policyTemplates={policyTemplates} /> + </> ); } else if (overallState === 'failure') { return ( diff --git a/x-pack/plugins/fleet/public/components/danger_eui_context_menu_item.tsx b/x-pack/plugins/fleet/public/components/danger_eui_context_menu_item.tsx index 87fb523121cf..b7983ac255fb 100644 --- a/x-pack/plugins/fleet/public/components/danger_eui_context_menu_item.tsx +++ b/x-pack/plugins/fleet/public/components/danger_eui_context_menu_item.tsx @@ -5,9 +5,19 @@ * 2.0. */ -import styled from 'styled-components'; -import { EuiContextMenuItem } from '@elastic/eui'; +import React from 'react'; +import { css } from '@emotion/react'; +import type { EuiContextMenuItemProps } from '@elastic/eui'; +import { EuiContextMenuItem, useEuiTheme } from '@elastic/eui'; -export const DangerEuiContextMenuItem = styled(EuiContextMenuItem)` - color: ${(props) => props.theme.eui.euiColorDangerText}; -`; +export const DangerEuiContextMenuItem = (props: EuiContextMenuItemProps) => { + const theme = useEuiTheme(); + return ( + <EuiContextMenuItem + {...props} + css={css` + color: ${theme.euiTheme.colors.textDanger}; + `} + /> + ); +}; diff --git a/x-pack/plugins/fleet/server/constants/fleet_es_assets.ts b/x-pack/plugins/fleet/server/constants/fleet_es_assets.ts index 621adc5b3b81..384a77afd112 100644 --- a/x-pack/plugins/fleet/server/constants/fleet_es_assets.ts +++ b/x-pack/plugins/fleet/server/constants/fleet_es_assets.ts @@ -11,7 +11,7 @@ import { getESAssetMetadata } from '../services/epm/elasticsearch/meta'; const meta = getESAssetMetadata(); -export const FLEET_INSTALL_FORMAT_VERSION = '1.3.0'; +export const FLEET_INSTALL_FORMAT_VERSION = '1.4.0'; export const FLEET_AGENT_POLICIES_SCHEMA_VERSION = '1.1.1'; diff --git a/x-pack/plugins/fleet/server/services/agents/status.ts b/x-pack/plugins/fleet/server/services/agents/status.ts index e960a7b955fe..384c7b125255 100644 --- a/x-pack/plugins/fleet/server/services/agents/status.ts +++ b/x-pack/plugins/fleet/server/services/agents/status.ts @@ -271,7 +271,7 @@ export async function getIncomingDataByAgentsId({ } catch (error) { logger.debug(`Error getting incoming data for agents: ${error}`); throw new FleetError( - `Unable to retrive incoming data for agents due to error: ${error.message}` + `Unable to retrieve incoming data for agents due to error: ${error.message}` ); } } diff --git a/x-pack/plugins/fleet/server/services/backfill_agentless.test.ts b/x-pack/plugins/fleet/server/services/backfill_agentless.test.ts new file mode 100644 index 000000000000..b0fd41fe8329 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/backfill_agentless.test.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 { backfillPackagePolicySupportsAgentless } from './backfill_agentless'; +import { packagePolicyService } from './package_policy'; + +jest.mock('.', () => ({ + appContextService: { + getLogger: () => ({ + debug: jest.fn(), + }), + getInternalUserSOClientForSpaceId: jest.fn(), + getInternalUserSOClientWithoutSpaceExtension: () => ({ + find: jest.fn().mockImplementation((options) => { + if (options.type === 'ingest-agent-policies') { + return { + saved_objects: [{ id: 'agent_policy_1' }, { id: 'agent_policy_2' }], + }; + } else { + return { + saved_objects: [ + { + id: 'package_policy_1', + attributes: { + inputs: [], + policy_ids: ['agent_policy_1'], + supports_agentless: false, + }, + }, + ], + }; + } + }), + }), + }, +})); + +jest.mock('./package_policy', () => ({ + packagePolicyService: { + update: jest.fn(), + }, + getPackagePolicySavedObjectType: jest.fn().mockResolvedValue('ingest-package-policies'), +})); + +describe('backfill agentless package policies', () => { + it('should backfill package policies missing supports_agentless', async () => { + await backfillPackagePolicySupportsAgentless(undefined as any); + + expect(packagePolicyService.update).toHaveBeenCalledWith( + undefined, + undefined, + 'package_policy_1', + { + enabled: undefined, + inputs: [], + name: undefined, + policy_ids: ['agent_policy_1'], + supports_agentless: true, + } + ); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/backfill_agentless.ts b/x-pack/plugins/fleet/server/services/backfill_agentless.ts new file mode 100644 index 000000000000..b61265bd53c3 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/backfill_agentless.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ElasticsearchClient } from '@kbn/core/server'; + +import pMap from 'p-map'; + +import { MAX_CONCURRENT_AGENT_POLICIES_OPERATIONS, SO_SEARCH_LIMIT } from '../constants'; + +import type { AgentPolicySOAttributes, PackagePolicy, PackagePolicySOAttributes } from '../types'; + +import { getAgentPolicySavedObjectType } from './agent_policy'; + +import { appContextService } from '.'; +import { getPackagePolicySavedObjectType, packagePolicyService } from './package_policy'; +import { mapPackagePolicySavedObjectToPackagePolicy } from './package_policies'; + +export async function backfillPackagePolicySupportsAgentless(esClient: ElasticsearchClient) { + const apSavedObjectType = await getAgentPolicySavedObjectType(); + const internalSoClientWithoutSpaceExtension = + appContextService.getInternalUserSOClientWithoutSpaceExtension(); + const findRes = await internalSoClientWithoutSpaceExtension.find<AgentPolicySOAttributes>({ + type: apSavedObjectType, + page: 1, + perPage: SO_SEARCH_LIMIT, + filter: `${apSavedObjectType}.attributes.supports_agentless:true`, + fields: [`id`], + namespaces: ['*'], + }); + + const agentPolicyIds = findRes.saved_objects.map((so) => so.id); + + if (agentPolicyIds.length === 0) { + return; + } + + const savedObjectType = await getPackagePolicySavedObjectType(); + const packagePoliciesToUpdate = ( + await appContextService + .getInternalUserSOClientWithoutSpaceExtension() + .find<PackagePolicySOAttributes>({ + type: savedObjectType, + fields: [ + 'name', + 'policy_ids', + 'supports_agentless', + 'enabled', + 'policy_ids', + 'inputs', + 'package', + ], + filter: `${savedObjectType}.attributes.package.name:cloud_security_posture AND (NOT ${savedObjectType}.attributes.supports_agentless:true) AND ${savedObjectType}.attributes.policy_ids:(${agentPolicyIds.join( + ' OR ' + )})`, + perPage: SO_SEARCH_LIMIT, + namespaces: ['*'], + }) + ).saved_objects.map((so) => mapPackagePolicySavedObjectToPackagePolicy(so, so.namespaces)); + + appContextService + .getLogger() + .debug( + `Backfilling supports_agentless on package policies: ${packagePoliciesToUpdate.map( + (policy) => policy.id + )}` + ); + + if (packagePoliciesToUpdate.length > 0) { + const getPackagePolicyUpdate = (packagePolicy: PackagePolicy) => ({ + name: packagePolicy.name, + enabled: packagePolicy.enabled, + policy_ids: packagePolicy.policy_ids, + inputs: packagePolicy.inputs, + supports_agentless: true, + }); + + await pMap( + packagePoliciesToUpdate, + (packagePolicy) => { + const soClient = appContextService.getInternalUserSOClientForSpaceId( + packagePolicy.spaceIds?.[0] + ); + return packagePolicyService.update( + soClient, + esClient, + packagePolicy.id, + getPackagePolicyUpdate(packagePolicy) + ); + }, + { + concurrency: MAX_CONCURRENT_AGENT_POLICIES_OPERATIONS, + } + ); + } +} diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_system_object.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_system_object.test.ts index 8d80c236aefb..c4ae211c58fc 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_system_object.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_state_machine/steps/step_save_system_object.test.ts @@ -13,14 +13,12 @@ import { } from '@kbn/core/server/mocks'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; +import { FLEET_INSTALL_FORMAT_VERSION } from '../../../../../constants'; import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../../../common/constants'; - import { appContextService } from '../../../../app_context'; import { createAppContextStartContractMock } from '../../../../../mocks'; - import { auditLoggingService } from '../../../../audit_logging'; import { packagePolicyService } from '../../../../package_policy'; - import { createArchiveIteratorFromMap } from '../../../archive/archive_iterator'; import { stepSaveSystemObject } from './step_save_system_object'; @@ -94,7 +92,7 @@ describe('updateLatestExecutedState', () => { 'epm-packages', 'test-integration', { - install_format_schema_version: '1.3.0', + install_format_schema_version: FLEET_INSTALL_FORMAT_VERSION, install_status: 'installed', install_version: '1.0.0', latest_install_failed_attempts: [], @@ -161,7 +159,7 @@ describe('updateLatestExecutedState', () => { 'epm-packages', 'test-integration', { - install_format_schema_version: '1.3.0', + install_format_schema_version: FLEET_INSTALL_FORMAT_VERSION, install_status: 'installed', install_version: '1.0.0', latest_install_failed_attempts: [], diff --git a/x-pack/plugins/fleet/server/services/setup.test.ts b/x-pack/plugins/fleet/server/services/setup.test.ts index 8add6942e9da..d145f264ec3e 100644 --- a/x-pack/plugins/fleet/server/services/setup.test.ts +++ b/x-pack/plugins/fleet/server/services/setup.test.ts @@ -33,6 +33,7 @@ jest.mock('./epm/elasticsearch/template/install', () => { ...jest.requireActual('./epm/elasticsearch/template/install'), }; }); +jest.mock('./backfill_agentless'); const mockedMethodThrowsError = (mockFn: jest.Mock) => mockFn.mockImplementation(() => { diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index 24d655a4cae1..e17d8222cfd0 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -62,6 +62,7 @@ import { ensureDeleteUnenrolledAgentsSetting, getPreconfiguredDeleteUnenrolledAgentsSettingFromConfig, } from './preconfiguration/delete_unenrolled_agent_setting'; +import { backfillPackagePolicySupportsAgentless } from './backfill_agentless'; export interface SetupStatus { isInitialized: boolean; @@ -305,6 +306,9 @@ async function createSetupSideEffects( await ensureAgentPoliciesFleetServerKeysAndPolicies({ soClient, esClient, logger }); stepSpan?.end(); + logger.debug('Backfilling package policy supports_agentless field'); + await backfillPackagePolicySupportsAgentless(esClient); + const nonFatalErrors = [ ...preconfiguredPackagesNonFatalErrors, ...(messageSigningServiceNonFatalError ? [messageSigningServiceNonFatalError] : []), diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/aliases_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/aliases_details.tsx index 934c7c31cdbf..ae644b78e284 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/aliases_details.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/aliases_details.tsx @@ -7,7 +7,6 @@ import React, { FunctionComponent, useState } from 'react'; import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; import { EuiBadge, @@ -23,6 +22,7 @@ import { EuiText, EuiTextColor, EuiTitle, + useEuiFontSize, } from '@elastic/eui'; import { Index } from '../../../../../../../common'; @@ -31,6 +31,7 @@ import { OverviewCard } from './overview_card'; const MAX_VISIBLE_ALIASES = 3; export const AliasesDetails: FunctionComponent<{ aliases: Index['aliases'] }> = ({ aliases }) => { + const largeFontSize = useEuiFontSize('l').fontSize; const [isShowingAliases, setIsShowingAliases] = useState<boolean>(false); if (!Array.isArray(aliases)) { return null; @@ -58,7 +59,7 @@ export const AliasesDetails: FunctionComponent<{ aliases: Index['aliases'] }> = <EuiFlexItem grow={false}> <EuiText css={css` - font-size: ${euiThemeVars.euiFontSizeL}; + font-size: ${largeFontSize}; `} > {aliases.length} diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/data_stream_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/data_stream_details.tsx index a9b2768e140b..68d61d086a5f 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/data_stream_details.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/data_stream_details.tsx @@ -7,9 +7,16 @@ import React, { FunctionComponent, ReactNode } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText, EuiTextColor } from '@elastic/eui'; +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiText, + EuiTextColor, + useEuiFontSize, +} from '@elastic/eui'; import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; import { SectionLoading } from '@kbn/es-ui-shared-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -22,6 +29,7 @@ import { OverviewCard } from './overview_card'; export const DataStreamDetails: FunctionComponent<{ dataStreamName: string }> = ({ dataStreamName, }) => { + const largeFontSize = useEuiFontSize('l').fontSize; const { error, data: dataStream, isLoading, resendRequest } = useLoadDataStream(dataStreamName); const { core: { getUrlForApp }, @@ -32,7 +40,7 @@ export const DataStreamDetails: FunctionComponent<{ dataStreamName: string }> = <EuiFlexItem grow={false}> <EuiText css={css` - font-size: ${euiThemeVars.euiFontSizeL}; + font-size: ${largeFontSize}; `} > {dataStream?.generation} diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/overview_card.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/overview_card.tsx index 4aa6c804dc40..07e4b45c3a69 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/overview_card.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/overview_card.tsx @@ -7,8 +7,14 @@ import React, { FunctionComponent, ReactNode } from 'react'; import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiSplitPanel, EuiTitle } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiSplitPanel, + EuiTitle, + useEuiTheme, +} from '@elastic/eui'; interface Props { title: string; @@ -29,6 +35,7 @@ export const OverviewCard: FunctionComponent<Props> = ({ footer: { left: footerLeft, right: footerRight } = {}, 'data-test-subj': dataTestSubj, }) => { + const { euiTheme } = useEuiTheme(); return ( <EuiFlexItem> <EuiSplitPanel.Outer grow hasBorder={true} data-test-subj={dataTestSubj}> @@ -43,7 +50,7 @@ export const OverviewCard: FunctionComponent<Props> = ({ wrap={true} alignItems="center" css={css` - min-height: ${euiThemeVars.euiButtonHeightSmall}; + min-height: ${euiTheme.size.xl}; `} > <EuiFlexItem grow={false}>{contentLeft}</EuiFlexItem> diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx index 40294d76b269..617fb0f975cb 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/size_doc_count_details.tsx @@ -7,9 +7,15 @@ import React, { FunctionComponent } from 'react'; import { css } from '@emotion/react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText, EuiTextColor } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiText, + EuiTextColor, + useEuiFontSize, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { euiThemeVars } from '@kbn/ui-theme'; import type { Index } from '../../../../../../../common'; import { useAppContext } from '../../../../../app_context'; import { OverviewCard } from './overview_card'; @@ -18,6 +24,7 @@ export const SizeDocCountDetails: FunctionComponent<{ size: Index['size']; documents: Index['documents']; }> = ({ size, documents }) => { + const largeFontSize = useEuiFontSize('l').fontSize; const { config } = useAppContext(); if (!config.enableSizeAndDocCount) { return null; @@ -34,7 +41,7 @@ export const SizeDocCountDetails: FunctionComponent<{ <EuiFlexItem grow={false}> <EuiText css={css` - font-size: ${euiThemeVars.euiFontSizeL}; + font-size: ${largeFontSize}; `} > {size} diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/status_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/status_details.tsx index 8895daceceaf..82531bfda797 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/status_details.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/status_details.tsx @@ -15,9 +15,9 @@ import { EuiText, EuiTextColor, EuiBadgeProps, + useEuiFontSize, } from '@elastic/eui'; import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; import { useAppContext } from '../../../../../app_context'; import { Index } from '../../../../../../../common'; @@ -54,6 +54,7 @@ export const StatusDetails: FunctionComponent<{ status: Index['status']; health: Index['health']; }> = ({ documents, documentsDeleted, status, health }) => { + const largeFontSize = useEuiFontSize('l').fontSize; const { config } = useAppContext(); if (!config.enableIndexStats || !health) { return null; @@ -72,7 +73,7 @@ export const StatusDetails: FunctionComponent<{ <EuiText color={status === 'close' ? 'danger' : 'success'} css={css` - font-size: ${euiThemeVars.euiFontSizeL}; + font-size: ${largeFontSize}; `} > {status === 'close' diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/storage_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/storage_details.tsx index 22b47815ce95..1d90b86d4b4b 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/storage_details.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_overview/storage_details.tsx @@ -8,8 +8,14 @@ import React, { FunctionComponent } from 'react'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; -import { euiThemeVars } from '@kbn/ui-theme'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText, EuiTextColor } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiText, + EuiTextColor, + useEuiFontSize, +} from '@elastic/eui'; import { useAppContext } from '../../../../../app_context'; import { Index } from '../../../../../../../common'; @@ -21,6 +27,7 @@ export const StorageDetails: FunctionComponent<{ primary: Index['primary']; replica: Index['replica']; }> = ({ primarySize, size, primary, replica }) => { + const largeFontSize = useEuiFontSize('l').fontSize; const { config } = useAppContext(); if (!config.enableIndexStats) { return null; @@ -37,7 +44,7 @@ export const StorageDetails: FunctionComponent<{ <EuiFlexItem grow={false}> <EuiText css={css` - font-size: ${euiThemeVars.euiFontSizeL}; + font-size: ${largeFontSize}; `} > {primarySize} @@ -57,7 +64,7 @@ export const StorageDetails: FunctionComponent<{ <EuiFlexItem grow={false}> <EuiText css={css` - font-size: ${euiThemeVars.euiFontSizeL}; + font-size: ${largeFontSize}; `} > {size} diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/semantic_text_banner.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/semantic_text_banner.tsx index b07886c4526f..2ebcde354bab 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/semantic_text_banner.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/semantic_text_banner.tsx @@ -57,10 +57,10 @@ export function SemanticTextBanner({ return isSemanticTextBannerDisplayable && isSemanticTextEnabled ? ( <> - <EuiPanel color="success" data-test-subj="indexDetailsMappingsSemanticTextBanner"> + <EuiPanel color="accentSecondary" data-test-subj="indexDetailsMappingsSemanticTextBanner"> <EuiFlexGroup> <EuiFlexItem> - <EuiText size="m" color="success"> + <EuiText size="m" color="primary"> {isPlatinumLicense ? platinumLicenseMessage : defaultLicenseMessage} </EuiText> </EuiFlexItem> diff --git a/x-pack/plugins/index_management/tsconfig.json b/x-pack/plugins/index_management/tsconfig.json index eac67aa62097..48b40c937615 100644 --- a/x-pack/plugins/index_management/tsconfig.json +++ b/x-pack/plugins/index_management/tsconfig.json @@ -40,7 +40,6 @@ "@kbn/core-http-browser", "@kbn/search-api-panels", "@kbn/cloud-plugin", - "@kbn/ui-theme", "@kbn/code-editor", "@kbn/monaco", "@kbn/console-plugin", diff --git a/x-pack/plugins/lens/public/app_plugin/share_action.ts b/x-pack/plugins/lens/public/app_plugin/share_action.ts index dbb5d9d61eda..e9e0c73cd5d6 100644 --- a/x-pack/plugins/lens/public/app_plugin/share_action.ts +++ b/x-pack/plugins/lens/public/app_plugin/share_action.ts @@ -31,7 +31,7 @@ interface ShareableConfiguration export const DEFAULT_LENS_LAYOUT_DIMENSIONS = { width: 1793, // this is a magic number from the reporting tool implementation - // see: x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts#L146 + // see: x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/driver_factory/index.ts#L146 height: 1086, }; diff --git a/x-pack/plugins/lists/README.md b/x-pack/plugins/lists/README.md deleted file mode 100644 index 02be75730341..000000000000 --- a/x-pack/plugins/lists/README.md +++ /dev/null @@ -1,238 +0,0 @@ -README.md for developers working on the backend lists on how to get started -using the CURL scripts in the scripts folder. - -The scripts rely on CURL and jq: - -- [CURL](https://curl.haxx.se) -- [jq](https://stedolan.github.io/jq/) - -Install curl and jq (mac instructions) - -```sh -brew update -brew install curl -brew install jq -``` - -Open `$HOME/.zshrc` or `${HOME}.bashrc` depending on your SHELL output from `echo $SHELL` -and add these environment variables: - -```sh -export ELASTICSEARCH_USERNAME=${user} -export ELASTICSEARCH_PASSWORD=${password} -export ELASTICSEARCH_URL=https://${ip}:9200 -export KIBANA_URL=http://localhost:5601 -export TASK_MANAGER_INDEX=.kibana-task-manager-${your user id} -export KIBANA_INDEX=.kibana-${your user id} -``` - -source `$HOME/.zshrc` or `${HOME}.bashrc` to ensure variables are set: - -```sh -source ~/.zshrc -``` - -Open your `kibana.dev.yml` file and add these lines with your name: - -```sh -xpack.lists.listIndex: '.lists-your-name' -xpack.lists.listItemIndex: '.items-your-name' -``` - -Restart Kibana and ensure that you are using `--no-base-path` as changing the base path is a feature but will -get in the way of the CURL scripts written as is. - -Go to the scripts folder `cd kibana/x-pack/plugins/lists/server/scripts` and run: - -```sh -./hard_reset.sh -./post_list.sh -``` - -which will: - -- Delete any existing lists you have -- Delete any existing list items you have -- Delete any existing exception lists you have -- Delete any existing exception list items you have -- Delete any existing mapping, policies, and templates, you might have previously had. -- Add the latest list and list item index and its mappings using your settings from `kibana.dev.yml` environment variable of `xpack.lists.listIndex` and `xpack.lists.listItemIndex`. -- Posts the sample list from `./lists/new/ip_list.json` - -Now you can run - -```sh -./post_list.sh -``` - -You should see the new list created like so: - -```sh -{ - "id": "ip_list", - "created_at": "2020-05-28T19:15:22.344Z", - "created_by": "yo", - "description": "This list describes bad internet ip", - "name": "Simple list with an ip", - "tie_breaker_id": "c57efbc4-4977-4a32-995f-cfd296bed521", - "type": "ip", - "updated_at": "2020-05-28T19:15:22.344Z", - "updated_by": "yo" -} -``` - -You can add a list item like so: - -```sh - ./post_list_item.sh -``` - -You should see the new list item created and attached to the above list like so: - -```sh -{ - "id": "hand_inserted_item_id", - "type": "ip", - "value": "127.0.0.1", - "created_at": "2020-05-28T19:15:49.790Z", - "created_by": "yo", - "list_id": "ip_list", - "tie_breaker_id": "a881bf2e-1e17-4592-bba8-d567cb07d234", - "updated_at": "2020-05-28T19:15:49.790Z", - "updated_by": "yo" -} -``` - -If you want to post an exception list it would be like so: - -```sh -./post_exception_list.sh -``` - -You should see the new exception list created like so: - -```sh -{ - "created_at": "2020-05-28T19:16:31.052Z", - "created_by": "yo", - "description": "This is a sample endpoint type exception", - "id": "bcb94680-a117-11ea-ad9d-c71f4820e65b", - "list_id": "endpoint_list", - "name": "Sample Endpoint Exception List", - "namespace_type": "single", - "tags": [ - "user added string for a tag", - "malware" - ], - "tie_breaker_id": "86e08c8c-c970-4b08-a6e2-cdba7bb4e023", - "type": "endpoint", - "updated_at": "2020-05-28T19:16:31.080Z", - "updated_by": "yo" -} -``` - -And you can attach exception list items like so: - -```ts -{ - "comments": [], - "created_at": "2020-05-28T19:17:21.099Z", - "created_by": "yo", - "description": "This is a sample endpoint type exception", - "entries": [ - { - "field": "actingProcess.file.signer", - "operator": "included", - "type": "match", - "value": "Elastic, N.V." - }, - { - "field": "event.category", - "operator": "included", - "type": "match_any", - "value": [ - "process", - "malware" - ] - } - ], - "id": "da8d3b30-a117-11ea-ad9d-c71f4820e65b", - "item_id": "endpoint_list_item", - "list_id": "endpoint_list", - "name": "Sample Endpoint Exception List", - "namespace_type": "single", - "os_types": ["linux"], - "tags": [ - "user added string for a tag", - "malware" - ], - "tie_breaker_id": "21f84703-9476-4af8-a212-aad31e18dcb9", - "type": "simple", - "updated_at": "2020-05-28T19:17:21.123Z", - "updated_by": "yo" -} -``` - -You can then do find for each one like so: - -```sh -./find_lists.sh -``` - -```sh -{ - "cursor": "WzIwLFsiYzU3ZWZiYzQtNDk3Ny00YTMyLTk5NWYtY2ZkMjk2YmVkNTIxIl1d", - "data": [ - { - "id": "ip_list", - "created_at": "2020-05-28T19:15:22.344Z", - "created_by": "yo", - "description": "This list describes bad internet ip", - "name": "Simple list with an ip", - "tie_breaker_id": "c57efbc4-4977-4a32-995f-cfd296bed521", - "type": "ip", - "updated_at": "2020-05-28T19:15:22.344Z", - "updated_by": "yo" - } - ], - "page": 1, - "per_page": 20, - "total": 1 -} -``` - -or for finding exception lists: - -```sh -./find_exception_lists.sh -``` - -```sh -{ - "data": [ - { - "created_at": "2020-05-28T19:16:31.052Z", - "created_by": "yo", - "description": "This is a sample endpoint type exception", - "id": "bcb94680-a117-11ea-ad9d-c71f4820e65b", - "list_id": "endpoint_list", - "name": "Sample Endpoint Exception List", - "namespace_type": "single", - "os_types": ["linux"], - "tags": [ - "user added string for a tag", - "malware" - ], - "tie_breaker_id": "86e08c8c-c970-4b08-a6e2-cdba7bb4e023", - "type": "endpoint", - "updated_at": "2020-05-28T19:16:31.080Z", - "updated_by": "yo" - } - ], - "page": 1, - "per_page": 20, - "total": 1 -} -``` - -See the full scripts folder for all the capabilities. diff --git a/x-pack/plugins/lists/jest.config.js b/x-pack/plugins/lists/jest.config.js deleted file mode 100644 index cb9832920183..000000000000 --- a/x-pack/plugins/lists/jest.config.js +++ /dev/null @@ -1,15 +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. - */ - -module.exports = { - collectCoverageFrom: ['<rootDir>/x-pack/plugins/lists/{common,public,server}/**/*.{ts,tsx}'], - coverageDirectory: '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/lists', - coverageReporters: ['text', 'html'], - preset: '@kbn/test', - rootDir: '../../..', - roots: ['<rootDir>/x-pack/plugins/lists'], -}; diff --git a/x-pack/plugins/lists/tsconfig.json b/x-pack/plugins/lists/tsconfig.json deleted file mode 100644 index 61229d39bdbd..000000000000 --- a/x-pack/plugins/lists/tsconfig.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types" - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*", - // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 - "server/**/*.json" - ], - "kbn_references": [ - "@kbn/core", - "@kbn/spaces-plugin", - "@kbn/security-plugin", - "@kbn/unified-search-plugin", - "@kbn/securitysolution-io-ts-list-types", - "@kbn/securitysolution-list-constants", - "@kbn/securitysolution-list-hooks", - "@kbn/securitysolution-list-api", - "@kbn/securitysolution-lists-common", - "@kbn/securitysolution-exceptions-common", - "@kbn/securitysolution-endpoint-exceptions-common", - "@kbn/kibana-react-plugin", - "@kbn/i18n", - "@kbn/data-plugin", - "@kbn/securitysolution-list-utils", - "@kbn/securitysolution-utils", - "@kbn/es-query", - "@kbn/i18n-react", - "@kbn/securitysolution-autocomplete", - "@kbn/config-schema", - "@kbn/securitysolution-io-ts-utils", - "@kbn/core-http-server", - "@kbn/securitysolution-es-utils", - "@kbn/securitysolution-io-ts-types", - "@kbn/std", - "@kbn/utils", - "@kbn/logging-mocks", - "@kbn/utility-types", - "@kbn/core-elasticsearch-client-server-mocks", - "@kbn/core-saved-objects-server", - "@kbn/zod-helpers", - "@kbn/core-security-server", - "@kbn/core-http-server-mocks", - "@kbn/core-http-server-utils" - ], - "exclude": ["target/**/*"] -} diff --git a/x-pack/plugins/observability_solution/apm/kibana.jsonc b/x-pack/plugins/observability_solution/apm/kibana.jsonc index bcb0801fc639..0b8e8bd70a9a 100644 --- a/x-pack/plugins/observability_solution/apm/kibana.jsonc +++ b/x-pack/plugins/observability_solution/apm/kibana.jsonc @@ -39,6 +39,7 @@ "uiActions", "logsDataAccess", "savedSearch", + "entityManager" ], "optionalPlugins": [ "actions", diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/entities/entity_link/entity_link.test.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/entities/entity_link/entity_link.test.tsx index cdf6f23eb53d..4515c2cdd571 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/entities/entity_link/entity_link.test.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/entities/entity_link/entity_link.test.tsx @@ -173,7 +173,9 @@ describe('Entity link', () => { renderEntityLink({ isEntityCentricExperienceEnabled: true, serviceEntitySummaryMockReturnValue: { - serviceEntitySummary: { dataStreamTypes: ['metrics'] } as unknown as ServiceEntitySummary, + serviceEntitySummary: { + ['data_stream.type']: ['metrics'], + } as unknown as ServiceEntitySummary, serviceEntitySummaryStatus: FETCH_STATUS.SUCCESS, }, hasApmDataFetcherMockReturnValue: { @@ -200,7 +202,9 @@ describe('Entity link', () => { renderEntityLink({ isEntityCentricExperienceEnabled: true, serviceEntitySummaryMockReturnValue: { - serviceEntitySummary: { dataStreamTypes: ['metrics'] } as unknown as ServiceEntitySummary, + serviceEntitySummary: { + ['data_stream.type']: ['metrics'], + } as unknown as ServiceEntitySummary, serviceEntitySummaryStatus: FETCH_STATUS.SUCCESS, }, hasApmDataFetcherMockReturnValue: { diff --git a/x-pack/plugins/observability_solution/apm/public/hooks/use_entity_centric_experience_setting.tsx b/x-pack/plugins/observability_solution/apm/public/hooks/use_entity_centric_experience_setting.tsx index 32e68c40788f..e5290bb25dbb 100644 --- a/x-pack/plugins/observability_solution/apm/public/hooks/use_entity_centric_experience_setting.tsx +++ b/x-pack/plugins/observability_solution/apm/public/hooks/use_entity_centric_experience_setting.tsx @@ -13,7 +13,7 @@ export function useEntityCentricExperienceSetting() { const isEntityCentricExperienceEnabled = core.uiSettings.get<boolean>( entityCentricExperience, - true + false ); return { isEntityCentricExperienceEnabled }; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts deleted file mode 100644 index 6cedb09efa7c..000000000000 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts +++ /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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { - ENTITY_FIRST_SEEN, - ENTITY_LAST_SEEN, -} from '@kbn/observability-shared-plugin/common/field_names/elasticsearch'; -import type { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client'; -import { getEntityLatestServices } from './get_entity_latest_services'; -import type { EntityLatestServiceRaw } from './types'; - -export function entitiesRangeQuery(start?: number, end?: number): QueryDslQueryContainer[] { - if (!start || !end) { - return []; - } - - return [ - { - range: { - [ENTITY_LAST_SEEN]: { - gte: start, - }, - }, - }, - { - range: { - [ENTITY_FIRST_SEEN]: { - lte: end, - }, - }, - }, - ]; -} - -export async function getEntities({ - entitiesESClient, - start, - end, - environment, - kuery, - size, - serviceName, -}: { - entitiesESClient: EntitiesESClient; - start: number; - end: number; - environment: string; - kuery?: string; - size: number; - serviceName?: string; -}): Promise<EntityLatestServiceRaw[]> { - const entityLatestServices = await getEntityLatestServices({ - entitiesESClient, - start, - end, - environment, - kuery, - size, - serviceName, - }); - - return entityLatestServices; -} diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entity_latest_services.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entity_latest_services.ts deleted file mode 100644 index e08be75072b6..000000000000 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entity_latest_services.ts +++ /dev/null @@ -1,59 +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 { kqlQuery, termQuery } from '@kbn/observability-plugin/server'; -import { - ENTITY, - ENTITY_TYPE, - SOURCE_DATA_STREAM_TYPE, -} from '@kbn/observability-shared-plugin/common/field_names/elasticsearch'; -import { AGENT_NAME, SERVICE_ENVIRONMENT, SERVICE_NAME } from '../../../common/es_fields/apm'; -import { environmentQuery } from '../../../common/utils/environment_query'; -import { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client'; -import { entitiesRangeQuery } from './get_entities'; -import { EntityLatestServiceRaw, EntityType } from './types'; - -export async function getEntityLatestServices({ - entitiesESClient, - start, - end, - environment, - kuery, - size, - serviceName, -}: { - entitiesESClient: EntitiesESClient; - start?: number; - end?: number; - environment: string; - kuery?: string; - size: number; - serviceName?: string; -}): Promise<EntityLatestServiceRaw[]> { - const latestEntityServices = ( - await entitiesESClient.searchLatest<EntityLatestServiceRaw>(`get_entity_latest_services`, { - body: { - size, - track_total_hits: false, - _source: [AGENT_NAME, ENTITY, SOURCE_DATA_STREAM_TYPE, SERVICE_NAME, SERVICE_ENVIRONMENT], - query: { - bool: { - filter: [ - ...kqlQuery(kuery), - ...environmentQuery(environment, SERVICE_ENVIRONMENT), - ...entitiesRangeQuery(start, end), - ...termQuery(ENTITY_TYPE, EntityType.SERVICE), - ...termQuery(SERVICE_NAME, serviceName), - ], - }, - }, - }, - }) - ).hits.hits.map((hit) => hit._source); - - return latestEntityServices; -} diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/get_service_entities.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/get_service_entities.ts deleted file mode 100644 index 9e6bb34bceaf..000000000000 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/get_service_entities.ts +++ /dev/null @@ -1,61 +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 { errors } from '@elastic/elasticsearch'; -import { Logger } from '@kbn/core/server'; -import { WrappedElasticsearchClientError } from '@kbn/observability-plugin/server'; -import { EntitiesESClient } from '../../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client'; -import { withApmSpan } from '../../../utils/with_apm_span'; -import { getEntities } from '../get_entities'; -import { mergeEntities } from '../utils/merge_entities'; - -export const MAX_NUMBER_OF_SERVICES = 1_000; - -export async function getServiceEntities({ - entitiesESClient, - start, - end, - kuery, - environment, - logger, -}: { - entitiesESClient: EntitiesESClient; - start: number; - end: number; - kuery: string; - environment: string; - logger: Logger; -}) { - return withApmSpan('get_service_entities', async () => { - try { - const entities = await getEntities({ - entitiesESClient, - start, - end, - kuery, - environment, - size: MAX_NUMBER_OF_SERVICES, - }); - - return mergeEntities({ entities }); - } catch (error) { - // If the index does not exist, handle it gracefully - if ( - error instanceof WrappedElasticsearchClientError && - error.originalError instanceof errors.ResponseError - ) { - const type = error.originalError.body.error.type; - - if (type === 'index_not_found_exception') { - logger.error(`Entities index does not exist. Unable to fetch services.`); - return []; - } - } - - throw error; - } - }); -} diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/get_service_entity_summary.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/get_service_entity_summary.ts index 3ab3b907f5be..90a44a9b609e 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/get_service_entity_summary.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/get_service_entity_summary.ts @@ -5,28 +5,31 @@ * 2.0. */ -import type { EntitiesESClient } from '../../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client'; +import { SERVICE_NAME, AGENT_NAME, SERVICE_ENVIRONMENT } from '@kbn/apm-types'; +import { BUILT_IN_ENTITY_TYPES, DATA_STREAM_TYPE } from '@kbn/observability-shared-plugin/common'; +import moment from 'moment'; +import type { EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client'; import { withApmSpan } from '../../../utils/with_apm_span'; -import { getEntityLatestServices } from '../get_entity_latest_services'; import { mergeEntities } from '../utils/merge_entities'; -import { MAX_NUMBER_OF_SERVICES } from './get_service_entities'; interface Params { - entitiesESClient: EntitiesESClient; + entityManagerClient: EntityClient; serviceName: string; environment: string; } -export function getServiceEntitySummary({ entitiesESClient, environment, serviceName }: Params) { +export function getServiceEntitySummary({ entityManagerClient, environment, serviceName }: Params) { return withApmSpan('get_service_entity_summary', async () => { - const entityLatestServices = await getEntityLatestServices({ - entitiesESClient, - environment, - size: MAX_NUMBER_OF_SERVICES, - serviceName, + const serviceEntitySummary = await entityManagerClient.v2.searchEntities({ + start: moment().subtract(15, 'm').toISOString(), + end: moment().toISOString(), + type: BUILT_IN_ENTITY_TYPES.SERVICE_V2, + filters: [`${SERVICE_NAME}: "${serviceName}"`], + limit: 1, + metadata_fields: [DATA_STREAM_TYPE, AGENT_NAME, SERVICE_ENVIRONMENT], }); - const serviceEntity = mergeEntities({ entities: entityLatestServices }); + const serviceEntity = mergeEntities({ entities: serviceEntitySummary?.entities }); return serviceEntity[0]; }); } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts index ab89f9417ec8..e3d44645ad39 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts @@ -6,10 +6,8 @@ */ import * as t from 'io-ts'; import { environmentQuery } from '../../../../common/utils/environment_query'; -import { createEntitiesESClient } from '../../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client'; import { createApmServerRoute } from '../../apm_routes/create_apm_server_route'; import { environmentRt, kueryRt, rangeRt } from '../../default_api_types'; -import { getServiceEntities } from './get_service_entities'; import { getServiceEntitySummary } from './get_service_entity_summary'; const serviceEntitiesSummaryRoute = createApmServerRoute({ @@ -20,52 +18,20 @@ const serviceEntitiesSummaryRoute = createApmServerRoute({ }), security: { authz: { requiredPrivileges: ['apm'] } }, async handler(resources) { - const { context, params, request } = resources; - const coreContext = await context.core; - - const entitiesESClient = await createEntitiesESClient({ - request, - esClient: coreContext.elasticsearch.client.asCurrentUser, - }); + const { params, request, plugins } = resources; + const entityManagerStart = await plugins.entityManager.start(); + const entityManagerClient = await entityManagerStart.getScopedClient({ request }); const { serviceName } = params.path; const { environment } = params.query; - return getServiceEntitySummary({ - entitiesESClient, + const serviceEntitySummary = await getServiceEntitySummary({ + entityManagerClient, serviceName, environment, }); - }, -}); - -const servicesEntitiesRoute = createApmServerRoute({ - endpoint: 'GET /internal/apm/entities/services', - params: t.type({ - query: t.intersection([environmentRt, kueryRt, rangeRt]), - }), - security: { authz: { requiredPrivileges: ['apm'] } }, - async handler(resources) { - const { context, params, request } = resources; - const coreContext = await context.core; - - const entitiesESClient = await createEntitiesESClient({ - request, - esClient: coreContext.elasticsearch.client.asCurrentUser, - }); - - const { start, end, kuery, environment } = params.query; - - const services = await getServiceEntities({ - entitiesESClient, - start, - end, - kuery, - environment, - logger: resources.logger, - }); - return { services }; + return serviceEntitySummary; }, }); @@ -137,7 +103,6 @@ const serviceLogErrorRateTimeseriesRoute = createApmServerRoute({ }); export const servicesEntitiesRoutesRepository = { - ...servicesEntitiesRoute, ...serviceLogRateTimeseriesRoute, ...serviceLogErrorRateTimeseriesRoute, ...serviceEntitiesSummaryRoute, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts index 833e565ec00e..c65af1a05a26 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts @@ -4,28 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { AgentName } from '../../../typings/es_schemas/ui/fields/agent'; +import type { EntityMetadata, EntityV2 } from '@kbn/entities-schema'; export enum EntityType { SERVICE = 'service', } -export interface EntityLatestServiceRaw { - agent: { - name: AgentName[]; - }; - source_data_stream: { - type: string[]; - }; - service: { - name: string; - environment?: string; - }; - entity: Entity; -} - -interface Entity { - id: string; - last_seen_timestamp: string; - identity_fields: string[]; -} +export type EntityLatestServiceRaw = EntityV2 & EntityMetadata; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts index e3dd0ef5e0d4..91f1eff244de 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts @@ -13,17 +13,14 @@ describe('mergeEntities', () => { it('modifies one service', () => { const entities: EntityLatestServiceRaw[] = [ { - service: { - name: 'service-1', - environment: 'test', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: ['metrics', 'logs'] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name', 'service.environment'], - id: 'service-1:test', - }, + 'data_stream.type': ['metrics', 'logs'], + 'agent.name': 'nodejs', + 'service.environment': 'test', + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1', + 'entity.display_name': 'service-1', }, ]; const result = mergeEntities({ entities }); @@ -32,7 +29,7 @@ describe('mergeEntities', () => { agentName: 'nodejs' as AgentName, dataStreamTypes: ['metrics', 'logs'], environments: ['test'], - lastSeenTimestamp: '2024-06-05T10:34:40.810Z', + lastSeenTimestamp: '2024-12-13T14:52:35.461Z', serviceName: 'service-1', }, ]); @@ -41,56 +38,44 @@ describe('mergeEntities', () => { it('joins two service with the same name ', () => { const entities: EntityLatestServiceRaw[] = [ { - service: { - name: 'service-1', - environment: 'env-service-1', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: ['foo'] }, - entity: { - last_seen_timestamp: '2024-03-05T10:34:40.810Z', - identity_fields: ['service.name', 'service.environment'], - id: 'service-1:env-service-1', - }, + 'data_stream.type': ['foo'], + 'agent.name': 'nodejs', + 'service.environment': 'env-service-1', + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:env-service-1', + 'entity.display_name': 'service-1', }, { - service: { - name: 'service-1', - environment: 'env-service-2', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: ['bar'] }, - entity: { - last_seen_timestamp: '2024-03-05T10:34:40.810Z', - identity_fields: ['service.name', 'service.environment'], - id: 'apm-only-1:synthtrace-env-2', - }, + 'data_stream.type': ['bar'], + 'agent.name': 'nodejs', + 'service.environment': 'env-service-2', + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:env-service-2', + 'entity.display_name': 'service-1', }, { - service: { - name: 'service-2', - environment: 'env-service-3', - }, - agent: { name: ['java'] }, - source_data_stream: { type: ['baz'] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name', 'service.environment'], - id: 'service-2:env-service-3', - }, + 'data_stream.type': ['baz'], + 'agent.name': 'java', + 'service.environment': 'env-service-3', + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-2', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-2:env-service-3', + 'entity.display_name': 'service-2', }, { - service: { - name: 'service-2', - environment: 'env-service-4', - }, - agent: { name: ['java'] }, - source_data_stream: { type: ['baz'] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name', 'service.environment'], - id: 'service-2:env-service-3', - }, + 'data_stream.type': ['baz'], + 'agent.name': ['java'], + 'service.environment': 'env-service-4', + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-2', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-2:env-service-4', + 'entity.display_name': 'service-2', }, ]; @@ -100,14 +85,14 @@ describe('mergeEntities', () => { agentName: 'nodejs' as AgentName, dataStreamTypes: ['foo', 'bar'], environments: ['env-service-1', 'env-service-2'], - lastSeenTimestamp: '2024-03-05T10:34:40.810Z', + lastSeenTimestamp: '2024-12-13T14:52:35.461Z', serviceName: 'service-1', }, { agentName: 'java' as AgentName, dataStreamTypes: ['baz'], environments: ['env-service-3', 'env-service-4'], - lastSeenTimestamp: '2024-06-05T10:34:40.810Z', + lastSeenTimestamp: '2024-12-13T14:52:35.461Z', serviceName: 'service-2', }, ]); @@ -115,43 +100,34 @@ describe('mergeEntities', () => { it('handles duplicate environments and data streams', () => { const entities: EntityLatestServiceRaw[] = [ { - service: { - name: 'service-1', - environment: 'test', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: ['metrics', 'logs'] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name', 'service.environment'], - id: 'service-1:test', - }, + 'data_stream.type': ['metrics', 'logs'], + 'agent.name': ['nodejs'], + 'service.environment': 'test', + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:test', + 'entity.display_name': 'service-1', }, { - service: { - name: 'service-1', - environment: 'test', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: ['metrics', 'logs'] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name', 'service.environment'], - id: 'service-1:test', - }, + 'data_stream.type': ['metrics', 'logs'], + 'agent.name': ['nodejs'], + 'service.environment': 'test', + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:test', + 'entity.display_name': 'service-1', }, { - service: { - name: 'service-1', - environment: 'prod', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: ['foo'] }, - entity: { - last_seen_timestamp: '2024-23-05T10:34:40.810Z', - identity_fields: ['service.name', 'service.environment'], - id: 'service-1:prod', - }, + 'data_stream.type': ['foo'], + 'agent.name': ['nodejs'], + 'service.environment': 'prod', + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:prod', + 'entity.display_name': 'service-1', }, ]; const result = mergeEntities({ entities }); @@ -160,7 +136,7 @@ describe('mergeEntities', () => { agentName: 'nodejs' as AgentName, dataStreamTypes: ['metrics', 'logs', 'foo'], environments: ['test', 'prod'], - lastSeenTimestamp: '2024-23-05T10:34:40.810Z', + lastSeenTimestamp: '2024-12-13T14:52:35.461Z', serviceName: 'service-1', }, ]); @@ -168,17 +144,14 @@ describe('mergeEntities', () => { it('handles null environment', () => { const entity: EntityLatestServiceRaw[] = [ { - service: { - name: 'service-1', - environment: undefined, - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: [] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name'], - id: 'service-1:test', - }, + 'data_stream.type': [], + 'agent.name': ['nodejs'], + 'service.environment': null, + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:test', + 'entity.display_name': 'service-1', }, ]; const entityResult = mergeEntities({ entities: entity }); @@ -187,35 +160,31 @@ describe('mergeEntities', () => { agentName: 'nodejs' as AgentName, dataStreamTypes: [], environments: [], - lastSeenTimestamp: '2024-06-05T10:34:40.810Z', + lastSeenTimestamp: '2024-12-13T14:52:35.461Z', serviceName: 'service-1', }, ]); const entities: EntityLatestServiceRaw[] = [ { - service: { - name: 'service-1', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: [] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name'], - id: 'service-1:test', - }, + 'data_stream.type': [], + 'agent.name': ['nodejs'], + 'service.environment': null, + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:test', + 'entity.display_name': 'service-1', }, { - service: { - name: 'service-1', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: [] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name'], - id: 'service-1:test', - }, + 'data_stream.type': [], + 'agent.name': ['nodejs'], + 'service.environment': null, + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:test', + 'entity.display_name': 'service-1', }, ]; const result = mergeEntities({ entities }); @@ -224,7 +193,7 @@ describe('mergeEntities', () => { agentName: 'nodejs' as AgentName, dataStreamTypes: [], environments: [], - lastSeenTimestamp: '2024-06-05T10:34:40.810Z', + lastSeenTimestamp: '2024-12-13T14:52:35.461Z', serviceName: 'service-1', }, ]); @@ -233,16 +202,13 @@ describe('mergeEntities', () => { it('handles undefined environment', () => { const entity: EntityLatestServiceRaw[] = [ { - service: { - name: 'service-1', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: [] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name'], - id: 'service-1:test', - }, + 'data_stream.type': [], + 'agent.name': ['nodejs'], + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:test', + 'entity.display_name': 'service-1', }, ]; const entityResult = mergeEntities({ entities: entity }); @@ -251,35 +217,29 @@ describe('mergeEntities', () => { agentName: 'nodejs', dataStreamTypes: [], environments: [], - lastSeenTimestamp: '2024-06-05T10:34:40.810Z', + lastSeenTimestamp: '2024-12-13T14:52:35.461Z', serviceName: 'service-1', }, ]); const entities: EntityLatestServiceRaw[] = [ { - service: { - name: 'service-1', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: [] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name'], - id: 'service-1:test', - }, + 'data_stream.type': [], + 'agent.name': ['nodejs'], + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:test', + 'entity.display_name': 'service-1', }, { - service: { - name: 'service-1', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: [] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name'], - id: 'service-1:test', - }, + 'data_stream.type': [], + 'agent.name': ['nodejs'], + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:test', + 'entity.display_name': 'service-1', }, ]; const result = mergeEntities({ entities }); @@ -288,7 +248,7 @@ describe('mergeEntities', () => { agentName: 'nodejs', dataStreamTypes: [], environments: [], - lastSeenTimestamp: '2024-06-05T10:34:40.810Z', + lastSeenTimestamp: '2024-12-13T14:52:35.461Z', serviceName: 'service-1', }, ]); @@ -297,17 +257,14 @@ describe('mergeEntities', () => { it('has no logs when log rate is not returned', () => { const entities: EntityLatestServiceRaw[] = [ { - service: { - name: 'service-1', - environment: 'test', - }, - agent: { name: ['nodejs'] }, - source_data_stream: { type: ['metrics'] }, - entity: { - last_seen_timestamp: '2024-06-05T10:34:40.810Z', - identity_fields: ['service.name', 'service.environment'], - id: 'service-1:test', - }, + 'data_stream.type': ['metrics'], + 'agent.name': ['nodejs'], + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'service.environment': 'test', + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:test', + 'entity.display_name': 'service-1', }, ]; const result = mergeEntities({ entities }); @@ -316,7 +273,31 @@ describe('mergeEntities', () => { agentName: 'nodejs' as AgentName, dataStreamTypes: ['metrics'], environments: ['test'], - lastSeenTimestamp: '2024-06-05T10:34:40.810Z', + lastSeenTimestamp: '2024-12-13T14:52:35.461Z', + serviceName: 'service-1', + }, + ]); + }); + it('has multiple duplicate environments and data stream types', () => { + const entities: EntityLatestServiceRaw[] = [ + { + 'data_stream.type': ['metrics', 'metrics', 'logs', 'logs'], + 'agent.name': ['nodejs', 'nodejs'], + 'entity.last_seen_timestamp': '2024-12-13T14:52:35.461Z', + 'service.name': 'service-1', + 'service.environment': ['test', 'test', 'test'], + 'entity.type': 'built_in_services_from_ecs_data', + 'entity.id': 'service-1:test', + 'entity.display_name': 'service-1', + }, + ]; + const result = mergeEntities({ entities }); + expect(result).toEqual([ + { + agentName: 'nodejs' as AgentName, + dataStreamTypes: ['metrics', 'logs'], + environments: ['test'], + lastSeenTimestamp: '2024-12-13T14:52:35.461Z', serviceName: 'service-1', }, ]); diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts index 2f33c4728bd1..1e95656cb1f8 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts @@ -23,7 +23,7 @@ export function mergeEntities({ entities: EntityLatestServiceRaw[]; }): MergedServiceEntity[] { const mergedEntities = entities.reduce((map, current) => { - const key = current.service.name; + const key = current['service.name']; if (map.has(key)) { const existingEntity = map.get(key); map.set(key, mergeFunc(current, existingEntity)); @@ -33,28 +33,37 @@ export function mergeEntities({ return map; }, new Map()); - return [...mergedEntities.values()]; + return [...new Set(mergedEntities.values())]; } function mergeFunc(entity: EntityLatestServiceRaw, existingEntity?: MergedServiceEntity) { const commonEntityFields = { - serviceName: entity.service.name, - agentName: entity.agent.name[0], - lastSeenTimestamp: entity.entity.last_seen_timestamp, + serviceName: entity['service.name'], + agentName: + Array.isArray(entity['agent.name']) && entity['agent.name'].length > 0 + ? entity['agent.name'][0] + : entity['agent.name'], + lastSeenTimestamp: entity['entity.last_seen_timestamp'], }; if (!existingEntity) { return { ...commonEntityFields, - dataStreamTypes: entity.source_data_stream.type, - environments: compact([entity?.service.environment]), + dataStreamTypes: uniq(entity['data_stream.type']), + environments: uniq( + compact( + Array.isArray(entity['service.environment']) + ? entity['service.environment'] + : [entity['service.environment']] + ) + ), }; } return { ...commonEntityFields, dataStreamTypes: uniq( - compact([...(existingEntity?.dataStreamTypes ?? []), ...entity.source_data_stream.type]) + compact([...(existingEntity?.dataStreamTypes ?? []), ...entity['data_stream.type']]) ), - environments: uniq(compact([...existingEntity?.environments, entity?.service.environment])), + environments: uniq(compact([...existingEntity?.environments, entity['service.environment']])), }; } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/services/route.ts b/x-pack/plugins/observability_solution/apm/server/routes/services/route.ts index d6c3b5b73e3d..71d570d2708f 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/services/route.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/services/route.ts @@ -80,7 +80,6 @@ import { import { getThroughput, ServiceThroughputResponse } from './get_throughput'; import { getServiceEntitySummary } from '../entities/services/get_service_entity_summary'; import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; -import { createEntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client'; const servicesRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/services', @@ -297,16 +296,11 @@ const serviceAgentRoute = createApmServerRoute({ }), security: { authz: { requiredPrivileges: ['apm'] } }, handler: async (resources): Promise<ServiceAgentResponse> => { - const { context, request } = resources; - const coreContext = await context.core; + const { request, plugins } = resources; + const entityManagerStart = await plugins.entityManager.start(); - const [apmEventClient, entitiesESClient] = await Promise.all([ - getApmEventClient(resources), - createEntitiesESClient({ - request, - esClient: coreContext.elasticsearch.client.asCurrentUser, - }), - ]); + const apmEventClient = await getApmEventClient(resources); + const entityManagerClient = await entityManagerStart.getScopedClient({ request }); const { params } = resources; const { serviceName } = params.path; const { start, end } = params.query; @@ -320,7 +314,7 @@ const serviceAgentRoute = createApmServerRoute({ }), getServiceEntitySummary({ serviceName, - entitiesESClient, + entityManagerClient, environment: ENVIRONMENT_ALL.value, }), ]); diff --git a/x-pack/plugins/observability_solution/apm/server/types.ts b/x-pack/plugins/observability_solution/apm/server/types.ts index e99860b9d441..ba1d17d6af6b 100644 --- a/x-pack/plugins/observability_solution/apm/server/types.ts +++ b/x-pack/plugins/observability_solution/apm/server/types.ts @@ -5,52 +5,50 @@ * 2.0. */ -import { SharePluginSetup } from '@kbn/share-plugin/server'; -import { Observable } from 'rxjs'; -import { - RuleRegistryPluginSetupContract, - RuleRegistryPluginStartContract, -} from '@kbn/rule-registry-plugin/server'; -import { - PluginSetup as DataPluginSetup, - PluginStart as DataPluginStart, -} from '@kbn/data-plugin/server'; -import { +import type { ApmDataAccessPluginSetup, ApmDataAccessPluginStart, } from '@kbn/apm-data-access-plugin/server'; - -import { SpacesPluginSetup, SpacesPluginStart } from '@kbn/spaces-plugin/server'; +import type { + PluginSetup as DataPluginSetup, + PluginStart as DataPluginStart, +} from '@kbn/data-plugin/server'; +import type { + RuleRegistryPluginSetupContract, + RuleRegistryPluginStartContract, +} from '@kbn/rule-registry-plugin/server'; +import type { SharePluginSetup } from '@kbn/share-plugin/server'; +import type { Observable } from 'rxjs'; +import type { ActionsPlugin } from '@kbn/actions-plugin/server'; +import type { CloudSetup } from '@kbn/cloud-plugin/server'; +import type { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; +import type { FeaturesPluginSetup, FeaturesPluginStart } from '@kbn/features-plugin/server'; import { HomeServerPluginSetup, HomeServerPluginStart } from '@kbn/home-plugin/server'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; -import { ActionsPlugin } from '@kbn/actions-plugin/server'; import type { AlertingServerSetup, AlertingServerStart } from '@kbn/alerting-plugin/server'; -import { CloudSetup } from '@kbn/cloud-plugin/server'; -import { FeaturesPluginSetup, FeaturesPluginStart } from '@kbn/features-plugin/server'; import { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-plugin/server'; import { MlPluginSetup, MlPluginStart } from '@kbn/ml-plugin/server'; import { ObservabilityPluginSetup } from '@kbn/observability-plugin/server'; import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; -import { - TaskManagerSetupContract, - TaskManagerStartContract, -} from '@kbn/task-manager-plugin/server'; import { FleetSetupContract as FleetPluginSetup, FleetStartContract as FleetPluginStart, } from '@kbn/fleet-plugin/server'; -import { MetricsDataPluginSetup } from '@kbn/metrics-data-access-plugin/server'; -import { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; - -import { +import type { MetricsDataPluginSetup } from '@kbn/metrics-data-access-plugin/server'; +import type { SpacesPluginSetup, SpacesPluginStart } from '@kbn/spaces-plugin/server'; +import type { + TaskManagerSetupContract, + TaskManagerStartContract, +} from '@kbn/task-manager-plugin/server'; +import type { CustomIntegrationsPluginSetup, CustomIntegrationsPluginStart, } from '@kbn/custom-integrations-plugin/server'; -import { - ProfilingDataAccessPluginSetup, - ProfilingDataAccessPluginStart, -} from '@kbn/profiling-data-access-plugin/server'; -import { +import type { + EntityManagerServerPluginSetup, + EntityManagerServerPluginStart, +} from '@kbn/entityManager-plugin/server'; +import type { LogsDataAccessPluginSetup, LogsDataAccessPluginStart, } from '@kbn/logs-data-access-plugin/server'; @@ -58,6 +56,10 @@ import type { ObservabilityAIAssistantServerSetup, ObservabilityAIAssistantServerStart, } from '@kbn/observability-ai-assistant-plugin/server'; +import type { + ProfilingDataAccessPluginSetup, + ProfilingDataAccessPluginStart, +} from '@kbn/profiling-data-access-plugin/server'; import { APMConfig } from '.'; export interface APMPluginSetup { @@ -75,8 +77,10 @@ export interface APMPluginSetupDependencies { metricsDataAccess: MetricsDataPluginSetup; dataViews: {}; share: SharePluginSetup; - observabilityAIAssistant?: ObservabilityAIAssistantServerSetup; + logsDataAccess: LogsDataAccessPluginSetup; + entityManager: EntityManagerServerPluginSetup; // optional dependencies + observabilityAIAssistant?: ObservabilityAIAssistantServerSetup; actions?: ActionsPlugin['setup']; alerting?: AlertingServerSetup; cloud?: CloudSetup; @@ -89,7 +93,6 @@ export interface APMPluginSetupDependencies { usageCollection?: UsageCollectionSetup; customIntegrations?: CustomIntegrationsPluginSetup; profilingDataAccess?: ProfilingDataAccessPluginSetup; - logsDataAccess: LogsDataAccessPluginSetup; } export interface APMPluginStartDependencies { // required dependencies @@ -102,8 +105,10 @@ export interface APMPluginStartDependencies { metricsDataAccess: MetricsDataPluginSetup; dataViews: DataViewsServerPluginStart; share: undefined; - observabilityAIAssistant?: ObservabilityAIAssistantServerStart; + logsDataAccess: LogsDataAccessPluginStart; + entityManager: EntityManagerServerPluginStart; // optional dependencies + observabilityAIAssistant?: ObservabilityAIAssistantServerStart; actions?: ActionsPlugin['start']; alerting?: AlertingServerStart; cloud?: undefined; @@ -116,5 +121,4 @@ export interface APMPluginStartDependencies { usageCollection?: undefined; customIntegrations?: CustomIntegrationsPluginStart; profilingDataAccess?: ProfilingDataAccessPluginStart; - logsDataAccess: LogsDataAccessPluginStart; } diff --git a/x-pack/plugins/observability_solution/apm/tsconfig.json b/x-pack/plugins/observability_solution/apm/tsconfig.json index b2fda13c3f76..82dd82708603 100644 --- a/x-pack/plugins/observability_solution/apm/tsconfig.json +++ b/x-pack/plugins/observability_solution/apm/tsconfig.json @@ -129,6 +129,7 @@ "@kbn/alerting-comparators", "@kbn/saved-search-component", "@kbn/saved-search-plugin", + "@kbn/entityManager-plugin", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_entity_centric_experience_setting.tsx b/x-pack/plugins/observability_solution/infra/public/hooks/use_entity_centric_experience_setting.tsx index 1cbc4680405e..1ac6eed88f2a 100644 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_entity_centric_experience_setting.tsx +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_entity_centric_experience_setting.tsx @@ -11,7 +11,7 @@ import { useKibanaContextForPlugin } from './use_kibana'; export function useEntityCentricExperienceSetting() { const { uiSettings } = useKibanaContextForPlugin().services; - const isEntityCentricExperienceEnabled = uiSettings.get<boolean>(entityCentricExperience, true); + const isEntityCentricExperienceEnabled = uiSettings.get<boolean>(entityCentricExperience); return { isEntityCentricExperienceEnabled }; } diff --git a/x-pack/plugins/observability_solution/infra/public/plugin.ts b/x-pack/plugins/observability_solution/infra/public/plugin.ts index c8217794acf7..524ca1841be9 100644 --- a/x-pack/plugins/observability_solution/infra/public/plugin.ts +++ b/x-pack/plugins/observability_solution/infra/public/plugin.ts @@ -19,7 +19,6 @@ import { i18n } from '@kbn/i18n'; import { METRICS_EXPLORER_LOCATOR_ID, MetricsExplorerLocatorParams, - ObservabilityTriggerId, } from '@kbn/observability-shared-plugin/common'; import { BehaviorSubject, @@ -101,10 +100,6 @@ export class Plugin implements InfraClientPluginClass { registerFeatures(pluginsSetup.home); } - pluginsSetup.uiActions.registerTrigger({ - id: ObservabilityTriggerId.LogEntryContextMenu, - }); - const assetDetailsLocator = pluginsSetup.share.url.locators.get<AssetDetailsLocatorParams>(ASSET_DETAILS_LOCATOR_ID); const inventoryLocator = diff --git a/x-pack/plugins/observability_solution/infra/server/routes/entities/get_data_stream_types.test.ts b/x-pack/plugins/observability_solution/infra/server/routes/entities/get_data_stream_types.test.ts index 75048ac22a6a..4a1466b698c7 100644 --- a/x-pack/plugins/observability_solution/infra/server/routes/entities/get_data_stream_types.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/routes/entities/get_data_stream_types.test.ts @@ -35,13 +35,13 @@ describe('getDataStreamTypes', () => { jest.clearAllMocks(); }); - it('should return only metrics when entityCentriExperienceEnabled is false and hasMetricsData is true', async () => { + it('should return only metrics when entityCentricExperienceEnabled is false and hasMetricsData is true', async () => { (getHasMetricsData as jest.Mock).mockResolvedValue(true); const params = { entityId: 'entity123', entityType: 'host' as EntityType, - entityCentriExperienceEnabled: false, + entityCentricExperienceEnabled: false, infraMetricsClient, obsEsClient, entityManagerClient, @@ -58,13 +58,13 @@ describe('getDataStreamTypes', () => { }); }); - it('should return an empty array when entityCentriExperienceEnabled is false and hasMetricsData is false', async () => { + it('should return an empty array when entityCentricExperienceEnabled is false and hasMetricsData is false', async () => { (getHasMetricsData as jest.Mock).mockResolvedValue(false); const params = { entityId: 'entity123', entityType: 'container' as EntityType, - entityCentriExperienceEnabled: false, + entityCentricExperienceEnabled: false, infraMetricsClient, obsEsClient, entityManagerClient, @@ -75,7 +75,7 @@ describe('getDataStreamTypes', () => { expect(result).toEqual([]); }); - it('should return metrics and entity source_data_stream types when entityCentriExperienceEnabled is true and has entity data', async () => { + it('should return metrics and entity source_data_stream types when entityCentricExperienceEnabled is true and has entity data', async () => { (getHasMetricsData as jest.Mock).mockResolvedValue(true); (getLatestEntity as jest.Mock).mockResolvedValue({ sourceDataStreamType: ['logs', 'metrics'], @@ -84,7 +84,7 @@ describe('getDataStreamTypes', () => { const params = { entityId: 'entity123', entityType: 'host' as EntityType, - entityCentriExperienceEnabled: true, + entityCentricExperienceEnabled: true, infraMetricsClient, obsEsClient, entityManagerClient, @@ -104,14 +104,14 @@ describe('getDataStreamTypes', () => { }); }); - it('should return only metrics when entityCentriExperienceEnabled is true but entity data is undefined', async () => { + it('should return only metrics when entityCentricExperienceEnabled is true but entity data is undefined', async () => { (getHasMetricsData as jest.Mock).mockResolvedValue(true); (getLatestEntity as jest.Mock).mockResolvedValue(undefined); const params = { entityId: 'entity123', entityType: 'host' as EntityType, - entityCentriExperienceEnabled: true, + entityCentricExperienceEnabled: true, infraMetricsClient, obsEsClient, entityManagerClient, @@ -131,7 +131,7 @@ describe('getDataStreamTypes', () => { const params = { entityId: 'entity123', entityType: 'host' as EntityType, - entityCentriExperienceEnabled: true, + entityCentricExperienceEnabled: true, infraMetricsClient, obsEsClient, entityManagerClient, diff --git a/x-pack/plugins/observability_solution/infra/server/routes/entities/get_data_stream_types.ts b/x-pack/plugins/observability_solution/infra/server/routes/entities/get_data_stream_types.ts index 4a949de4d0ed..d1cd99be31f9 100644 --- a/x-pack/plugins/observability_solution/infra/server/routes/entities/get_data_stream_types.ts +++ b/x-pack/plugins/observability_solution/infra/server/routes/entities/get_data_stream_types.ts @@ -18,7 +18,7 @@ import { getLatestEntity } from './get_latest_entity'; interface Params { entityId: string; entityType: 'host' | 'container'; - entityCentriExperienceEnabled: boolean; + entityCentricExperienceEnabled: boolean; infraMetricsClient: InfraMetricsClient; obsEsClient: ObservabilityElasticsearchClient; entityManagerClient: EntityClient; @@ -26,7 +26,7 @@ interface Params { } export async function getDataStreamTypes({ - entityCentriExperienceEnabled, + entityCentricExperienceEnabled, entityId, entityManagerClient, entityType, @@ -42,7 +42,7 @@ export async function getDataStreamTypes({ const sourceDataStreams = new Set(hasMetricsData ? [EntityDataStreamType.METRICS] : []); - if (!entityCentriExperienceEnabled) { + if (!entityCentricExperienceEnabled) { return Array.from(sourceDataStreams); } diff --git a/x-pack/plugins/observability_solution/infra/server/routes/entities/index.ts b/x-pack/plugins/observability_solution/infra/server/routes/entities/index.ts index 4056faba9427..9c5c5c4527fe 100644 --- a/x-pack/plugins/observability_solution/infra/server/routes/entities/index.ts +++ b/x-pack/plugins/observability_solution/infra/server/routes/entities/index.ts @@ -54,13 +54,13 @@ export const initEntitiesConfigurationRoutes = (libs: InfraBackendLibs) => { plugin: `@kbn/${METRICS_APP_ID}-plugin`, }); - const entityCentriExperienceEnabled = await coreContext.uiSettings.client.get( + const entityCentricExperienceEnabled = await coreContext.uiSettings.client.get( entityCentricExperience ); try { const sourceDataStreamTypes = await getDataStreamTypes({ - entityCentriExperienceEnabled, + entityCentricExperienceEnabled, entityId, entityManagerClient, entityType, diff --git a/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/alert_count/alert_count.cy.ts b/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/alert_count/alert_count.cy.ts index ac5fa17ecb08..525828de10b1 100644 --- a/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/alert_count/alert_count.cy.ts +++ b/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/alert_count/alert_count.cy.ts @@ -33,10 +33,15 @@ const verifyAlertsTableCount = (alertsCount: string) => { verifyNumber(cy.getByTestSubj('toolbar-alerts-count'), alertsCount); }; -describe('Alert count', () => { +// Temporary skipping those test, will be enabled in the future once we fix them https://github.com/elastic/kibana/issues/204558 +describe.skip('Alert count', () => { beforeEach(() => { cy.loginAsSuperUser(); + cy.updateAdvancedSettings({ + 'observability:entityCentricExperience': true, + }); + cy.intercept('GET', '/internal/entities/managed/enablement', { fixture: 'eem_enabled.json', }).as('getEEMStatus'); @@ -49,6 +54,9 @@ describe('Alert count', () => { }); afterEach(() => { + cy.updateAdvancedSettings({ + 'observability:entityCentricExperience': false, + }); entitiesSynthtrace.clean(); cleanEntityAlerts(); }); diff --git a/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts b/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts index c2e7f1232e6a..e86c673c2a6b 100644 --- a/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts +++ b/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts @@ -11,9 +11,29 @@ import { generateEntities, generateLogs, generateTraces } from './generate_data' const start = '2024-10-16T00:00:00.000Z'; const end = '2024-10-16T00:15:00.000Z'; -describe('Home page', () => { +// Temporary skipping those test, will be enabled in the future once we fix them https://github.com/elastic/kibana/issues/204558 +describe.skip('Home page', () => { beforeEach(() => { cy.loginAsSuperUser(); + cy.updateAdvancedSettings({ + 'observability:entityCentricExperience': true, + }); + }); + + afterEach(() => { + cy.updateAdvancedSettings({ + 'observability:entityCentricExperience': false, + }); + }); + + describe('When the entityCentricExperience FF is disabled', () => { + it('Shows 404', () => { + cy.updateAdvancedSettings({ + 'observability:entityCentricExperience': false, + }); + cy.visitKibana('/app/inventory'); + cy.contains('Application Not Found'); + }); }); describe('When EEM is disabled', () => { diff --git a/x-pack/plugins/observability_solution/inventory/e2e/cypress/support/commands.ts b/x-pack/plugins/observability_solution/inventory/e2e/cypress/support/commands.ts index c3462f8b6ff1..9a260519c760 100644 --- a/x-pack/plugins/observability_solution/inventory/e2e/cypress/support/commands.ts +++ b/x-pack/plugins/observability_solution/inventory/e2e/cypress/support/commands.ts @@ -55,3 +55,17 @@ Cypress.Commands.add( ); } ); + +Cypress.Commands.add('updateAdvancedSettings', (settings: Record<string, unknown>) => { + const kibanaUrl = Cypress.env('KIBANA_URL'); + cy.request({ + log: false, + method: 'POST', + url: `${kibanaUrl}/internal/kibana/settings`, + body: { changes: settings }, + headers: { + 'kbn-xsrf': 'e2e_test', + }, + auth: { user: 'elastic', pass: 'changeme' }, + }); +}); diff --git a/x-pack/plugins/observability_solution/inventory/e2e/cypress/support/types.d.ts b/x-pack/plugins/observability_solution/inventory/e2e/cypress/support/types.d.ts index c51b20c3b990..8e49da093612 100644 --- a/x-pack/plugins/observability_solution/inventory/e2e/cypress/support/types.d.ts +++ b/x-pack/plugins/observability_solution/inventory/e2e/cypress/support/types.d.ts @@ -13,6 +13,7 @@ declare namespace Cypress { password: string; }): Cypress.Chainable<Cypress.Response<any>>; getByTestSubj(selector: string): Chainable<JQuery<Element>>; + updateAdvancedSettings(settings: Record<string, unknown>): void; visitKibana(url: string): void; } } diff --git a/x-pack/plugins/observability_solution/inventory/public/plugin.ts b/x-pack/plugins/observability_solution/inventory/public/plugin.ts index 109123859d4c..0d9309d3694f 100644 --- a/x-pack/plugins/observability_solution/inventory/public/plugin.ts +++ b/x-pack/plugins/observability_solution/inventory/public/plugin.ts @@ -57,7 +57,7 @@ export class InventoryPlugin const inventoryAPIClient = createCallInventoryAPI(coreSetup); const isEntityCentricExperienceSettingEnabled = coreSetup.uiSettings.get<boolean>( 'observability:entityCentricExperience', - true + false ); this.telemetry.setup({ diff --git a/x-pack/plugins/observability_solution/logs_data_access/kibana.jsonc b/x-pack/plugins/observability_solution/logs_data_access/kibana.jsonc index 02fcff85404a..f3eccc1c0c15 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/kibana.jsonc +++ b/x-pack/plugins/observability_solution/logs_data_access/kibana.jsonc @@ -1,13 +1,17 @@ { "type": "plugin", "id": "@kbn/logs-data-access-plugin", - "owner": ["@elastic/obs-ux-logs-team"], + "owner": [ + "@elastic/obs-ux-logs-team" + ], + "group": "platform", + "visibility": "shared", "plugin": { "id": "logsDataAccess", "server": true, "browser": true, "requiredPlugins": [ - "data", + "data", "dataViews" ], "optionalPlugins": [], diff --git a/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc b/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc index e6ee1a22edc0..46aa8f451260 100644 --- a/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc +++ b/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc @@ -2,12 +2,17 @@ "type": "plugin", "id": "@kbn/logs-shared-plugin", "owner": "@elastic/obs-ux-logs-team", + "group": "platform", + "visibility": "shared", "description": "Exposes the shared components and APIs to access and visualize logs.", "plugin": { "id": "logsShared", "server": true, "browser": true, - "configPath": ["xpack", "logs_shared"], + "configPath": [ + "xpack", + "logs_shared" + ], "requiredPlugins": [ "charts", "data", @@ -15,16 +20,21 @@ "dataViews", "discoverShared", "logsDataAccess", - "observabilityShared", "share", "spaces", + "uiActions", "usageCollection", "embeddable", ], "optionalPlugins": [ "observabilityAIAssistant", ], - "requiredBundles": ["kibanaUtils", "kibanaReact"], - "extraPublicDirs": ["common"] + "requiredBundles": [ + "kibanaUtils", + "kibanaReact" + ], + "extraPublicDirs": [ + "common" + ] } } diff --git a/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx b/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx index f82e9436fe5c..8d33fe4b5d34 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx +++ b/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx @@ -10,19 +10,19 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { buildEsQuery, Filter, Query } from '@kbn/es-query'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import { JsonValue } from '@kbn/utility-types'; import { noop } from 'lodash'; import React, { useCallback, useEffect, useMemo } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; -import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; -import { useKibanaQuerySettings } from '@kbn/observability-shared-plugin/public'; import { LogEntryCursor } from '../../../common/log_entry'; import { defaultLogViewsStaticConfig, LogViewReference } from '../../../common/log_views'; import { BuiltEsQuery, useLogStream } from '../../containers/logs/log_stream'; import { useLogView } from '../../hooks/use_log_view'; import { LogViewsClient } from '../../services/log_views'; import { LogColumnRenderConfiguration } from '../../utils/log_column_render_configuration'; +import { useKibanaQuerySettings } from '../../utils/use_kibana_query_settings'; import { useLogEntryFlyout } from '../logging/log_entry_flyout'; import { ScrollableLogTextStreamView, VisibleInterval } from '../logging/log_text_stream'; import { LogStreamErrorBoundary } from './log_stream_error_boundary'; diff --git a/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx b/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx index ad989f846001..05906b2794c9 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx +++ b/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx @@ -7,6 +7,8 @@ import { coreMock } from '@kbn/core/public/mocks'; import { + TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR, + TransactionDetailsByTraceIdLocatorParams, uptimeOverviewLocatorID, UptimeOverviewLocatorInfraParams, UptimeOverviewLocatorParams, @@ -26,10 +28,10 @@ coreStartMock.application.getUrlForApp.mockImplementation((app, options) => { }); const emptyUrlService = new MockUrlService(); -const urlServiceWithUptimeLocator = new MockUrlService(); +const urlServiceWithMockLocators = new MockUrlService(); // we can't use the actual locator here because its import would create a // forbidden ts project reference cycle -urlServiceWithUptimeLocator.locators.create< +urlServiceWithMockLocators.locators.create< UptimeOverviewLocatorInfraParams | UptimeOverviewLocatorParams >({ id: uptimeOverviewLocatorID, @@ -37,6 +39,12 @@ urlServiceWithUptimeLocator.locators.create< return { app: 'uptime', path: '/overview', state: {} }; }, }); +urlServiceWithMockLocators.locators.create<TransactionDetailsByTraceIdLocatorParams>({ + id: TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR, + getLocation: async (params) => { + return { app: 'apm', path: '/trace-id', state: {} }; + }, +}); const ProviderWrapper: FC<React.PropsWithChildren<{ urlService?: UrlService }>> = ({ children, @@ -90,7 +98,7 @@ describe('LogEntryActionsMenu component', () => { describe('uptime link with legacy uptime enabled', () => { it('renders as enabled when a host ip is present in the log entry', () => { const elementWrapper = mount( - <ProviderWrapper urlService={urlServiceWithUptimeLocator}> + <ProviderWrapper urlService={urlServiceWithMockLocators}> <LogEntryActionsMenu logEntry={{ fields: [{ field: 'host.ip', value: ['HOST_IP'] }], @@ -120,7 +128,7 @@ describe('LogEntryActionsMenu component', () => { it('renders as enabled when a container id is present in the log entry', () => { const elementWrapper = mount( - <ProviderWrapper urlService={urlServiceWithUptimeLocator}> + <ProviderWrapper urlService={urlServiceWithMockLocators}> <LogEntryActionsMenu logEntry={{ fields: [{ field: 'container.id', value: ['CONTAINER_ID'] }], @@ -150,7 +158,7 @@ describe('LogEntryActionsMenu component', () => { it('renders as enabled when a pod uid is present in the log entry', () => { const elementWrapper = mount( - <ProviderWrapper urlService={urlServiceWithUptimeLocator}> + <ProviderWrapper urlService={urlServiceWithMockLocators}> <LogEntryActionsMenu logEntry={{ fields: [{ field: 'kubernetes.pod.uid', value: ['POD_UID'] }], @@ -180,7 +188,7 @@ describe('LogEntryActionsMenu component', () => { it('renders as disabled when no supported field is present in the log entry', () => { const elementWrapper = mount( - <ProviderWrapper urlService={urlServiceWithUptimeLocator}> + <ProviderWrapper urlService={urlServiceWithMockLocators}> <LogEntryActionsMenu logEntry={{ fields: [], @@ -215,7 +223,7 @@ describe('LogEntryActionsMenu component', () => { describe('apm link', () => { it('renders with a trace id filter when present in log entry', () => { const elementWrapper = mount( - <ProviderWrapper> + <ProviderWrapper urlService={urlServiceWithMockLocators}> <LogEntryActionsMenu logEntry={{ fields: [{ field: 'trace.id', value: ['1234567'] }], @@ -246,7 +254,7 @@ describe('LogEntryActionsMenu component', () => { it('renders with a trace id filter and timestamp when present in log entry', () => { const timestamp = '2019-06-27T17:44:08.693Z'; const elementWrapper = mount( - <ProviderWrapper> + <ProviderWrapper urlService={urlServiceWithMockLocators}> <LogEntryActionsMenu logEntry={{ fields: [ @@ -279,7 +287,7 @@ describe('LogEntryActionsMenu component', () => { it('renders as disabled when no supported field is present in log entry', () => { const elementWrapper = mount( - <ProviderWrapper> + <ProviderWrapper urlService={urlServiceWithMockLocators}> <LogEntryActionsMenu logEntry={{ fields: [], @@ -303,7 +311,10 @@ describe('LogEntryActionsMenu component', () => { elementWrapper.update(); expect( - elementWrapper.find(`button${testSubject('~apmLogEntryActionsMenuItem')}`).prop('disabled') + elementWrapper + .find(`${testSubject('~apmLogEntryActionsMenuItem')}`) + .first() + .prop('disabled') ).toEqual(true); }); }); diff --git a/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx b/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx index 4f16d34a489a..404f6c37bffa 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx +++ b/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx @@ -7,13 +7,14 @@ import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; import { + TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR, uptimeOverviewLocatorID, + type TransactionDetailsByTraceIdLocatorParams, type UptimeOverviewLocatorInfraParams, } from '@kbn/deeplinks-observability'; import { FormattedMessage } from '@kbn/i18n-react'; -import { LinkDescriptor, useLinkProps } from '@kbn/observability-shared-plugin/public'; import { getRouterLinkProps } from '@kbn/router-utils'; -import { ILocatorClient } from '@kbn/share-plugin/common/url_service'; +import { BrowserUrlService } from '@kbn/share-plugin/public'; import React, { useMemo } from 'react'; import { LogEntry } from '../../../../common/search_strategies/log_entries/log_entry'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; @@ -33,14 +34,11 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => { } = useKibanaContextForPlugin(); const { hide, isVisible, toggle } = useVisibilityState(false); - const apmLinkDescriptor = useMemo(() => getAPMLink(logEntry), [logEntry]); - - const uptimeLinkProps = getUptimeLink({ locators })(logEntry); - - const apmLinkProps = useLinkProps({ - app: 'apm', - ...(apmLinkDescriptor ? apmLinkDescriptor : {}), - }); + const apmLinkProps = useMemo(() => getAPMLink({ locators })(logEntry), [locators, logEntry]); + const uptimeLinkProps = useMemo( + () => getUptimeLink({ locators })(logEntry), + [locators, logEntry] + ); const menuItems = useMemo( () => [ @@ -58,7 +56,7 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => { </EuiContextMenuItem>, <EuiContextMenuItem data-test-subj="logEntryActionsMenuItem apmLogEntryActionsMenuItem" - disabled={!apmLinkDescriptor} + disabled={!apmLinkProps} icon="apmApp" key="apmLink" {...apmLinkProps} @@ -69,7 +67,7 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => { /> </EuiContextMenuItem>, ], - [apmLinkDescriptor, apmLinkProps, uptimeLinkProps] + [apmLinkProps, uptimeLinkProps] ); const hasMenuItems = useMemo(() => menuItems.length > 0, [menuItems]); @@ -101,8 +99,8 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => { }; const getUptimeLink = - ({ locators }: { locators: ILocatorClient }) => - (logEntry: LogEntry): ContextRouterLinkProps | undefined => { + ({ locators }: { locators: BrowserUrlService['locators'] }) => + (logEntry: LogEntry) => { const uptimeLocator = locators.get<UptimeOverviewLocatorInfraParams>(uptimeOverviewLocatorID); if (!uptimeLocator) { @@ -135,47 +133,49 @@ const getUptimeLink = }) as ContextRouterLinkProps; }; -const getAPMLink = (logEntry: LogEntry): LinkDescriptor | undefined => { - const traceId = logEntry.fields.find( - ({ field, value }) => typeof value[0] === 'string' && field === 'trace.id' - )?.value?.[0]; - - if (typeof traceId !== 'string') { - return undefined; - } - - const timestampField = logEntry.fields.find(({ field }) => field === '@timestamp'); - const timestamp = timestampField ? timestampField.value[0] : null; - const { rangeFrom, rangeTo } = - typeof timestamp === 'number' - ? (() => { - const from = new Date(timestamp); - const to = new Date(timestamp); - - from.setMinutes(from.getMinutes() - 10); - to.setMinutes(to.getMinutes() + 10); - - return { rangeFrom: from.toISOString(), rangeTo: to.toISOString() }; - })() - : { rangeFrom: 'now-1y', rangeTo: 'now' }; - - return { - app: 'apm', - pathname: getApmTraceUrl({ traceId, rangeFrom, rangeTo }), - }; -}; +const getAPMLink = + ({ locators }: { locators: BrowserUrlService['locators'] }) => + (logEntry: LogEntry) => { + const traceId = logEntry.fields.find( + ({ field, value }) => typeof value[0] === 'string' && field === 'trace.id' + )?.value?.[0]; -function getApmTraceUrl({ - traceId, - rangeFrom, - rangeTo, -}: { - traceId: string; - rangeFrom: string; - rangeTo: string; -}) { - return `/link-to/trace/${traceId}?` + new URLSearchParams({ rangeFrom, rangeTo }).toString(); -} + if (typeof traceId !== 'string') { + return undefined; + } + + const apmLocator = locators.get<TransactionDetailsByTraceIdLocatorParams>( + TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR + ); + + if (!apmLocator) { + return undefined; + } + + const timestampField = logEntry.fields.find(({ field }) => field === '@timestamp'); + const timestamp = timestampField ? timestampField.value[0] : null; + const { rangeFrom, rangeTo } = + typeof timestamp === 'number' || typeof timestamp === 'string' + ? (() => { + const from = new Date(timestamp); + const to = new Date(timestamp); + + from.setMinutes(from.getMinutes() - 10); + to.setMinutes(to.getMinutes() + 10); + + return { rangeFrom: from.toISOString(), rangeTo: to.toISOString() }; + })() + : { rangeFrom: 'now-1y', rangeTo: 'now' }; + + const apmLocatorParams = { traceId, rangeFrom, rangeTo }; + + // Coercing the return value to ContextRouterLinkProps because + // EuiContextMenuItem defines a too broad type for onClick + return getRouterLinkProps({ + href: apmLocator.getRedirectUrl(apmLocatorParams), + onClick: () => apmLocator.navigate(apmLocatorParams), + }) as ContextRouterLinkProps; + }; export interface ContextRouterLinkProps { href: string | undefined; diff --git a/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx b/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx index 952ee959e4a7..a66cb1790525 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx +++ b/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx @@ -28,7 +28,6 @@ import { useLogEntry } from '../../../containers/logs/log_entry'; import { CenteredEuiFlyoutBody } from '../../centered_flyout_body'; import { DataSearchErrorCallout } from '../../data_search_error_callout'; import { DataSearchProgress } from '../../data_search_progress'; -import LogAIAssistant from '../../log_ai_assistant/log_ai_assistant'; import { LogEntryActionsMenu } from './log_entry_actions_menu'; import { LogEntryFieldsTable } from './log_entry_fields_table'; @@ -42,7 +41,7 @@ export interface LogEntryFlyoutProps { export const useLogEntryFlyout = (logViewReference: LogViewReference) => { const flyoutRef = useRef<OverlayRef>(); const { - services: { http, data, share, uiSettings, application, observabilityAIAssistant }, + services: { http, data, share, uiSettings, application, logsShared }, overlays: { openFlyout }, } = useKibanaContextForPlugin(); @@ -58,7 +57,7 @@ export const useLogEntryFlyout = (logViewReference: LogViewReference) => { share, uiSettings, application, - observabilityAIAssistant, + logsShared, }); flyoutRef.current = openFlyout( @@ -72,12 +71,12 @@ export const useLogEntryFlyout = (logViewReference: LogViewReference) => { ); }, [ + logsShared, application, closeLogEntryFlyout, data, http, logViewReference, - observabilityAIAssistant, openFlyout, share, uiSettings, @@ -115,7 +114,11 @@ export const LogEntryFlyout = ({ logEntryId, }); - const { observabilityAIAssistant } = useKibanaContextForPlugin().services; + const { + services: { + logsShared: { LogAIAssistant }, + }, + } = useKibanaContextForPlugin(); useEffect(() => { if (logViewReference && logEntryId) { @@ -183,12 +186,9 @@ export const LogEntryFlyout = ({ } > <EuiFlexGroup direction="column" gutterSize="m"> - {observabilityAIAssistant && ( + {LogAIAssistant && ( <EuiFlexItem grow={false}> - <LogAIAssistant - observabilityAIAssistant={observabilityAIAssistant} - doc={logEntry} - /> + <LogAIAssistant doc={logEntry} /> </EuiFlexItem> )} <EuiFlexItem grow={false}> diff --git a/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_text_stream/index.ts b/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_text_stream/index.ts index 6468ea3d94d2..be32a2be6dc5 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_text_stream/index.ts +++ b/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_text_stream/index.ts @@ -5,12 +5,11 @@ * 2.0. */ -export type { LogEntryStreamItem } from './item'; -export type { LogEntryColumnWidths } from './log_entry_column'; - export { LogColumnHeader } from './column_headers'; export { LogColumnHeadersWrapper } from './column_headers_wrapper'; -export { iconColumnId, LogEntryColumn, useColumnWidths } from './log_entry_column'; +export type { LogEntryStreamItem } from './item'; +export { LogEntryColumn, iconColumnId, useColumnWidths } from './log_entry_column'; +export type { LogEntryColumnWidths } from './log_entry_column'; export { LogEntryContextMenu } from './log_entry_context_menu'; export { LogEntryFieldColumn } from './log_entry_field_column'; export { LogEntryMessageColumn } from './log_entry_message_column'; diff --git a/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_text_stream/log_entry_row.tsx b/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_text_stream/log_entry_row.tsx index b73da833032f..411b5d073138 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_text_stream/log_entry_row.tsx +++ b/x-pack/plugins/observability_solution/logs_shared/public/components/logging/log_text_stream/log_entry_row.tsx @@ -6,25 +6,19 @@ */ import { i18n } from '@kbn/i18n'; -import { ObservabilityTriggerId } from '@kbn/observability-shared-plugin/common'; -import { - useUiTracker, - getContextMenuItemsFromActions, -} from '@kbn/observability-shared-plugin/public'; import { isEmpty } from 'lodash'; import React, { memo, useCallback, useMemo, useState } from 'react'; -import useAsync from 'react-use/lib/useAsync'; import { LogColumn, LogEntry } from '../../../../common/log_entry'; import { TextScale } from '../../../../common/log_text_scale'; -import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { + LogColumnRenderConfiguration, isFieldColumnRenderConfiguration, isMessageColumnRenderConfiguration, isTimestampColumnRenderConfiguration, - LogColumnRenderConfiguration, } from '../../../utils/log_column_render_configuration'; import { isTimestampColumn } from '../../../utils/log_entry'; -import { iconColumnId, LogEntryColumn, LogEntryColumnWidths } from './log_entry_column'; +import { useUiTracker } from '../../../utils/use_ui_tracker'; +import { LogEntryColumn, LogEntryColumnWidths, iconColumnId } from './log_entry_column'; import { LogEntryContextMenu } from './log_entry_context_menu'; import { LogEntryFieldColumn } from './log_entry_field_column'; import { LogEntryMessageColumn } from './log_entry_message_column'; @@ -74,7 +68,7 @@ export const LogEntryRow = memo( scale, wrap, }: LogEntryRowProps) => { - const trackMetric = useUiTracker({ app: 'infra_logs' }); + const trackMetric = useUiTracker(); const [isHovered, setIsHovered] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false); @@ -99,16 +93,6 @@ export const LogEntryRow = memo( const hasActionViewLogInContext = hasContext && openViewLogInContext !== undefined; const hasActionsMenu = hasActionFlyoutWithItem || hasActionViewLogInContext; - const uiActions = useKibanaContextForPlugin().services.uiActions; - - const externalContextMenuItems = useAsync(() => { - return getContextMenuItemsFromActions({ - uiActions, - triggerId: ObservabilityTriggerId.LogEntryContextMenu, - context: logEntry, - }); - }, [uiActions, logEntry]); - const menuItems = useMemo(() => { const items = []; if (hasActionFlyoutWithItem) { @@ -251,7 +235,6 @@ export const LogEntryRow = memo( onOpen={openMenu} onClose={closeMenu} items={menuItems} - externalItems={externalContextMenuItems.value} /> ) : null} </LogEntryColumn> diff --git a/x-pack/plugins/observability_solution/logs_shared/public/hooks/use_kibana.tsx b/x-pack/plugins/observability_solution/logs_shared/public/hooks/use_kibana.tsx index 09032b4b644a..af55accdd66b 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/hooks/use_kibana.tsx +++ b/x-pack/plugins/observability_solution/logs_shared/public/hooks/use_kibana.tsx @@ -20,8 +20,7 @@ import { } from '../types'; export type PluginKibanaContextValue = CoreStart & - LogsSharedClientStartDeps & - LogsSharedClientStartExports; + LogsSharedClientStartDeps & { logsShared: LogsSharedClientStartExports }; export const createKibanaContextForPlugin = ( core: CoreStart, @@ -31,7 +30,7 @@ export const createKibanaContextForPlugin = ( createKibanaReactContext<PluginKibanaContextValue>({ ...core, ...plugins, - ...pluginStart, + logsShared: pluginStart, }); export const useKibanaContextForPlugin = diff --git a/x-pack/plugins/observability_solution/logs_shared/public/utils/use_kibana_query_settings.ts b/x-pack/plugins/observability_solution/logs_shared/public/utils/use_kibana_query_settings.ts new file mode 100644 index 000000000000..521cd0142303 --- /dev/null +++ b/x-pack/plugins/observability_solution/logs_shared/public/utils/use_kibana_query_settings.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 type { EsQueryConfig } from '@kbn/es-query'; +import { SerializableRecord } from '@kbn/utility-types'; +import { useMemo } from 'react'; +import { UI_SETTINGS } from '@kbn/data-plugin/public'; +import { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; + +export const useKibanaQuerySettings = (): EsQueryConfig => { + const [allowLeadingWildcards] = useUiSetting$<boolean>(UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS); + const [queryStringOptions] = useUiSetting$<SerializableRecord>(UI_SETTINGS.QUERY_STRING_OPTIONS); + const [dateFormatTZ] = useUiSetting$<string>(UI_SETTINGS.DATEFORMAT_TZ); + const [ignoreFilterIfFieldNotInIndex] = useUiSetting$<boolean>( + UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX + ); + + return useMemo( + () => ({ + allowLeadingWildcards, + queryStringOptions, + dateFormatTZ, + ignoreFilterIfFieldNotInIndex, + }), + [allowLeadingWildcards, dateFormatTZ, ignoreFilterIfFieldNotInIndex, queryStringOptions] + ); +}; diff --git a/x-pack/plugins/observability_solution/logs_shared/public/utils/use_ui_tracker.ts b/x-pack/plugins/observability_solution/logs_shared/public/utils/use_ui_tracker.ts new file mode 100644 index 000000000000..bc7e3696b993 --- /dev/null +++ b/x-pack/plugins/observability_solution/logs_shared/public/utils/use_ui_tracker.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 { useMemo } from 'react'; +import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; +import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; + +/** + * Note: The usage_collection plugin will take care of sending this data to the telemetry server. + * You can find the metrics that are collected by these hooks in Stack Telemetry. + * Search the index `kibana-ui-counter`. You can filter for `eventName` and/or `appName`. + */ + +interface TrackOptions { + metricType?: UiCounterMetricType; + delay?: number; // in ms +} + +interface ServiceDeps { + usageCollection: UsageCollectionSetup; // TODO: This should really be start. Looking into it. +} + +export type TrackMetricOptions = TrackOptions & { metric: string }; +export type UiTracker = ReturnType<typeof useUiTracker>; +export type TrackEvent = (options: TrackMetricOptions) => void; + +export { METRIC_TYPE }; + +export function useUiTracker<Services extends ServiceDeps>(): TrackEvent { + const reportUiCounter = useKibana<Services>().services?.usageCollection?.reportUiCounter; + const trackEvent = useMemo(() => { + return ({ metric, metricType = METRIC_TYPE.COUNT }: TrackMetricOptions) => { + if (reportUiCounter) { + reportUiCounter('infra_logs', metricType, metric); + } + }; + }, [reportUiCounter]); + return trackEvent; +} diff --git a/x-pack/plugins/observability_solution/logs_shared/tsconfig.json b/x-pack/plugins/observability_solution/logs_shared/tsconfig.json index cf1bb42b058b..1892e6b4e2dc 100644 --- a/x-pack/plugins/observability_solution/logs_shared/tsconfig.json +++ b/x-pack/plugins/observability_solution/logs_shared/tsconfig.json @@ -11,7 +11,9 @@ "types/**/*", "emotion.d.ts" ], - "exclude": ["target/**/*"], + "exclude": [ + "target/**/*" + ], "kbn_references": [ "@kbn/core", "@kbn/i18n", @@ -29,7 +31,6 @@ "@kbn/logging-mocks", "@kbn/kibana-react-plugin", "@kbn/test-subj-selector", - "@kbn/observability-shared-plugin", "@kbn/datemath", "@kbn/core-http-browser", "@kbn/ui-actions-plugin", @@ -53,5 +54,7 @@ "@kbn/embeddable-plugin", "@kbn/saved-search-plugin", "@kbn/spaces-plugin", + "@kbn/analytics", + "@kbn/usage-collection-plugin", ] } diff --git a/x-pack/plugins/observability_solution/observability_shared/common/entity/entity_types.ts b/x-pack/plugins/observability_solution/observability_shared/common/entity/entity_types.ts index db3e91fbf493..222780a1fc31 100644 --- a/x-pack/plugins/observability_solution/observability_shared/common/entity/entity_types.ts +++ b/x-pack/plugins/observability_solution/observability_shared/common/entity/entity_types.ts @@ -14,6 +14,7 @@ export const BUILT_IN_ENTITY_TYPES = { HOST: 'host', CONTAINER: 'container', SERVICE: 'service', + SERVICE_V2: 'built_in_services_from_ecs_data', KUBERNETES: { CLUSTER: createKubernetesEntity('cluster'), CONTAINER: createKubernetesEntity('container'), diff --git a/x-pack/plugins/observability_solution/observability_shared/common/field_names/elasticsearch.ts b/x-pack/plugins/observability_solution/observability_shared/common/field_names/elasticsearch.ts index e703cd487259..5569dac69b8b 100644 --- a/x-pack/plugins/observability_solution/observability_shared/common/field_names/elasticsearch.ts +++ b/x-pack/plugins/observability_solution/observability_shared/common/field_names/elasticsearch.ts @@ -146,6 +146,8 @@ export const PROFILE_ALLOC_SPACE = 'profile.alloc_space.bytes'; export const PROFILE_INUSE_OBJECTS = 'profile.inuse_objects.count'; export const PROFILE_INUSE_SPACE = 'profile.inuse_space.bytes'; +export const DATA_STREAM_TYPE = 'data_stream.type'; + export const ENTITY = 'entity'; export const ENTITY_ID = 'entity.id'; export const ENTITY_TYPE = 'entity.type'; diff --git a/x-pack/plugins/observability_solution/observability_shared/common/index.ts b/x-pack/plugins/observability_solution/observability_shared/common/index.ts index a8e26366ab4b..24d12362d7cf 100644 --- a/x-pack/plugins/observability_solution/observability_shared/common/index.ts +++ b/x-pack/plugins/observability_solution/observability_shared/common/index.ts @@ -128,15 +128,16 @@ export { PROFILE_ALLOC_SPACE, PROFILE_INUSE_OBJECTS, PROFILE_INUSE_SPACE, + DATA_STREAM_TYPE, ENTITY, - ENTITY_DEFINITION_ID, - ENTITY_DISPLAY_NAME, - ENTITY_FIRST_SEEN, ENTITY_ID, - ENTITY_LAST_SEEN, ENTITY_TYPE, - SOURCE_DATA_STREAM_TYPE, + ENTITY_LAST_SEEN, + ENTITY_FIRST_SEEN, + ENTITY_DISPLAY_NAME, + ENTITY_DEFINITION_ID, ENTITY_IDENTITY_FIELDS, + SOURCE_DATA_STREAM_TYPE, } from './field_names/elasticsearch'; export { diff --git a/x-pack/plugins/observability_solution/observability_shared/common/locators/apm/transaction_details_by_trace_id_locator.ts b/x-pack/plugins/observability_solution/observability_shared/common/locators/apm/transaction_details_by_trace_id_locator.ts index 2e461bc4f9d5..94fa1176c3ee 100644 --- a/x-pack/plugins/observability_solution/observability_shared/common/locators/apm/transaction_details_by_trace_id_locator.ts +++ b/x-pack/plugins/observability_solution/observability_shared/common/locators/apm/transaction_details_by_trace_id_locator.ts @@ -4,14 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import qs from 'query-string'; import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; -import type { SerializableRecord } from '@kbn/utility-types'; +import { + TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR, + type TransactionDetailsByTraceIdLocatorParams, +} from '@kbn/deeplinks-observability'; -export const TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR = 'TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR'; - -export interface TransactionDetailsByTraceIdLocatorParams extends SerializableRecord { - traceId: string; -} +export { TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR, type TransactionDetailsByTraceIdLocatorParams }; export type TransactionDetailsByTraceIdLocator = LocatorPublic<TransactionDetailsByTraceIdLocatorParams>; @@ -21,10 +21,15 @@ export class TransactionDetailsByTraceIdLocatorDefinition { public readonly id = TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR; - public readonly getLocation = async ({ traceId }: TransactionDetailsByTraceIdLocatorParams) => { + public readonly getLocation = async ({ + rangeFrom, + rangeTo, + traceId, + }: TransactionDetailsByTraceIdLocatorParams) => { + const params = { rangeFrom, rangeTo }; return { app: 'apm', - path: `/link-to/trace/${encodeURIComponent(traceId)}`, + path: `/link-to/trace/${encodeURIComponent(traceId)}?${qs.stringify(params)}`, state: {}, }; }; diff --git a/x-pack/plugins/observability_solution/observability_shared/common/trigger_ids.ts b/x-pack/plugins/observability_solution/observability_shared/common/trigger_ids.ts index 404aaab8781b..8a75472e0546 100644 --- a/x-pack/plugins/observability_solution/observability_shared/common/trigger_ids.ts +++ b/x-pack/plugins/observability_solution/observability_shared/common/trigger_ids.ts @@ -6,7 +6,6 @@ */ export enum ObservabilityTriggerId { - LogEntryContextMenu = 'logEntryContextMenu', ApmTransactionContextMenu = 'apmTransactionContextMenu', ApmErrorContextMenu = 'apmErrorContextMenu', } diff --git a/x-pack/plugins/observability_solution/observability_shared/tsconfig.json b/x-pack/plugins/observability_solution/observability_shared/tsconfig.json index f7b8a7ff6c57..15ae8d34c7f5 100644 --- a/x-pack/plugins/observability_solution/observability_shared/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_shared/tsconfig.json @@ -46,6 +46,7 @@ "@kbn/es-query", "@kbn/serverless", "@kbn/data-views-plugin", + "@kbn/deeplinks-observability", ], "exclude": ["target/**/*", ".storybook/**/*.js"] } diff --git a/x-pack/plugins/screenshotting/README.md b/x-pack/plugins/screenshotting/README.md deleted file mode 100644 index 8138be53dc8f..000000000000 --- a/x-pack/plugins/screenshotting/README.md +++ /dev/null @@ -1,156 +0,0 @@ -# Kibana Screenshotting - -This plugin provides functionality to take screenshots of the Kibana pages. -It uses Chromium and Puppeteer underneath to run the browser in headless mode. - -## Capabilities -- Canvas workpads screenshots. -- Dashboards screenshots. -- Expressions screenshots. -- PDF generation. -- Batch screenshotting. - -## Usage - -### Getting started -After listing the `screenshotting` plugin in your dependencies, the plugin will be intitalized on the setup stage. -The intitalization process downloads (if it is not already present) and verifies the Chromium build. - -The start contract exposes a public API to interact with the plugin. -Apart from the actual screenshotting functionality, it also provides a way for self-diagnostics. - -Here is an example of how you can take a screenshot of a Kibana URL. - -```typescript -import { lastValueFrom } from 'rxjs'; -import type { CoreSetup, Plugin } from 'src/core/server'; -import type { ScreenshottingStart } from 'x-pack/plugins/screenshotting/server'; - - -interface StartDeps { - screenshotting: ScreenshottingStart; -} - -class ExamplePlugin implements Plugin<void, void, void, StartDeps> { - setup({ http, getStartServices }: CoreSetup<StartDeps>) { - const router = http.createRouter(); - - router.get( - { - path: '/api/capture', - validate: { - query: schema.object({ - id: schema.string(), - }), - }, - }, - async (context, request, response) => { - const [, { screenshotting }] = await getStartServices(); - const { metrics, results } = await lastValueFrom( - screenshotting.getScreenshots({ - request, - urls: [`http://localhost/app/canvas#/workpad/workpad-${request.query.id}`], - }) - ); - - return response.ok({ - body: JSON.stringify({ - metrics, - image: results[0]?.screenshots[0]?.data.toString('base64'), - errors: results[0]?.renderErrors, - } as ScreenshottingExpressionResponse), - }); - } - ); - } - - start() {} -} - -export function plugin() { - return new ExamplePlugin(); -} -``` - -### API -Please use automatically generated API reference or generated TypeDoc comments to find the complete documentation. - -#### `getScreenshots(options): Observable` -Takes screenshots of multiple pages or an expression and returns an observable with the screenshotting results. - -The `options` parameter is an object with parameters of the screenshotting session. -Option | Required | Default | Description ---- | :---: | --- | --- -`browserTimezone` | no | _none_ | The browser timezone that will be emulated in the browser instance. This option should be used to keep timezone on server and client in sync. -`expression` | no | _none_ | An expression to capture screenshot of. Mutually exclusive with the `urls` parameter. -`format` | no | `'png'` | An output format. It can either be PDF or PNG. In case of capturing multiple URLs, all the screenshots will be combined into one document for PDF format. For PNG format, an array of screenshots will be returned. -`headers` | no | _none_ | Custom headers to be sent with each request. The headers will be used for authorization. -`input` | no | `undefined` | The expression input. -`layout` | no | `{}` | Page layout parameters describing characteristics of the capturing screenshot (e.g., dimensions, zoom, etc.). -`request` | no | _none_ | Kibana Request reference to extract headers from. -`timeouts` | no | _none_ | Timeouts for each phase of the screenshot. -`timeouts.openUrl` | no | (kibana.yml setting) | The timeout in milliseconds to allow the Chromium browser to wait for the "Loading…" screen to dismiss and find the initial data for the page. If the time is exceeded, a screenshot is captured showing the current page, and the result structure contains an error message. -`timeouts.renderComplete` | no | (kibana.yml setting) | The timeout in milliseconds to allow the Chromium browser to wait for all visualizations to fetch and render the data. If the time is exceeded, a screenshot is captured showing the current page, and the result structure contains an error message. -`timeouts.waitForElements` | no | (kibana.yml setting) | The timeout in milliseconds to allow the Chromium browser to wait for all visualization panels to load on the page. If the time is exceeded, a screenshot is captured showing the current page, and the result structure contains an error message. -`urls` | no | `[]` | The list or URL to take screenshots of. Every item can either be a string or a tuple containing a URL and a context. The contextual data can be gathered using the screenshot mode plugin. Mutually exclusive with the `expression` parameter. - -#### `diagnose(flags?: string[]): Observable` -Runs browser diagnostics. -The diagnostic implementation launches Chromium and emits the output in the resulting observable. - -There is a way to override some Chromium command line arguments using the `flags` parameter. - -### Configuration -Option | Default | Description ---- | --- | --- -`xpack.screenshotting.networkPolicy.enabled` | `true` | Capturing a screenshot from a Kibana page involves sending out requests for all the linked web assets. For example, a Markdown visualization can show an image from a remote server. -`xpack.screenshotting.networkPolicy.rules` | Allow http, https, ws, wss, and data. | A policy is specified as an array of objects that describe what to allow or deny based on a host or protocol. If a host or protocol is not specified, the rule matches any host or protocol. -`xpack.screenshotting.browser.autoDownload` | Depends on the `dist` parameter. | Flag to automatically download chromium distribution. -`xpack.screenshotting.browser.chromium.disableSandbox` | Defaults to `false` for all operating systems except Debian and Red Hat Linux, which use `true`. | It is recommended that you research the feasibility of enabling unprivileged user namespaces. An exception is if you are running Kibana in Docker because the container runs in a user namespace with the built-in seccomp/bpf filters. For more information, refer to [Chromium sandbox](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/linux/sandboxing.md). -`xpack.screenshotting.browser.chromium.proxy.enabled` | `false` | Enables the proxy for Chromium to use. -`xpack.screenshotting.browser.chromium.proxy.server` | _none_ | The uri for the proxy server. Providing the username and password for the proxy server via the uri is not supported. -`xpack.screenshotting.browser.chromium.proxy.bypass` | `[]` | An array of hosts that should not go through the proxy server and should use a direct connection instead. Examples of valid entries are "elastic.co", "*.elastic.co", ".elastic.co", ".elastic.co:5601". - -## How It Works -```mermaid -sequenceDiagram - participant User - participant Screenshotting - participant Browser - - User ->> Screenshotting: API call - Screenshotting ->> Browser: Launch browser - activate Browser - Screenshotting ->> Browser: Create page - Screenshotting ->> Browser: Set parameters - Note over Screenshotting,Browser: timezone - - Screenshotting ->> Browser: Open URL - Screenshotting ->> Browser: Set contextual data - Note over Screenshotting,Browser: custom context, screenshot mode flag - Browser ->> Screenshotting: Rendering - - Screenshotting ->> Browser: Wait for visualizations - Note over Screenshotting,Browser: poll for a number of DOM nodes to match <br> the number of dashboard elements - Screenshotting ->> Browser: Wait for render completion - Note over Screenshotting,Browser: poll for selectors indicating rendering completion - Browser ->> Screenshotting: Page is ready - - Screenshotting ->> Browser: Take screenshot - Browser ->> Screenshotting: Return PNG buffer - Screenshotting ->> User: Return screenshot -``` - -## Testing -### Integration -There is an example plugin that demonstrates integration with the screenshotting plugin. That plugin utilizes expression capturing. - -### Chromium Downloads -To download all Chromium browsers for all platforms and architectures: - -```bash -cd x-pack -npx gulp downloadChromium -``` - -This command is used to provision CI workspaces so that Chromium does not need to be downloaded for every CI run. diff --git a/x-pack/plugins/screenshotting/jest.config.js b/x-pack/plugins/screenshotting/jest.config.js deleted file mode 100644 index a02d667f86a1..000000000000 --- a/x-pack/plugins/screenshotting/jest.config.js +++ /dev/null @@ -1,15 +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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../../..', - roots: ['<rootDir>/x-pack/plugins/screenshotting'], - coverageDirectory: '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/screenshotting', - coverageReporters: ['text', 'html'], - collectCoverageFrom: ['<rootDir>/x-pack/plugins/screenshotting/server/**/*.{ts}'], -}; diff --git a/x-pack/plugins/screenshotting/jest.integration.config.js b/x-pack/plugins/screenshotting/jest.integration.config.js deleted file mode 100644 index 45a65c93c6af..000000000000 --- a/x-pack/plugins/screenshotting/jest.integration.config.js +++ /dev/null @@ -1,12 +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. - */ - -module.exports = { - preset: '@kbn/test/jest_integration', - rootDir: '../../..', - roots: ['<rootDir>/x-pack/plugins/screenshotting'], -}; diff --git a/x-pack/plugins/screenshotting/tsconfig.json b/x-pack/plugins/screenshotting/tsconfig.json deleted file mode 100644 index 5e37b84bbf2e..000000000000 --- a/x-pack/plugins/screenshotting/tsconfig.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "target/types", - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*", - "../../../typings/**/*" - ], - "kbn_references": [ - "@kbn/core", - { "path": "../../../src/setup_node_env/tsconfig.json" }, - "@kbn/expressions-plugin", - "@kbn/screenshot-mode-plugin", - "@kbn/cloud-plugin", - "@kbn/utility-types", - "@kbn/logging", - "@kbn/std", - "@kbn/i18n", - "@kbn/utils", - "@kbn/core-logging-server-mocks", - "@kbn/logging-mocks", - "@kbn/core-http-server", - "@kbn/core-plugins-server", - "@kbn/task-manager-plugin", - "@kbn/screenshotting-server", - ], - "exclude": [ - "target/**/*", - ] -} diff --git a/x-pack/plugins/search_notebooks/public/components/notebooks_button.tsx b/x-pack/plugins/search_notebooks/public/components/notebooks_button.tsx index 66fc50a03ddd..d71cade82aae 100644 --- a/x-pack/plugins/search_notebooks/public/components/notebooks_button.tsx +++ b/x-pack/plugins/search_notebooks/public/components/notebooks_button.tsx @@ -30,7 +30,7 @@ export const SearchNotebooksButton = ({ if (activeView) { return ( <EuiButton - color="success" + color="primary" fill onClick={onClick} size="s" @@ -47,7 +47,7 @@ export const SearchNotebooksButton = ({ } return ( <EuiButtonEmpty - color="success" + color="primary" onClick={onClick} size="s" iconType="documentation" diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.scss b/x-pack/plugins/serverless_search/public/application/components/overview.scss index fe5e83e5ccf4..195fbd2fdf01 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.scss +++ b/x-pack/plugins/serverless_search/public/application/components/overview.scss @@ -1,7 +1,3 @@ -.serverlessSearchHeaderSection { - background-color: $euiColorPrimary; -} - .serverlessSearchHeaderSection > div { padding-bottom: 0; padding-top: 0; @@ -13,4 +9,4 @@ .serverlessSearchCloudDetailsCopyPanel { word-break: break-all; -} \ No newline at end of file +} diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.tsx b/x-pack/plugins/serverless_search/public/application/components/overview.tsx index 678eadb6b5cb..d20706a6aa80 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/overview.tsx @@ -14,7 +14,6 @@ import { EuiIcon, EuiPageTemplate, EuiPanel, - EuiText, EuiBadge, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -96,9 +95,7 @@ export const ElasticsearchOverview = () => { return ( <EuiPageTemplate offset={0} grow restrictWidth data-test-subj="svlSearchOverviewPage"> <EuiPageTemplate.Section alignment="top" className="serverlessSearchHeaderSection"> - <EuiText color="ghost"> - <WelcomeBanner user={user} assetBasePath={assetBasePath} /> - </EuiText> + <WelcomeBanner user={user} assetBasePath={assetBasePath} /> </EuiPageTemplate.Section> <EuiPageTemplate.Section color="subdued" diff --git a/x-pack/plugins/stack_connectors/common/crowdstrike/schema.ts b/x-pack/plugins/stack_connectors/common/crowdstrike/schema.ts index d5d4154f985e..40e58613c69e 100644 --- a/x-pack/plugins/stack_connectors/common/crowdstrike/schema.ts +++ b/x-pack/plugins/stack_connectors/common/crowdstrike/schema.ts @@ -318,16 +318,16 @@ export const CrowdstrikeExecuteRTRResponseSchema = schema.object( schema.string(), schema.object( { - session_id: schema.maybe(schema.string()), - task_id: schema.maybe(schema.string()), - complete: schema.maybe(schema.boolean()), - stdout: schema.maybe(schema.string()), - stderr: schema.maybe(schema.string()), - base_command: schema.maybe(schema.string()), - aid: schema.maybe(schema.string()), - errors: schema.maybe(schema.arrayOf(schema.any())), - query_time: schema.maybe(schema.number()), - offline_queued: schema.maybe(schema.boolean()), + session_id: schema.string(), + task_id: schema.string(), + complete: schema.boolean(), + stdout: schema.string(), + stderr: schema.string(), + base_command: schema.string(), + aid: schema.string(), + errors: schema.arrayOf(schema.any()), + query_time: schema.number(), + offline_queued: schema.boolean(), }, { unknowns: 'allow' } ) @@ -337,9 +337,9 @@ export const CrowdstrikeExecuteRTRResponseSchema = schema.object( ), meta: schema.object( { - query_time: schema.maybe(schema.number()), - powered_by: schema.maybe(schema.string()), - trace_id: schema.maybe(schema.string()), + query_time: schema.number(), + powered_by: schema.string(), + trace_id: schema.string(), }, { unknowns: 'allow' } ), @@ -348,7 +348,5 @@ export const CrowdstrikeExecuteRTRResponseSchema = schema.object( { unknowns: 'allow' } ); -export type CrowdStrikeExecuteRTRResponse = typeof CrowdstrikeExecuteRTRResponseSchema; - // TODO: will be part of a next PR export const CrowdstrikeGetScriptsParamsSchema = schema.any({}); diff --git a/x-pack/plugins/stack_connectors/common/crowdstrike/types.ts b/x-pack/plugins/stack_connectors/common/crowdstrike/types.ts index 3c9cc15ea167..68f8275d7143 100644 --- a/x-pack/plugins/stack_connectors/common/crowdstrike/types.ts +++ b/x-pack/plugins/stack_connectors/common/crowdstrike/types.ts @@ -17,8 +17,8 @@ import { CrowdstrikeGetTokenResponseSchema, CrowdstrikeGetAgentsResponseSchema, RelaxedCrowdstrikeBaseApiResponseSchema, - CrowdstrikeInitRTRResponseSchema, CrowdstrikeInitRTRParamsSchema, + CrowdstrikeExecuteRTRResponseSchema, } from './schema'; export type CrowdstrikeConfig = TypeOf<typeof CrowdstrikeConfigSchema>; @@ -35,9 +35,10 @@ export type CrowdstrikeGetAgentOnlineStatusResponse = TypeOf< typeof CrowdstrikeGetAgentOnlineStatusResponseSchema >; export type CrowdstrikeGetTokenResponse = TypeOf<typeof CrowdstrikeGetTokenResponseSchema>; -export type CrowdstrikeInitRTRResponse = TypeOf<typeof CrowdstrikeInitRTRResponseSchema>; export type CrowdstrikeHostActionsParams = TypeOf<typeof CrowdstrikeHostActionsParamsSchema>; export type CrowdstrikeActionParams = TypeOf<typeof CrowdstrikeActionParamsSchema>; export type CrowdstrikeInitRTRParams = TypeOf<typeof CrowdstrikeInitRTRParamsSchema>; + +export type CrowdStrikeExecuteRTRResponse = TypeOf<typeof CrowdstrikeExecuteRTRResponseSchema>; diff --git a/x-pack/plugins/stack_connectors/common/inference/constants.ts b/x-pack/plugins/stack_connectors/common/inference/constants.ts index b795e54f5d32..c2f2e6713270 100644 --- a/x-pack/plugins/stack_connectors/common/inference/constants.ts +++ b/x-pack/plugins/stack_connectors/common/inference/constants.ts @@ -32,6 +32,9 @@ export enum ServiceProviderKeys { export const INFERENCE_CONNECTOR_ID = '.inference'; export enum SUB_ACTION { + UNIFIED_COMPLETION_ASYNC_ITERATOR = 'unified_completion_async_iterator', + UNIFIED_COMPLETION_STREAM = 'unified_completion_stream', + UNIFIED_COMPLETION = 'unified_completion', COMPLETION = 'completion', RERANK = 'rerank', TEXT_EMBEDDING = 'text_embedding', diff --git a/x-pack/plugins/stack_connectors/common/inference/schema.ts b/x-pack/plugins/stack_connectors/common/inference/schema.ts index 07b51cf9a5aa..c62e9782bb51 100644 --- a/x-pack/plugins/stack_connectors/common/inference/schema.ts +++ b/x-pack/plugins/stack_connectors/common/inference/schema.ts @@ -23,6 +23,176 @@ export const ChatCompleteParamsSchema = schema.object({ input: schema.string(), }); +// subset of OpenAI.ChatCompletionMessageParam https://github.com/openai/openai-node/blob/master/src/resources/chat/completions.ts +const AIMessage = schema.object({ + role: schema.string(), + content: schema.maybe(schema.string()), + name: schema.maybe(schema.string()), + tool_calls: schema.maybe( + schema.arrayOf( + schema.object({ + id: schema.string(), + function: schema.object({ + arguments: schema.maybe(schema.string()), + name: schema.maybe(schema.string()), + }), + type: schema.string(), + }) + ) + ), + tool_call_id: schema.maybe(schema.string()), +}); + +const AITool = schema.object({ + type: schema.string(), + function: schema.object({ + name: schema.string(), + description: schema.maybe(schema.string()), + parameters: schema.maybe(schema.recordOf(schema.string(), schema.any())), + }), +}); + +// subset of OpenAI.ChatCompletionCreateParamsBase https://github.com/openai/openai-node/blob/master/src/resources/chat/completions.ts +export const UnifiedChatCompleteParamsSchema = schema.object({ + body: schema.object({ + messages: schema.arrayOf(AIMessage, { defaultValue: [] }), + model: schema.maybe(schema.string()), + /** + * The maximum number of [tokens](/tokenizer) that can be generated in the chat + * completion. This value can be used to control + * [costs](https://openai.com/api/pricing/) for text generated via API. + * + * This value is now deprecated in favor of `max_completion_tokens`, and is not + * compatible with + * [o1 series models](https://platform.openai.com/docs/guides/reasoning). + */ + max_tokens: schema.maybe(schema.number()), + /** + * Developer-defined tags and values used for filtering completions in the + * [dashboard](https://platform.openai.com/chat-completions). + */ + metadata: schema.maybe(schema.recordOf(schema.string(), schema.string())), + /** + * How many chat completion choices to generate for each input message. Note that + * you will be charged based on the number of generated tokens across all of the + * choices. Keep `n` as `1` to minimize costs. + */ + n: schema.maybe(schema.number()), + /** + * Up to 4 sequences where the API will stop generating further tokens. + */ + stop: schema.maybe( + schema.nullable(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])) + ), + /** + * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + * make the output more random, while lower values like 0.2 will make it more + * focused and deterministic. + * + * We generally recommend altering this or `top_p` but not both. + */ + temperature: schema.maybe(schema.number()), + /** + * Controls which (if any) tool is called by the model. `none` means the model will + * not call any tool and instead generates a message. `auto` means the model can + * pick between generating a message or calling one or more tools. `required` means + * the model must call one or more tools. Specifying a particular tool via + * `{"type": "function", "function": {"name": "my_function"}}` forces the model to + * call that tool. + * + * `none` is the default when no tools are present. `auto` is the default if tools + * are present. + */ + tool_choice: schema.maybe( + schema.oneOf([ + schema.string(), + schema.object({ + type: schema.string(), + function: schema.object({ + name: schema.string(), + }), + }), + ]) + ), + /** + * A list of tools the model may call. Currently, only functions are supported as a + * tool. Use this to provide a list of functions the model may generate JSON inputs + * for. A max of 128 functions are supported. + */ + tools: schema.maybe(schema.arrayOf(AITool)), + /** + * An alternative to sampling with temperature, called nucleus sampling, where the + * model considers the results of the tokens with top_p probability mass. So 0.1 + * means only the tokens comprising the top 10% probability mass are considered. + * + * We generally recommend altering this or `temperature` but not both. + */ + top_p: schema.maybe(schema.number()), + /** + * A unique identifier representing your end-user, which can help OpenAI to monitor + * and detect abuse. + * [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids). + */ + user: schema.maybe(schema.string()), + }), + // abort signal from client + signal: schema.maybe(schema.any()), +}); + +export const UnifiedChatCompleteResponseSchema = schema.object({ + id: schema.string(), + choices: schema.arrayOf( + schema.object({ + finish_reason: schema.maybe( + schema.nullable( + schema.oneOf([ + schema.literal('stop'), + schema.literal('length'), + schema.literal('tool_calls'), + schema.literal('content_filter'), + schema.literal('function_call'), + ]) + ) + ), + index: schema.maybe(schema.number()), + message: schema.object({ + content: schema.maybe(schema.nullable(schema.string())), + refusal: schema.maybe(schema.nullable(schema.string())), + role: schema.maybe(schema.string()), + tool_calls: schema.maybe( + schema.arrayOf( + schema.object({ + id: schema.maybe(schema.string()), + index: schema.maybe(schema.number()), + function: schema.maybe( + schema.object({ + arguments: schema.maybe(schema.string()), + name: schema.maybe(schema.string()), + }) + ), + type: schema.maybe(schema.string()), + }), + { defaultValue: [] } + ) + ), + }), + }), + { defaultValue: [] } + ), + created: schema.maybe(schema.number()), + model: schema.maybe(schema.string()), + object: schema.maybe(schema.string()), + usage: schema.maybe( + schema.nullable( + schema.object({ + completion_tokens: schema.maybe(schema.number()), + prompt_tokens: schema.maybe(schema.number()), + total_tokens: schema.maybe(schema.number()), + }) + ) + ), +}); + export const ChatCompleteResponseSchema = schema.arrayOf( schema.object({ result: schema.string(), @@ -66,3 +236,12 @@ export const TextEmbeddingResponseSchema = schema.arrayOf( ); export const StreamingResponseSchema = schema.stream(); + +// Run action schema +export const DashboardActionParamsSchema = schema.object({ + dashboardId: schema.string(), +}); + +export const DashboardActionResponseSchema = schema.object({ + available: schema.boolean(), +}); diff --git a/x-pack/plugins/stack_connectors/common/inference/types.ts b/x-pack/plugins/stack_connectors/common/inference/types.ts index d8b846ce1942..1593429792e0 100644 --- a/x-pack/plugins/stack_connectors/common/inference/types.ts +++ b/x-pack/plugins/stack_connectors/common/inference/types.ts @@ -18,12 +18,19 @@ import { SparseEmbeddingResponseSchema, TextEmbeddingParamsSchema, TextEmbeddingResponseSchema, + UnifiedChatCompleteParamsSchema, + UnifiedChatCompleteResponseSchema, + DashboardActionParamsSchema, + DashboardActionResponseSchema, } from './schema'; import { ConfigProperties } from '../dynamic_config/types'; export type Config = TypeOf<typeof ConfigSchema>; export type Secrets = TypeOf<typeof SecretsSchema>; +export type UnifiedChatCompleteParams = TypeOf<typeof UnifiedChatCompleteParamsSchema>; +export type UnifiedChatCompleteResponse = TypeOf<typeof UnifiedChatCompleteResponseSchema>; + export type ChatCompleteParams = TypeOf<typeof ChatCompleteParamsSchema>; export type ChatCompleteResponse = TypeOf<typeof ChatCompleteResponseSchema>; @@ -38,6 +45,9 @@ export type TextEmbeddingResponse = TypeOf<typeof TextEmbeddingResponseSchema>; export type StreamingResponse = TypeOf<typeof StreamingResponseSchema>; +export type DashboardActionParams = TypeOf<typeof DashboardActionParamsSchema>; +export type DashboardActionResponse = TypeOf<typeof DashboardActionResponseSchema>; + export type FieldsConfiguration = Record<string, ConfigProperties>; export interface InferenceProvider { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/constants.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/constants.tsx index 8427caaf49ff..1b635ca8fe88 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/constants.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/constants.tsx @@ -25,13 +25,27 @@ export const DEFAULT_TEXT_EMBEDDING_BODY = { inputType: 'ingest', }; +export const DEFAULT_UNIFIED_CHAT_COMPLETE_BODY = { + body: { + messages: [ + { + role: 'user', + content: 'Hello world', + }, + ], + }, +}; + export const DEFAULTS_BY_TASK_TYPE: Record<string, unknown> = { [SUB_ACTION.COMPLETION]: DEFAULT_CHAT_COMPLETE_BODY, + [SUB_ACTION.UNIFIED_COMPLETION]: DEFAULT_UNIFIED_CHAT_COMPLETE_BODY, + [SUB_ACTION.UNIFIED_COMPLETION_STREAM]: DEFAULT_UNIFIED_CHAT_COMPLETE_BODY, + [SUB_ACTION.UNIFIED_COMPLETION_ASYNC_ITERATOR]: DEFAULT_UNIFIED_CHAT_COMPLETE_BODY, [SUB_ACTION.RERANK]: DEFAULT_RERANK_BODY, [SUB_ACTION.SPARSE_EMBEDDING]: DEFAULT_SPARSE_EMBEDDING_BODY, [SUB_ACTION.TEXT_EMBEDDING]: DEFAULT_TEXT_EMBEDDING_BODY, }; -export const DEFAULT_TASK_TYPE = 'completion'; +export const DEFAULT_TASK_TYPE = 'unified_completion'; export const DEFAULT_PROVIDER = 'elasticsearch'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.test.tsx index 76dc50a316e6..b67264674aeb 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.test.tsx @@ -44,8 +44,16 @@ describe('OpenAI action params validation', () => { subActionParams: { input: ['message test'], query: 'foobar' }, }, { - subAction: SUB_ACTION.COMPLETION, - subActionParams: { input: 'message test' }, + subAction: SUB_ACTION.UNIFIED_COMPLETION, + subActionParams: { body: { messages: [{ role: 'user', content: 'What is Elastic?' }] } }, + }, + { + subAction: SUB_ACTION.UNIFIED_COMPLETION_STREAM, + subActionParams: { body: { messages: [{ role: 'user', content: 'What is Elastic?' }] } }, + }, + { + subAction: SUB_ACTION.UNIFIED_COMPLETION_ASYNC_ITERATOR, + subActionParams: { body: { messages: [{ role: 'user', content: 'What is Elastic?' }] } }, }, { subAction: SUB_ACTION.TEXT_EMBEDDING, @@ -55,6 +63,10 @@ describe('OpenAI action params validation', () => { subAction: SUB_ACTION.SPARSE_EMBEDDING, subActionParams: { input: 'message test' }, }, + { + subAction: SUB_ACTION.COMPLETION, + subActionParams: { input: 'message test' }, + }, ])( 'validation succeeds when params are valid for subAction $subAction', async ({ subAction, subActionParams }) => { @@ -63,19 +75,25 @@ describe('OpenAI action params validation', () => { subActionParams, }; expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { input: [], subAction: [], inputType: [], query: [] }, + errors: { body: [], input: [], subAction: [], inputType: [], query: [] }, }); } ); test('params validation fails when params is a wrong object', async () => { const actionParams = { - subAction: SUB_ACTION.COMPLETION, + subAction: SUB_ACTION.UNIFIED_COMPLETION, subActionParams: { body: 'message {test}' }, }; expect(await actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { input: ['Input is required.'], inputType: [], query: [], subAction: [] }, + errors: { + body: ['Messages is required.'], + inputType: [], + query: [], + subAction: [], + input: [], + }, }); }); @@ -86,6 +104,7 @@ describe('OpenAI action params validation', () => { expect(await actionTypeModel.validateParams(actionParams)).toEqual({ errors: { + body: [], input: [], inputType: [], query: [], @@ -102,6 +121,7 @@ describe('OpenAI action params validation', () => { expect(await actionTypeModel.validateParams(actionParams)).toEqual({ errors: { + body: [], input: [], inputType: [], query: [], @@ -118,6 +138,7 @@ describe('OpenAI action params validation', () => { expect(await actionTypeModel.validateParams(actionParams)).toEqual({ errors: { + body: [], input: ['Input is required.', 'Input does not have a valid Array format.'], inputType: [], query: ['Query is required.'], @@ -134,6 +155,7 @@ describe('OpenAI action params validation', () => { expect(await actionTypeModel.validateParams(actionParams)).toEqual({ errors: { + body: [], input: [], inputType: ['Input type is required.'], query: [], diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.tsx index e16d03306c16..388da0556801 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.tsx @@ -19,6 +19,7 @@ import { InferenceActionParams, InferenceConnector } from './types'; interface ValidationErrors { subAction: string[]; input: string[]; + body: string[]; // rerank only query: string[]; // text_embedding only @@ -40,14 +41,28 @@ export function getConnectorType(): InferenceConnector { const translations = await import('./translations'); const errors: ValidationErrors = { input: [], + body: [], subAction: [], inputType: [], query: [], }; if ( - subAction === SUB_ACTION.RERANK || + subAction === SUB_ACTION.UNIFIED_COMPLETION || + subAction === SUB_ACTION.UNIFIED_COMPLETION_STREAM || + subAction === SUB_ACTION.UNIFIED_COMPLETION_ASYNC_ITERATOR + ) { + if ( + !Array.isArray(subActionParams.body.messages) || + !subActionParams.body.messages.length + ) { + errors.body.push(translations.getRequiredMessage('Messages')); + } + } + + if ( subAction === SUB_ACTION.COMPLETION || + subAction === SUB_ACTION.RERANK || subAction === SUB_ACTION.TEXT_EMBEDDING || subAction === SUB_ACTION.SPARSE_EMBEDDING ) { @@ -76,10 +91,13 @@ export function getConnectorType(): InferenceConnector { errors.subAction.push(translations.getRequiredMessage('Action')); } else if ( ![ - SUB_ACTION.COMPLETION, + SUB_ACTION.UNIFIED_COMPLETION, + SUB_ACTION.UNIFIED_COMPLETION_STREAM, + SUB_ACTION.UNIFIED_COMPLETION_ASYNC_ITERATOR, SUB_ACTION.SPARSE_EMBEDDING, SUB_ACTION.RERANK, SUB_ACTION.TEXT_EMBEDDING, + SUB_ACTION.COMPLETION, ].includes(subAction) ) { errors.subAction.push(translations.INVALID_ACTION); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/params.test.tsx index 49773edc2246..ba094ec64f6b 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/params.test.tsx @@ -15,8 +15,8 @@ describe('Inference Params Fields renders', () => { const { getByTestId } = render( <ParamsFields actionParams={{ - subAction: SUB_ACTION.COMPLETION, - subActionParams: { input: 'What is Elastic?' }, + subAction: SUB_ACTION.UNIFIED_COMPLETION, + subActionParams: { body: { messages: [{ role: 'user', content: 'What is Elastic?' }] } }, }} actionConnector={{ actionTypeId: '.inference', @@ -35,8 +35,11 @@ describe('Inference Params Fields renders', () => { index={0} /> ); - expect(getByTestId('inferenceInput')).toBeInTheDocument(); - expect(getByTestId('inferenceInput')).toHaveProperty('value', 'What is Elastic?'); + expect(getByTestId('inference-bodyJsonEditor')).toBeInTheDocument(); + expect(getByTestId('bodyJsonEditor')).toHaveProperty( + 'value', + `{\"messages\":[{\"role\":\"user\",\"content\":\"What is Elastic?\"}]}` + ); }); test.each(['openai', 'googleaistudio'])( @@ -76,15 +79,25 @@ describe('Inference Params Fields renders', () => { /> ); expect(editAction).toHaveBeenCalledTimes(2); - expect(editAction).toHaveBeenCalledWith('subAction', SUB_ACTION.COMPLETION, 0); if (provider === 'openai') { + expect(editAction).toHaveBeenCalledWith('subAction', SUB_ACTION.UNIFIED_COMPLETION, 0); expect(editAction).toHaveBeenCalledWith( 'subActionParams', - { input: 'What is Elastic?' }, + { + body: { + messages: [ + { + content: 'Hello world', + role: 'user', + }, + ], + }, + }, 0 ); } if (provider === 'googleaistudio') { + expect(editAction).toHaveBeenCalledWith('subAction', SUB_ACTION.COMPLETION, 0); expect(editAction).toHaveBeenCalledWith( 'subActionParams', { input: 'What is Elastic?' }, diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/params.tsx index c24fff24c33f..be162e70493b 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/params.tsx @@ -12,11 +12,13 @@ import { } from '@kbn/triggers-actions-ui-plugin/public'; import { EuiTextArea, EuiFormRow, EuiSpacer, EuiSelect } from '@elastic/eui'; import type { RuleFormParamsErrors } from '@kbn/response-ops-rule-form'; +import { ActionVariable } from '@kbn/alerting-types'; import { ChatCompleteParams, RerankParams, SparseEmbeddingParams, TextEmbeddingParams, + UnifiedChatCompleteParams, } from '../../../common/inference/types'; import { DEFAULTS_BY_TASK_TYPE } from './constants'; import * as i18n from './translations'; @@ -25,28 +27,38 @@ import { InferenceActionConnector, InferenceActionParams } from './types'; const InferenceServiceParamsFields: React.FunctionComponent< ActionParamsProps<InferenceActionParams> -> = ({ actionParams, editAction, index, errors, actionConnector }) => { +> = ({ actionParams, editAction, index, errors, actionConnector, messageVariables }) => { const { subAction, subActionParams } = actionParams; - const { taskType } = (actionConnector as unknown as InferenceActionConnector).config; + const { taskType, provider } = (actionConnector as unknown as InferenceActionConnector).config; useEffect(() => { if (!subAction) { - editAction('subAction', taskType, index); + editAction( + 'subAction', + provider === 'openai' && taskType === 'completion' + ? SUB_ACTION.UNIFIED_COMPLETION + : taskType, + index + ); } - }, [editAction, index, subAction, taskType]); + }, [editAction, index, provider, subAction, taskType]); useEffect(() => { if (!subActionParams) { editAction( 'subActionParams', { - ...(DEFAULTS_BY_TASK_TYPE[taskType] ?? {}), + ...(DEFAULTS_BY_TASK_TYPE[ + provider === 'openai' && taskType === 'completion' + ? SUB_ACTION.UNIFIED_COMPLETION + : taskType + ] ?? {}), }, index ); } - }, [editAction, index, subActionParams, taskType]); + }, [editAction, index, provider, subActionParams, taskType]); const editSubActionParams = useCallback( (params: Partial<InferenceActionParams['subActionParams']>) => { @@ -55,6 +67,28 @@ const InferenceServiceParamsFields: React.FunctionComponent< [editAction, index, subActionParams] ); + if (subAction === SUB_ACTION.UNIFIED_COMPLETION) { + return ( + <UnifiedCompletionParamsFields + errors={errors} + messageVariables={messageVariables} + editSubActionParams={editSubActionParams} + subActionParams={subActionParams as UnifiedChatCompleteParams} + /> + ); + } + + if (subAction === SUB_ACTION.UNIFIED_COMPLETION_ASYNC_ITERATOR) { + return ( + <UnifiedCompletionParamsFields + errors={errors} + messageVariables={messageVariables} + editSubActionParams={editSubActionParams} + subActionParams={subActionParams as UnifiedChatCompleteParams} + /> + ); + } + if (subAction === SUB_ACTION.COMPLETION) { return ( <CompletionParamsFields @@ -119,6 +153,36 @@ const InferenceInput: React.FunctionComponent<{ ); }; +const UnifiedCompletionParamsFields: React.FunctionComponent<{ + subActionParams: UnifiedChatCompleteParams; + errors: RuleFormParamsErrors; + editSubActionParams: (params: Partial<InferenceActionParams['subActionParams']>) => void; + messageVariables: ActionVariable[] | undefined; +}> = ({ subActionParams, editSubActionParams, errors, messageVariables }) => { + const { body } = subActionParams ?? {}; + + return ( + <> + <JsonEditorWithMessageVariables + messageVariables={messageVariables} + paramsProperty={'body'} + inputTargetValue={JSON.stringify(body)} + label={i18n.BODY} + errors={errors.body as string[]} + onDocumentsChange={(json: string) => { + editSubActionParams({ body: JSON.parse(json) }); + }} + onBlur={() => { + if (!subActionParams.body) { + editSubActionParams({ body: { messages: [] } }); + } + }} + dataTestSubj="inference-bodyJsonEditor" + /> + </> + ); +}; + const CompletionParamsFields: React.FunctionComponent<{ subActionParams: ChatCompleteParams; errors: RuleFormParamsErrors; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts index 1bd55793bc46..1756e213a1a7 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts @@ -13,9 +13,16 @@ import { RerankParams, SparseEmbeddingParams, TextEmbeddingParams, + UnifiedChatCompleteParams, } from '../../../common/inference/types'; export type InferenceActionParams = + | { subAction: SUB_ACTION.UNIFIED_COMPLETION_STREAM; subActionParams: UnifiedChatCompleteParams } + | { subAction: SUB_ACTION.UNIFIED_COMPLETION; subActionParams: UnifiedChatCompleteParams } + | { + subAction: SUB_ACTION.UNIFIED_COMPLETION_ASYNC_ITERATOR; + subActionParams: UnifiedChatCompleteParams; + } | { subAction: SUB_ACTION.COMPLETION; subActionParams: ChatCompleteParams } | { subAction: SUB_ACTION.RERANK; subActionParams: RerankParams } | { subAction: SUB_ACTION.SPARSE_EMBEDDING; subActionParams: SparseEmbeddingParams } diff --git a/x-pack/plugins/stack_connectors/server/connector_types/crowdstrike/crowdstrike.ts b/x-pack/plugins/stack_connectors/server/connector_types/crowdstrike/crowdstrike.ts index 14b38b414eb3..9bc53c58aa19 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/crowdstrike/crowdstrike.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/crowdstrike/crowdstrike.ts @@ -22,6 +22,7 @@ import type { CrowdstrikeGetTokenResponse, CrowdstrikeGetAgentOnlineStatusResponse, RelaxedCrowdstrikeBaseApiResponse, + CrowdStrikeExecuteRTRResponse, } from '../../../common/crowdstrike/types'; import { CrowdstrikeHostActionsParamsSchema, @@ -31,7 +32,6 @@ import { CrowdstrikeRTRCommandParamsSchema, CrowdstrikeExecuteRTRResponseSchema, CrowdstrikeGetScriptsParamsSchema, - CrowdStrikeExecuteRTRResponse, CrowdstrikeApiDoNotValidateResponsesSchema, CrowdstrikeGetTokenResponseSchema, } from '../../../common/crowdstrike/schema'; @@ -292,15 +292,9 @@ export class CrowdstrikeConnector extends SubActionConnector< payload: { command: string; endpoint_ids: string[]; - overwriteUrl?: 'batchExecuteRTR' | 'batchActiveResponderExecuteRTR' | 'batchAdminExecuteRTR'; }, connectorUsageCollector: ConnectorUsageCollector ): Promise<CrowdStrikeExecuteRTRResponse> => { - // Some commands are only available in specific API endpoints, however there's an additional requirement check for the command's argument - // Eg. runscript command is available with the batchExecuteRTR endpoint, but if it goes with --Raw parameter, it should go to batchAdminExecuteRTR endpoint - // This overwrite value will be coming from kibana response actions api - const csUrl = payload.overwriteUrl ? this.urls[payload.overwriteUrl] : url; - const batchId = await this.crowdStrikeSessionManager.initializeSession( { endpoint_ids: payload.endpoint_ids }, connectorUsageCollector @@ -313,7 +307,7 @@ export class CrowdstrikeConnector extends SubActionConnector< } return await this.crowdstrikeApiRequest<CrowdStrikeExecuteRTRResponse>( { - url: csUrl, + url, method: 'post', data: { base_command: baseCommand, @@ -335,7 +329,6 @@ export class CrowdstrikeConnector extends SubActionConnector< payload: { command: string; endpoint_ids: string[]; - overwriteUrl?: 'batchActiveResponderExecuteRTR' | 'batchAdminExecuteRTR'; }, connectorUsageCollector: ConnectorUsageCollector ): Promise<CrowdStrikeExecuteRTRResponse> { @@ -351,7 +344,6 @@ export class CrowdstrikeConnector extends SubActionConnector< payload: { command: string; endpoint_ids: string[]; - overwriteUrl?: 'batchAdminExecuteRTR'; }, connectorUsageCollector: ConnectorUsageCollector ): Promise<CrowdStrikeExecuteRTRResponse> { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/inference/helpers.ts b/x-pack/plugins/stack_connectors/server/connector_types/inference/helpers.ts new file mode 100644 index 000000000000..7c6bfab9c639 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/inference/helpers.ts @@ -0,0 +1,111 @@ +/* + * 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 { last, lastValueFrom, map, merge, Observable, scan, share } from 'rxjs'; +import type { Readable } from 'node:stream'; +import { createParser } from 'eventsource-parser'; +import { UnifiedChatCompleteResponse } from '../../../common/inference/types'; + +// TODO: Extract to the common package with appex-ai +export function eventSourceStreamIntoObservable(readable: Readable) { + return new Observable<string>((subscriber) => { + const parser = createParser({ + onEvent: (event) => { + subscriber.next(event.data); + }, + }); + + async function processStream() { + for await (const chunk of readable) { + parser.feed(chunk.toString()); + } + } + + processStream().then( + () => { + subscriber.complete(); + }, + (error) => { + subscriber.error(error); + } + ); + }); +} + +export function chunksIntoMessage(obs$: Observable<UnifiedChatCompleteResponse>) { + const shared$ = obs$.pipe(share()); + + return lastValueFrom( + merge( + shared$, + shared$.pipe( + scan( + (prev, chunk) => { + if (chunk.choices.length > 0 && !chunk.usage) { + prev.choices[0].message.content += chunk.choices[0].message.content ?? ''; + + chunk.choices[0].message.tool_calls?.forEach((toolCall) => { + if (toolCall.index !== undefined) { + const prevToolCallLength = prev.choices[0].message.tool_calls?.length ?? 0; + if (prevToolCallLength - 1 !== toolCall.index) { + if (!prev.choices[0].message.tool_calls) { + prev.choices[0].message.tool_calls = []; + } + prev.choices[0].message.tool_calls.push({ + function: { + name: '', + arguments: '', + }, + id: '', + }); + } + const prevToolCall = prev.choices[0].message.tool_calls[toolCall.index]; + + if (toolCall.function?.name) { + prevToolCall.function.name += toolCall.function?.name; + } + if (toolCall.function?.arguments) { + prevToolCall.function.arguments += toolCall.function?.arguments; + } + if (toolCall.id) { + prevToolCall.id += toolCall.id; + } + if (toolCall.type) { + prevToolCall.type = toolCall.type; + } + } + }); + } else if (chunk.usage) { + prev.usage = chunk.usage; + } + return { ...prev, id: chunk.id, model: chunk.model }; + }, + { + choices: [ + { + message: { + content: '', + role: 'assistant', + }, + }, + ], + object: 'chat.completion', + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any + ), + last(), + map((concatenatedChunk): UnifiedChatCompleteResponse => { + // TODO: const validatedToolCalls = validateToolCalls(concatenatedChunk.choices[0].message.tool_calls); + if (concatenatedChunk.choices[0].message.content === '') { + concatenatedChunk.choices[0].message.content = null; + } + return concatenatedChunk; + }) + ) + ) + ); +} diff --git a/x-pack/plugins/stack_connectors/server/connector_types/inference/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/inference/index.ts index 18af48bc18a5..5af6773d15fe 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/inference/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/inference/index.ts @@ -161,7 +161,10 @@ export const configValidator = (configObject: Config, validatorServices: Validat ); } - if (!Object.keys(SUB_ACTION).includes(taskType.toUpperCase())) { + if ( + !taskType.includes('completion') && + !Object.keys(SUB_ACTION).includes(taskType.toUpperCase()) + ) { throw new Error( `Task type is not supported${ taskType && taskType.length ? `: ${taskType}` : `` diff --git a/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.test.ts index a79bd0360598..4aa28d2952db 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.test.ts @@ -9,7 +9,7 @@ import { InferenceConnector } from './inference'; import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { actionsMock } from '@kbn/actions-plugin/server/mocks'; -import { PassThrough, Transform } from 'stream'; +import { Readable, Transform } from 'stream'; import {} from '@kbn/actions-plugin/server/types'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { InferenceInferenceResponse } from '@elastic/elasticsearch/lib/api/types'; @@ -29,7 +29,7 @@ describe('InferenceConnector', () => { ], }; - describe('performApiCompletion', () => { + describe('performApiUnifiedCompletion', () => { const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; beforeEach(() => { @@ -60,28 +60,44 @@ describe('InferenceConnector', () => { }); it('uses the completion task_type is supplied', async () => { - const response = await connector.performApiCompletion({ - input: 'What is Elastic?', + const stream = Readable.from([ + `data: {"id":"chatcmpl-AbLKRuRMZCAcMMQdl96KMTUgAfZNg","choices":[{"delta":{"content":" you"},"index":0}],"model":"gpt-4o-2024-08-06","object":"chat.completion.chunk"}\n\n`, + `data: [DONE]\n\n`, + ]); + mockEsClient.transport.request.mockResolvedValue(stream); + + const response = await connector.performApiUnifiedCompletion({ + body: { messages: [{ content: 'What is Elastic?', role: 'user' }] }, }); - expect(mockEsClient.inference.inference).toBeCalledTimes(1); - expect(mockEsClient.inference.inference).toHaveBeenCalledWith( + expect(mockEsClient.transport.request).toBeCalledTimes(1); + expect(mockEsClient.transport.request).toHaveBeenCalledWith( { - inference_id: 'test', - input: 'What is Elastic?', - task_type: 'completion', + body: { + messages: [ + { + content: 'What is Elastic?', + role: 'user', + }, + ], + n: undefined, + }, + method: 'POST', + path: '_inference/completion/test/_unified', }, - { asStream: false } + { asStream: true } ); - expect(response).toEqual(mockResponse.completion); + expect(response.choices[0].message.content).toEqual(' you'); }); it('errors during API calls are properly handled', async () => { // @ts-ignore - mockEsClient.inference.inference = mockError; + mockEsClient.transport.request = mockError; - await expect(connector.performApiCompletion({ input: 'What is Elastic?' })).rejects.toThrow( - 'API Error' - ); + await expect( + connector.performApiUnifiedCompletion({ + body: { messages: [{ content: 'What is Elastic?', role: 'user' }] }, + }) + ).rejects.toThrow('API Error'); }); }); @@ -223,6 +239,7 @@ describe('InferenceConnector', () => { }; beforeEach(() => { + jest.clearAllMocks(); // @ts-ignore mockStream(); }); @@ -238,7 +255,7 @@ describe('InferenceConnector', () => { }, provider: 'elasticsearch', taskType: 'completion', - inferenceId: '', + inferenceId: 'test', taskTypeConfig: {}, }, secrets: { providerSecrets: {} }, @@ -247,13 +264,23 @@ describe('InferenceConnector', () => { }); it('the API call is successful with correct request parameters', async () => { - await connector.performApiCompletionStream({ input: 'Hello world' }); - expect(mockEsClient.inference.inference).toBeCalledTimes(1); - expect(mockEsClient.inference.inference).toHaveBeenCalledWith( + await connector.performApiUnifiedCompletionStream({ + body: { messages: [{ content: 'Hello world', role: 'user' }] }, + }); + expect(mockEsClient.transport.request).toBeCalledTimes(1); + expect(mockEsClient.transport.request).toHaveBeenCalledWith( { - inference_id: '', - input: 'Hello world', - task_type: 'completion', + body: { + messages: [ + { + content: 'Hello world', + role: 'user', + }, + ], + n: undefined, + }, + method: 'POST', + path: '_inference/completion/test/_unified', }, { asStream: true } ); @@ -261,32 +288,42 @@ describe('InferenceConnector', () => { it('signal is properly passed to streamApi', async () => { const signal = jest.fn() as unknown as AbortSignal; - await connector.performApiCompletionStream({ input: 'Hello world', signal }); + await connector.performApiUnifiedCompletionStream({ + body: { messages: [{ content: 'Hello world', role: 'user' }] }, + signal, + }); - expect(mockEsClient.inference.inference).toHaveBeenCalledWith( + expect(mockEsClient.transport.request).toHaveBeenCalledWith( { - inference_id: '', - input: 'Hello world', - task_type: 'completion', + body: { messages: [{ content: 'Hello world', role: 'user' }], n: undefined }, + method: 'POST', + path: '_inference/completion/test/_unified', }, - { asStream: true, signal } + { asStream: true } ); }); it('errors during API calls are properly handled', async () => { // @ts-ignore - mockEsClient.inference.inference = mockError; + mockEsClient.transport.request = mockError; await expect( - connector.performApiCompletionStream({ input: 'What is Elastic?' }) + connector.performApiUnifiedCompletionStream({ + body: { messages: [{ content: 'What is Elastic?', role: 'user' }] }, + }) ).rejects.toThrow('API Error'); }); it('responds with a readable stream', async () => { - const response = await connector.performApiCompletionStream({ - input: 'What is Elastic?', + const stream = Readable.from([ + `data: {"id":"chatcmpl-AbLKRuRMZCAcMMQdl96KMTUgAfZNg","choices":[{"delta":{"content":" you"},"index":0}],"model":"gpt-4o-2024-08-06","object":"chat.completion.chunk"}\n\n`, + `data: [DONE]\n\n`, + ]); + mockEsClient.transport.request.mockResolvedValue(stream); + const response = await connector.performApiUnifiedCompletionStream({ + body: { messages: [{ content: 'What is Elastic?', role: 'user' }] }, }); - expect(response instanceof PassThrough).toEqual(true); + expect(response instanceof Readable).toEqual(true); }); }); }); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.ts b/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.ts index d9aa4bf044e1..d6c9af0e1365 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.ts @@ -6,36 +6,44 @@ */ import { ServiceParams, SubActionConnector } from '@kbn/actions-plugin/server'; - -import { PassThrough, Stream } from 'stream'; -import { IncomingMessage } from 'http'; +import { Stream } from 'openai/streaming'; +import { Readable } from 'stream'; import { AxiosError } from 'axios'; import { InferenceInferenceRequest, InferenceInferenceResponse, - InferenceTaskType, } from '@elastic/elasticsearch/lib/api/types'; +import { ConnectorUsageCollector } from '@kbn/actions-plugin/server/usage'; +import { filter, from, identity, map, mergeMap, Observable, tap } from 'rxjs'; +import OpenAI from 'openai'; +import { ChatCompletionChunk } from 'openai/resources'; import { ChatCompleteParamsSchema, RerankParamsSchema, SparseEmbeddingParamsSchema, TextEmbeddingParamsSchema, + UnifiedChatCompleteParamsSchema, } from '../../../common/inference/schema'; import { Config, Secrets, - ChatCompleteParams, - ChatCompleteResponse, - StreamingResponse, RerankParams, RerankResponse, SparseEmbeddingParams, SparseEmbeddingResponse, TextEmbeddingParams, TextEmbeddingResponse, + UnifiedChatCompleteParams, + UnifiedChatCompleteResponse, + DashboardActionParams, + DashboardActionResponse, + ChatCompleteParams, + ChatCompleteResponse, } from '../../../common/inference/types'; import { SUB_ACTION } from '../../../common/inference/constants'; +import { initDashboard } from '../lib/gen_ai/create_gen_ai_dashboard'; +import { chunksIntoMessage, eventSourceStreamIntoObservable } from './helpers'; export class InferenceConnector extends SubActionConnector<Config, Secrets> { // Not using Axios @@ -60,10 +68,25 @@ export class InferenceConnector extends SubActionConnector<Config, Secrets> { } private registerSubActions() { + // non-streaming unified completion task this.registerSubAction({ - name: SUB_ACTION.COMPLETION, - method: 'performApiCompletion', - schema: ChatCompleteParamsSchema, + name: SUB_ACTION.UNIFIED_COMPLETION, + method: 'performApiUnifiedCompletion', + schema: UnifiedChatCompleteParamsSchema, + }); + + // streaming unified completion task + this.registerSubAction({ + name: SUB_ACTION.UNIFIED_COMPLETION_STREAM, + method: 'performApiUnifiedCompletionStream', + schema: UnifiedChatCompleteParamsSchema, + }); + + // streaming unified completion task for langchain + this.registerSubAction({ + name: SUB_ACTION.UNIFIED_COMPLETION_ASYNC_ITERATOR, + method: 'performApiUnifiedCompletionAsyncIterator', + schema: UnifiedChatCompleteParamsSchema, }); this.registerSubAction({ @@ -85,8 +108,8 @@ export class InferenceConnector extends SubActionConnector<Config, Secrets> { }); this.registerSubAction({ - name: SUB_ACTION.COMPLETION_STREAM, - method: 'performApiCompletionStream', + name: SUB_ACTION.COMPLETION, + method: 'performApiCompletion', schema: ChatCompleteParamsSchema, }); } @@ -96,16 +119,112 @@ export class InferenceConnector extends SubActionConnector<Config, Secrets> { * @param input the text on which you want to perform the inference task. * @signal abort signal */ - public async performApiCompletion({ - input, - signal, - }: ChatCompleteParams & { signal?: AbortSignal }): Promise<ChatCompleteResponse> { - const response = await this.performInferenceApi( - { inference_id: this.inferenceId, input, task_type: 'completion' }, - false, - signal + public async performApiUnifiedCompletion( + params: UnifiedChatCompleteParams + ): Promise<UnifiedChatCompleteResponse> { + const res = await this.performApiUnifiedCompletionStream(params); + + const obs$ = from(eventSourceStreamIntoObservable(res as unknown as Readable)).pipe( + filter((line) => !!line && line !== '[DONE]'), + map((line) => { + return JSON.parse(line) as OpenAI.ChatCompletionChunk | { error: { message: string } }; + }), + tap((line) => { + if ('error' in line) { + throw new Error(line.error.message); + } + if ( + 'choices' in line && + line.choices.length && + line.choices[0].finish_reason === 'length' + ) { + throw new Error('createTokenLimitReachedError()'); + } + }), + filter((line): line is OpenAI.ChatCompletionChunk => { + return 'object' in line && line.object === 'chat.completion.chunk'; + }), + mergeMap((chunk): Observable<UnifiedChatCompleteResponse> => { + const events: UnifiedChatCompleteResponse[] = []; + events.push({ + choices: chunk.choices.map((c) => ({ + message: { + tool_calls: c.delta.tool_calls?.map((t) => ({ + index: t.index, + id: t.id, + function: t.function, + type: t.type, + })), + content: c.delta.content, + refusal: c.delta.refusal, + role: c.delta.role, + }, + finish_reason: c.finish_reason, + index: c.index, + })), + id: chunk.id, + model: chunk.model, + object: chunk.object, + usage: chunk.usage, + }); + return from(events); + }), + identity + ); + + return chunksIntoMessage(obs$); + } + + /** + * responsible for making a esClient inference method to perform chat completetion task endpoint and returning the service response data + * @param input the text on which you want to perform the inference task. + * @signal abort signal + */ + public async performApiUnifiedCompletionStream(params: UnifiedChatCompleteParams) { + return await this.esClient.transport.request<UnifiedChatCompleteResponse>( + { + method: 'POST', + path: `_inference/completion/${this.inferenceId}/_unified`, + body: { ...params.body, n: undefined }, // exclude n param for now, constant is used on the inference API side + }, + { + asStream: true, + } ); - return response.completion!; + } + + /** + * Streamed requests (langchain) + * @param params - the request body + * @returns { + * consumerStream: Stream<UnifiedChatCompleteResponse>; the result to be read/transformed on the server and sent to the client via Server Sent Events + * tokenCountStream: Stream<UnifiedChatCompleteResponse>; the result for token counting stream + * } + */ + public async performApiUnifiedCompletionAsyncIterator( + params: UnifiedChatCompleteParams & { signal?: AbortSignal }, + connectorUsageCollector: ConnectorUsageCollector + ): Promise<{ + consumerStream: Stream<ChatCompletionChunk>; + tokenCountStream: Stream<ChatCompletionChunk>; + }> { + try { + connectorUsageCollector.addRequestBodyBytes(undefined, params.body); + const res = await this.performApiUnifiedCompletionStream(params); + const controller = new AbortController(); + // splits the stream in two, one is used for the UI and other for token tracking + + const stream = Stream.fromSSEResponse<ChatCompletionChunk>( + { body: res } as unknown as Response, + controller + ); + const teed = stream.tee(); + return { consumerStream: teed[0], tokenCountStream: teed[1] }; + // since we do not use the sub action connector request method, we need to do our own error handling + } catch (e) { + const errorMessage = this.getResponseErrorMessage(e); + throw new Error(errorMessage); + } } /** @@ -198,35 +317,56 @@ export class InferenceConnector extends SubActionConnector<Config, Secrets> { } } - private async streamAPI({ + /** + * responsible for making a esClient inference method to perform chat completetion task endpoint and returning the service response data + * @param input the text on which you want to perform the inference task. + * @signal abort signal + */ + public async performApiCompletion({ input, signal, - }: ChatCompleteParams & { signal?: AbortSignal }): Promise<StreamingResponse> { + }: ChatCompleteParams & { signal?: AbortSignal }): Promise<ChatCompleteResponse> { const response = await this.performInferenceApi( - { inference_id: this.inferenceId, input, task_type: this.taskType as InferenceTaskType }, - true, + { inference_id: this.inferenceId, input, task_type: 'completion' }, + false, signal ); - - return (response as unknown as Stream).pipe(new PassThrough()); + return response.completion!; } /** - * takes input. It calls the streamApi method to make a - * request to the Inference API with the message. It then returns a Transform stream - * that pipes the response from the API through the transformToString function, - * which parses the proprietary response into a string of the response text alone - * @param input A message to be sent to the API - * @signal abort signal + * retrieves a dashboard from the Kibana server and checks if the + * user has the necessary privileges to access it. + * @param dashboardId The ID of the dashboard to retrieve. */ - public async performApiCompletionStream({ - input, - signal, - }: ChatCompleteParams & { signal?: AbortSignal }): Promise<IncomingMessage> { - const res = (await this.streamAPI({ - input, - signal, - })) as unknown as IncomingMessage; - return res; + public async getDashboard({ + dashboardId, + }: DashboardActionParams): Promise<DashboardActionResponse> { + const privilege = (await this.esClient.transport.request({ + path: '/_security/user/_has_privileges', + method: 'POST', + body: { + index: [ + { + names: ['.kibana-event-log-*'], + allow_restricted_indices: true, + privileges: ['read'], + }, + ], + }, + })) as { has_all_requested: boolean }; + + if (!privilege?.has_all_requested) { + return { available: false }; + } + + const response = await initDashboard({ + logger: this.logger, + savedObjectsClient: this.savedObjectsClient, + dashboardId, + genAIProvider: 'Inference', + }); + + return { available: response.success }; } } diff --git a/x-pack/plugins/task_manager/server/saved_objects/model_versions/task_model_versions.ts b/x-pack/plugins/task_manager/server/saved_objects/model_versions/task_model_versions.ts index 775b3ea2f8ca..a86aed7e358f 100644 --- a/x-pack/plugins/task_manager/server/saved_objects/model_versions/task_model_versions.ts +++ b/x-pack/plugins/task_manager/server/saved_objects/model_versions/task_model_versions.ts @@ -8,6 +8,10 @@ import { SavedObjectsModelVersionMap } from '@kbn/core-saved-objects-server'; import { taskSchemaV1, taskSchemaV2 } from '../schemas/task'; +// IMPORTANT!!! +// When adding new model versions, make sure to manually test +// downgrading to the previous version. This is a gap in our +// automated test coverage so manual testing is needed. export const taskModelVersions: SavedObjectsModelVersionMap = { '1': { changes: [ diff --git a/x-pack/plugins/task_manager/server/saved_objects/schemas/task.test.ts b/x-pack/plugins/task_manager/server/saved_objects/schemas/task.test.ts new file mode 100644 index 000000000000..709e50bc54bf --- /dev/null +++ b/x-pack/plugins/task_manager/server/saved_objects/schemas/task.test.ts @@ -0,0 +1,24 @@ +/* + * 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 { validateDuration } from './task'; + +test('allows valid duration', () => { + expect(validateDuration('1s')).toBeUndefined(); + expect(validateDuration('45346s')).toBeUndefined(); + expect(validateDuration('10m')).toBeUndefined(); + expect(validateDuration('30000000h')).toBeUndefined(); + expect(validateDuration('3245d')).toBeUndefined(); +}); + +test('returns error message for invalid duration', () => { + expect(validateDuration('10x')).toBe('string is not a valid duration: 10x'); + expect(validateDuration('PT1M')).toBe('string is not a valid duration: PT1M'); + expect(validateDuration('foo')).toBe('string is not a valid duration: foo'); + expect(validateDuration('1 minute')).toBe('string is not a valid duration: 1 minute'); + expect(validateDuration('1hr')).toBe('string is not a valid duration: 1hr'); +}); diff --git a/x-pack/plugins/task_manager/server/saved_objects/schemas/task.ts b/x-pack/plugins/task_manager/server/saved_objects/schemas/task.ts index 2a6ee5c92198..25b6a1cb079d 100644 --- a/x-pack/plugins/task_manager/server/saved_objects/schemas/task.ts +++ b/x-pack/plugins/task_manager/server/saved_objects/schemas/task.ts @@ -6,6 +6,13 @@ */ import { schema } from '@kbn/config-schema'; +import { isInterval } from '../../lib/intervals'; + +export function validateDuration(duration: string) { + if (!isInterval(duration)) { + return 'string is not a valid duration: ' + duration; + } +} export const taskSchemaV1 = schema.object({ taskType: schema.string(), @@ -15,7 +22,7 @@ export const taskSchemaV1 = schema.object({ runAt: schema.string(), schedule: schema.maybe( schema.object({ - interval: schema.duration(), + interval: schema.string({ validate: validateDuration }), }) ), params: schema.string(), diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/cloud_backup.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/cloud_backup.tsx index 0d06c72e5f1b..dabdd76bb137 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/cloud_backup.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/cloud_backup.tsx @@ -145,8 +145,16 @@ export const CloudBackup: React.FunctionComponent<Props> = ({ return ( <> + <EuiText> + <p> + {i18n.translate('xpack.upgradeAssistant.overview.cloudBackup.description', { + defaultMessage: 'Back up your data using snapshots before proceeding.', + })} + </p> + </EuiText> + <EuiSpacer size="m" /> {statusMessage} - <EuiSpacer size="s" /> + <EuiSpacer size="m" /> {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} <EuiButton href={cloudSnapshotsUrl} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/on_prem_backup.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/on_prem_backup.tsx index 1bccb4eb3f8d..c356f3403586 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/on_prem_backup.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/backup_step/on_prem_backup.tsx @@ -27,6 +27,7 @@ const SnapshotRestoreAppLink: React.FunctionComponent = () => { // eslint-disable-next-line @elastic/eui/href-or-on-click <EuiButton href={snapshotRestoreUrl} + target="_blank" onClick={() => { uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_BACKUP_DATA_ON_PREM_CLICK); }} @@ -46,12 +47,12 @@ export const OnPremBackup: React.FunctionComponent = () => { <EuiText> <p> {i18n.translate('xpack.upgradeAssistant.overview.backupStepDescription', { - defaultMessage: 'Make sure you have a current snapshot before making any changes.', + defaultMessage: 'Make sure you have a current snapshot before proceeding.', })} </p> </EuiText> - <EuiSpacer size="s" /> + <EuiSpacer size="m" /> <SnapshotRestoreAppLink /> </> 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 30663d02cda7..775da2fc1c80 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 @@ -18,7 +18,7 @@ export const getLensOperationFromRuleMetric = (metric: GenericMetric): LensOpera const { aggType, field, filter = '' } = metric; let operation: string = aggType; const operationArgs: string[] = []; - const escapedFilter = filter.replace(/'/g, "\\'"); + const escapedFilter = filter.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); if (aggType === Aggregators.RATE) { return { diff --git a/x-pack/solutions/observability/plugins/observability/server/ui_settings.ts b/x-pack/solutions/observability/plugins/observability/server/ui_settings.ts index e5695a3c9034..a68ae28b267f 100644 --- a/x-pack/solutions/observability/plugins/observability/server/ui_settings.ts +++ b/x-pack/solutions/observability/plugins/observability/server/ui_settings.ts @@ -325,7 +325,7 @@ export const uiSettings: Record<string, UiSettings> = { }, }), schema: schema.boolean(), - value: true, + value: false, requiresPageReload: true, type: 'boolean', }, diff --git a/x-pack/solutions/observability/plugins/uptime/kibana.jsonc b/x-pack/solutions/observability/plugins/uptime/kibana.jsonc index c4c8b8b9d76d..25fd311a81f8 100644 --- a/x-pack/solutions/observability/plugins/uptime/kibana.jsonc +++ b/x-pack/solutions/observability/plugins/uptime/kibana.jsonc @@ -38,7 +38,8 @@ "triggersActionsUi", "usageCollection", "unifiedSearch", - "bfetch" + "bfetch", + "charts" ], "optionalPlugins": [ "cloud", @@ -57,4 +58,4 @@ "observability" ] } -} \ No newline at end of file +} diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/app/uptime_app.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/app/uptime_app.tsx index 3ce07ac9803f..dc775e2295b9 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/app/uptime_app.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/app/uptime_app.tsx @@ -22,7 +22,6 @@ import { UMUpdateBadge } from '../lib/lib'; import { UptimeRefreshContextProvider, UptimeSettingsContextProvider, - UptimeThemeContextProvider, UptimeStartupPluginsContextProvider, } from '../contexts'; import { CommonlyUsedRange } from '../components/common/uptime_date_picker'; @@ -34,17 +33,6 @@ import { kibanaService } from '../state/kibana_service'; import { ActionMenu } from '../components/common/header/action_menu'; import { UptimeDataViewContextProvider } from '../contexts/uptime_data_view_context'; -export interface UptimeAppColors { - danger: string; - dangerBehindText: string; - success: string; - gray: string; - range: string; - mean: string; - warning: string; - lightestShade: string; -} - export interface UptimeAppProps { basePath: string; canSave: boolean; @@ -131,27 +119,25 @@ const Application = (props: UptimeAppProps) => { <EuiThemeProvider darkMode={darkMode}> <UptimeRefreshContextProvider> <UptimeSettingsContextProvider {...props}> - <UptimeThemeContextProvider darkMode={darkMode}> - <UptimeStartupPluginsContextProvider {...startPlugins}> - <UptimeDataViewContextProvider dataViews={startPlugins.dataViews}> - <PerformanceContextProvider> - <div className={APP_WRAPPER_CLASS} data-test-subj="uptimeApp"> - <RedirectAppLinks - coreStart={{ - application: core.application, - }} - > - <InspectorContextProvider> - <UptimeAlertsFlyoutWrapper /> - <PageRouter /> - <ActionMenu appMountParameters={appMountParameters} /> - </InspectorContextProvider> - </RedirectAppLinks> - </div> - </PerformanceContextProvider> - </UptimeDataViewContextProvider> - </UptimeStartupPluginsContextProvider> - </UptimeThemeContextProvider> + <UptimeStartupPluginsContextProvider {...startPlugins}> + <UptimeDataViewContextProvider dataViews={startPlugins.dataViews}> + <PerformanceContextProvider> + <div className={APP_WRAPPER_CLASS} data-test-subj="uptimeApp"> + <RedirectAppLinks + coreStart={{ + application: core.application, + }} + > + <InspectorContextProvider> + <UptimeAlertsFlyoutWrapper /> + <PageRouter /> + <ActionMenu appMountParameters={appMountParameters} /> + </InspectorContextProvider> + </RedirectAppLinks> + </div> + </PerformanceContextProvider> + </UptimeDataViewContextProvider> + </UptimeStartupPluginsContextProvider> </UptimeSettingsContextProvider> </UptimeRefreshContextProvider> </EuiThemeProvider> diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_monitors.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_monitors.test.tsx.snap index 57a90f0081b5..3a7233b30717 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_monitors.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_monitors.test.tsx.snap @@ -50,39 +50,28 @@ exports[`CertMonitors renders expected elements for valid props 1`] = ` `; exports[`CertMonitors shallow renders expected elements for valid props 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -90,73 +79,31 @@ exports[`CertMonitors shallow renders expected elements for valid props 1`] = ` "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ - Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, + <CertMonitors + monitors={ + Array [ + Object { + "id": "bad-ssl-dashboard", + "name": "", + "url": "https://badssl.com/dashboard/", + }, + Object { + "id": "elastic-co", + "name": "elastic", + "url": "https://www.elastic.co/", + }, + Object { + "id": "extended-validation", + "name": "", + "url": "https://extended-validation.badssl.com/", }, - "push": [Function], - "replace": [Function], - } + ] } - > - <CompatRouter> - <CertMonitors - monitors={ - Array [ - Object { - "id": "bad-ssl-dashboard", - "name": "", - "url": "https://badssl.com/dashboard/", - }, - Object { - "id": "elastic-co", - "name": "elastic", - "url": "https://www.elastic.co/", - }, - Object { - "id": "extended-validation", - "name": "", - "url": "https://extended-validation.badssl.com/", - }, - ] - } - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_search.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_search.test.tsx.snap index 663ef61fc87d..a08dc4b2286d 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_search.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_search.test.tsx.snap @@ -41,39 +41,28 @@ exports[`CertificatesSearch renders expected elements for valid props 1`] = ` `; exports[`CertificatesSearch shallow renders expected elements for valid props 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -81,55 +70,13 @@ exports[`CertificatesSearch shallow renders expected elements for valid props 1` "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ - Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - "push": [Function], - "replace": [Function], - } - } - > - <CompatRouter> - <CertificateSearch - setSearch={[MockFunction]} - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + <CertificateSearch + setSearch={[MockFunction]} + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_status.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_status.test.tsx.snap index 6849d2066b11..0a870ef6ea32 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_status.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/certificates/__snapshots__/cert_status.test.tsx.snap @@ -37,39 +37,28 @@ exports[`CertStatus renders expected elements for valid props 1`] = ` `; exports[`CertStatus shallow renders expected elements for valid props 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -77,71 +66,29 @@ exports[`CertStatus shallow renders expected elements for valid props 1`] = ` "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ + <CertStatus + cert={ Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ + "common_name": "github.com", + "issuer": "DigiCert SHA2 Extended Validation Server CA", + "monitors": Array [ Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, + "id": "github", + "name": "", + "url": "https://github.com/", }, ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - "push": [Function], - "replace": [Function], + "not_after": "2020-05-08T00:00:00.000Z", + "not_before": "2018-05-08T00:00:00.000Z", + "sha1": "ca06f56b258b7a0d4f2b05470939478651151984", + "sha256": "3111500c4a66012cdae333ec3fca1c9dde45c954440e7ee413716bff3663c074", } } - > - <CompatRouter> - <CertStatus - cert={ - Object { - "common_name": "github.com", - "issuer": "DigiCert SHA2 Extended Validation Server CA", - "monitors": Array [ - Object { - "id": "github", - "name": "", - "url": "https://github.com/", - }, - ], - "not_after": "2020-05-08T00:00:00.000Z", - "not_before": "2018-05-08T00:00:00.000Z", - "sha1": "ca06f56b258b7a0d4f2b05470939478651151984", - "sha256": "3111500c4a66012cdae333ec3fca1c9dde45c954440e7ee413716bff3663c074", - } - } - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/__snapshots__/monitor_tags.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/__snapshots__/monitor_tags.test.tsx.snap index 196f90fb8eca..0ec642138360 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/__snapshots__/monitor_tags.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/__snapshots__/monitor_tags.test.tsx.snap @@ -102,39 +102,28 @@ exports[`MonitorTags component it shows expand tag on too many tags 1`] = ` `; exports[`MonitorTags component render against summary 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -142,205 +131,143 @@ exports[`MonitorTags component render against summary 1`] = ` "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ + <MonitorTags + summary={ Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, + "minInterval": 75000, + "monitor_id": "android-homepage", + "state": Object { + "monitor": Object { + "name": "Android Homepage", + "type": "http", }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - "push": [Function], - "replace": [Function], - } - } - > - <CompatRouter> - <MonitorTags - summary={ - Object { - "minInterval": 75000, - "monitor_id": "android-homepage", - "state": Object { + "observer": Object { + "geo": Object { + "name": Array [ + "Europe", + ], + }, + }, + "summary": Object { + "down": 0, + "status": "up", + "up": 1, + }, + "summaryPings": Array [ + Object { + "@timestamp": "2020-12-09T17:08:22.200Z", + "agent": Object { + "ephemeral_id": "a4d0d3eb-d162-4cc7-b14d-eaaad8b3d224", + "id": "1f122196-6a5e-4bd4-8288-ef084e2ec982", + "name": "Elastic-Mac", + "type": "heartbeat", + "version": "8.0.0", + }, + "docId": "jBR5SHYBjTkd_7K7sM41", + "ecs": Object { + "version": "1.6.0", + }, + "event": Object { + "dataset": "uptime", + }, + "http": Object { + "response": Object { + "body": Object { + "bytes": 176335, + "hash": "8367430abf690d75f3c0277b31a16cfcc622e2b315e338f803ae850127d37f48", + }, + "headers": Object { + "Accept-Ranges": "bytes", + "Alt-Svc": "h3-29=\\":443\\"; ma=2592000,h3-T051=\\":443\\"; ma=2592000,h3-Q050=\\":443\\"; ma=2592000,h3-Q046=\\":443\\"; ma=2592000,h3-Q043=\\":443\\"; ma=2592000,quic=\\":443\\"; ma=2592000; v=\\"46,43\\"", + "Cache-Control": "private, max-age=0", + "Content-Length": "176335", + "Content-Type": "text/html", + "Cross-Origin-Resource-Policy": "cross-origin", + "Date": "Wed, 09 Dec 2020 17:08:22 GMT", + "Expires": "Wed, 09 Dec 2020 17:08:22 GMT", + "Last-Modified": "Thu, 03 Dec 2020 21:45:00 GMT", + "Server": "sffe", + "Vary": "Accept-Encoding", + "X-Content-Type-Options": "nosniff", + "X-Xss-Protection": "0", + }, + "status_code": 200, + }, + }, "monitor": Object { + "check_group": "12a21140-3a41-11eb-ae8d-a683e72ee74d", + "duration": Object { + "us": 247752, + }, + "id": "android-homepage", + "ip": "172.217.16.206", "name": "Android Homepage", + "status": "up", + "timespan": Object { + "gte": "2020-12-09T17:08:22.200Z", + "lt": "2020-12-09T17:08:52.200Z", + }, "type": "http", }, "observer": Object { "geo": Object { - "name": Array [ - "Europe", - ], + "location": "51.5074, -0.1278", + "name": "Europe", + }, + }, + "resolve": Object { + "ip": "172.217.16.206", + "rtt": Object { + "us": 7025, }, }, "summary": Object { "down": 0, - "status": "up", "up": 1, }, - "summaryPings": Array [ - Object { - "@timestamp": "2020-12-09T17:08:22.200Z", - "agent": Object { - "ephemeral_id": "a4d0d3eb-d162-4cc7-b14d-eaaad8b3d224", - "id": "1f122196-6a5e-4bd4-8288-ef084e2ec982", - "name": "Elastic-Mac", - "type": "heartbeat", - "version": "8.0.0", - }, - "docId": "jBR5SHYBjTkd_7K7sM41", - "ecs": Object { - "version": "1.6.0", - }, - "event": Object { - "dataset": "uptime", - }, - "http": Object { - "response": Object { - "body": Object { - "bytes": 176335, - "hash": "8367430abf690d75f3c0277b31a16cfcc622e2b315e338f803ae850127d37f48", - }, - "headers": Object { - "Accept-Ranges": "bytes", - "Alt-Svc": "h3-29=\\":443\\"; ma=2592000,h3-T051=\\":443\\"; ma=2592000,h3-Q050=\\":443\\"; ma=2592000,h3-Q046=\\":443\\"; ma=2592000,h3-Q043=\\":443\\"; ma=2592000,quic=\\":443\\"; ma=2592000; v=\\"46,43\\"", - "Cache-Control": "private, max-age=0", - "Content-Length": "176335", - "Content-Type": "text/html", - "Cross-Origin-Resource-Policy": "cross-origin", - "Date": "Wed, 09 Dec 2020 17:08:22 GMT", - "Expires": "Wed, 09 Dec 2020 17:08:22 GMT", - "Last-Modified": "Thu, 03 Dec 2020 21:45:00 GMT", - "Server": "sffe", - "Vary": "Accept-Encoding", - "X-Content-Type-Options": "nosniff", - "X-Xss-Protection": "0", - }, - "status_code": 200, - }, - }, - "monitor": Object { - "check_group": "12a21140-3a41-11eb-ae8d-a683e72ee74d", - "duration": Object { - "us": 247752, - }, - "id": "android-homepage", - "ip": "172.217.16.206", - "name": "Android Homepage", - "status": "up", - "timespan": Object { - "gte": "2020-12-09T17:08:22.200Z", - "lt": "2020-12-09T17:08:52.200Z", - }, - "type": "http", - }, - "observer": Object { - "geo": Object { - "location": "51.5074, -0.1278", - "name": "Europe", - }, - }, - "resolve": Object { - "ip": "172.217.16.206", - "rtt": Object { - "us": 7025, - }, - }, - "summary": Object { - "down": 0, - "up": 1, - }, - "tags": Array [ - "org:google", - ], - "tcp": Object { - "rtt": Object { - "connect": Object { - "us": 30719, - }, - }, - }, - "timestamp": "2020-12-09T17:08:22.200Z", - "tls": Object { - "certificate_not_valid_after": "2021-01-26T07:38:14.000Z", - "certificate_not_valid_before": "2020-11-03T07:38:14.000Z", - "cipher": "TLS-AES-128-GCM-SHA256", - "established": true, - "server": Object { - "hash": Object { - "sha1": "be647fa3de52eba57c89ac297c05604c4af69372", - "sha256": "19321783f8f923a0220cee1599a58203faf4c401ab5728c730ab05a44d9e7a9c", - }, - "x509": Object { - "issuer": Object { - "common_name": "GTS CA 1O1", - "distinguished_name": "CN=GTS CA 1O1,O=Google Trust Services,C=US", - }, - "not_after": "2021-01-26T07:38:14.000Z", - "not_before": "2020-11-03T07:38:14.000Z", - "public_key_algorithm": "ECDSA", - "public_key_curve": "P-256", - "serial_number": "264575002113234958015854475703440562297", - "signature_algorithm": "SHA256-RSA", - "subject": Object { - "common_name": "www.android.com", - "distinguished_name": "CN=www.android.com,O=Google LLC,L=Mountain View,ST=California,C=US", - }, - }, - }, - }, - "url": Object { - "domain": "www.android.com", - "full": "https://www.android.com", - "port": 443, - "scheme": "https", + "tags": Array [ + "org:google", + ], + "tcp": Object { + "rtt": Object { + "connect": Object { + "us": 30719, }, }, - ], + }, "timestamp": "2020-12-09T17:08:22.200Z", "tls": Object { "certificate_not_valid_after": "2021-01-26T07:38:14.000Z", "certificate_not_valid_before": "2020-11-03T07:38:14.000Z", "cipher": "TLS-AES-128-GCM-SHA256", "established": true, - "rtt": Object { - "handshake": Object { - "us": 39344, + "server": Object { + "hash": Object { + "sha1": "be647fa3de52eba57c89ac297c05604c4af69372", + "sha256": "19321783f8f923a0220cee1599a58203faf4c401ab5728c730ab05a44d9e7a9c", + }, + "x509": Object { + "issuer": Object { + "common_name": "GTS CA 1O1", + "distinguished_name": "CN=GTS CA 1O1,O=Google Trust Services,C=US", + }, + "not_after": "2021-01-26T07:38:14.000Z", + "not_before": "2020-11-03T07:38:14.000Z", + "public_key_algorithm": "ECDSA", + "public_key_curve": "P-256", + "serial_number": "264575002113234958015854475703440562297", + "signature_algorithm": "SHA256-RSA", + "subject": Object { + "common_name": "www.android.com", + "distinguished_name": "CN=www.android.com,O=Google LLC,L=Mountain View,ST=California,C=US", + }, }, }, - "version": "1.3", - "version_protocol": "tls", }, "url": Object { "domain": "www.android.com", @@ -349,48 +276,57 @@ exports[`MonitorTags component render against summary 1`] = ` "scheme": "https", }, }, - } - } - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + ], + "timestamp": "2020-12-09T17:08:22.200Z", + "tls": Object { + "certificate_not_valid_after": "2021-01-26T07:38:14.000Z", + "certificate_not_valid_before": "2020-11-03T07:38:14.000Z", + "cipher": "TLS-AES-128-GCM-SHA256", + "established": true, + "rtt": Object { + "handshake": Object { + "us": 39344, + }, + }, + "version": "1.3", + "version_protocol": "tls", + }, + "url": Object { + "domain": "www.android.com", + "full": "https://www.android.com", + "port": 443, + "scheme": "https", + }, + }, + } + } + /> +</Router> `; exports[`MonitorTags component renders against ping 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -398,173 +334,131 @@ exports[`MonitorTags component renders against ping 1`] = ` "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ + <MonitorTags + ping={ Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, + "@timestamp": "2020-12-09T17:08:22.200Z", + "agent": Object { + "ephemeral_id": "a4d0d3eb-d162-4cc7-b14d-eaaad8b3d224", + "id": "1f122196-6a5e-4bd4-8288-ef084e2ec982", + "name": "Elastic-Mac", + "type": "heartbeat", + "version": "8.0.0", }, - "push": [Function], - "replace": [Function], - } - } - > - <CompatRouter> - <MonitorTags - ping={ - Object { - "@timestamp": "2020-12-09T17:08:22.200Z", - "agent": Object { - "ephemeral_id": "a4d0d3eb-d162-4cc7-b14d-eaaad8b3d224", - "id": "1f122196-6a5e-4bd4-8288-ef084e2ec982", - "name": "Elastic-Mac", - "type": "heartbeat", - "version": "8.0.0", - }, - "docId": "jBR5SHYBjTkd_7K7sM41", - "ecs": Object { - "version": "1.6.0", - }, - "event": Object { - "dataset": "uptime", - }, - "http": Object { - "response": Object { - "body": Object { - "bytes": 176335, - "hash": "8367430abf690d75f3c0277b31a16cfcc622e2b315e338f803ae850127d37f48", - }, - "headers": Object { - "Accept-Ranges": "bytes", - "Alt-Svc": "h3-29=\\":443\\"; ma=2592000,h3-T051=\\":443\\"; ma=2592000,h3-Q050=\\":443\\"; ma=2592000,h3-Q046=\\":443\\"; ma=2592000,h3-Q043=\\":443\\"; ma=2592000,quic=\\":443\\"; ma=2592000; v=\\"46,43\\"", - "Cache-Control": "private, max-age=0", - "Content-Length": "176335", - "Content-Type": "text/html", - "Cross-Origin-Resource-Policy": "cross-origin", - "Date": "Wed, 09 Dec 2020 17:08:22 GMT", - "Expires": "Wed, 09 Dec 2020 17:08:22 GMT", - "Last-Modified": "Thu, 03 Dec 2020 21:45:00 GMT", - "Server": "sffe", - "Vary": "Accept-Encoding", - "X-Content-Type-Options": "nosniff", - "X-Xss-Protection": "0", - }, - "status_code": 200, - }, - }, - "monitor": Object { - "check_group": "12a21140-3a41-11eb-ae8d-a683e72ee74d", - "duration": Object { - "us": 247752, - }, - "id": "android-homepage", - "ip": "172.217.16.206", - "name": "Android Homepage", - "status": "up", - "timespan": Object { - "gte": "2020-12-09T17:08:22.200Z", - "lt": "2020-12-09T17:08:52.200Z", - }, - "type": "http", + "docId": "jBR5SHYBjTkd_7K7sM41", + "ecs": Object { + "version": "1.6.0", + }, + "event": Object { + "dataset": "uptime", + }, + "http": Object { + "response": Object { + "body": Object { + "bytes": 176335, + "hash": "8367430abf690d75f3c0277b31a16cfcc622e2b315e338f803ae850127d37f48", }, - "observer": Object { - "geo": Object { - "location": "51.5074, -0.1278", - "name": "Europe", - }, + "headers": Object { + "Accept-Ranges": "bytes", + "Alt-Svc": "h3-29=\\":443\\"; ma=2592000,h3-T051=\\":443\\"; ma=2592000,h3-Q050=\\":443\\"; ma=2592000,h3-Q046=\\":443\\"; ma=2592000,h3-Q043=\\":443\\"; ma=2592000,quic=\\":443\\"; ma=2592000; v=\\"46,43\\"", + "Cache-Control": "private, max-age=0", + "Content-Length": "176335", + "Content-Type": "text/html", + "Cross-Origin-Resource-Policy": "cross-origin", + "Date": "Wed, 09 Dec 2020 17:08:22 GMT", + "Expires": "Wed, 09 Dec 2020 17:08:22 GMT", + "Last-Modified": "Thu, 03 Dec 2020 21:45:00 GMT", + "Server": "sffe", + "Vary": "Accept-Encoding", + "X-Content-Type-Options": "nosniff", + "X-Xss-Protection": "0", }, - "resolve": Object { - "ip": "172.217.16.206", - "rtt": Object { - "us": 7025, - }, + "status_code": 200, + }, + }, + "monitor": Object { + "check_group": "12a21140-3a41-11eb-ae8d-a683e72ee74d", + "duration": Object { + "us": 247752, + }, + "id": "android-homepage", + "ip": "172.217.16.206", + "name": "Android Homepage", + "status": "up", + "timespan": Object { + "gte": "2020-12-09T17:08:22.200Z", + "lt": "2020-12-09T17:08:52.200Z", + }, + "type": "http", + }, + "observer": Object { + "geo": Object { + "location": "51.5074, -0.1278", + "name": "Europe", + }, + }, + "resolve": Object { + "ip": "172.217.16.206", + "rtt": Object { + "us": 7025, + }, + }, + "summary": Object { + "down": 0, + "up": 1, + }, + "tags": Array [ + "org:google", + ], + "tcp": Object { + "rtt": Object { + "connect": Object { + "us": 30719, }, - "summary": Object { - "down": 0, - "up": 1, + }, + }, + "timestamp": "2020-12-09T17:08:22.200Z", + "tls": Object { + "certificate_not_valid_after": "2021-01-26T07:38:14.000Z", + "certificate_not_valid_before": "2020-11-03T07:38:14.000Z", + "cipher": "TLS-AES-128-GCM-SHA256", + "established": true, + "server": Object { + "hash": Object { + "sha1": "be647fa3de52eba57c89ac297c05604c4af69372", + "sha256": "19321783f8f923a0220cee1599a58203faf4c401ab5728c730ab05a44d9e7a9c", }, - "tags": Array [ - "org:google", - ], - "tcp": Object { - "rtt": Object { - "connect": Object { - "us": 30719, - }, + "x509": Object { + "issuer": Object { + "common_name": "GTS CA 1O1", + "distinguished_name": "CN=GTS CA 1O1,O=Google Trust Services,C=US", }, - }, - "timestamp": "2020-12-09T17:08:22.200Z", - "tls": Object { - "certificate_not_valid_after": "2021-01-26T07:38:14.000Z", - "certificate_not_valid_before": "2020-11-03T07:38:14.000Z", - "cipher": "TLS-AES-128-GCM-SHA256", - "established": true, - "server": Object { - "hash": Object { - "sha1": "be647fa3de52eba57c89ac297c05604c4af69372", - "sha256": "19321783f8f923a0220cee1599a58203faf4c401ab5728c730ab05a44d9e7a9c", - }, - "x509": Object { - "issuer": Object { - "common_name": "GTS CA 1O1", - "distinguished_name": "CN=GTS CA 1O1,O=Google Trust Services,C=US", - }, - "not_after": "2021-01-26T07:38:14.000Z", - "not_before": "2020-11-03T07:38:14.000Z", - "public_key_algorithm": "ECDSA", - "public_key_curve": "P-256", - "serial_number": "264575002113234958015854475703440562297", - "signature_algorithm": "SHA256-RSA", - "subject": Object { - "common_name": "www.android.com", - "distinguished_name": "CN=www.android.com,O=Google LLC,L=Mountain View,ST=California,C=US", - }, - }, + "not_after": "2021-01-26T07:38:14.000Z", + "not_before": "2020-11-03T07:38:14.000Z", + "public_key_algorithm": "ECDSA", + "public_key_curve": "P-256", + "serial_number": "264575002113234958015854475703440562297", + "signature_algorithm": "SHA256-RSA", + "subject": Object { + "common_name": "www.android.com", + "distinguished_name": "CN=www.android.com,O=Google LLC,L=Mountain View,ST=California,C=US", }, }, - "url": Object { - "domain": "www.android.com", - "full": "https://www.android.com", - "port": 443, - "scheme": "https", - }, - } - } - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + }, + }, + "url": Object { + "domain": "www.android.com", + "full": "https://www.android.com", + "port": 443, + "scheme": "https", + }, + } + } + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap index 15f8ad666dce..a21a15116ccf 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap @@ -578,7 +578,6 @@ exports[`DonutChart component passes correct props without errors for valid prop "outerSizeRatio": 0.9, }, }, - Object {}, ] } /> @@ -697,7 +696,7 @@ exports[`DonutChart component renders a donut chart 1`] = ` class="euiFlexItem emotion-euiFlexItem-growZero" > <span - color="#bd271e" + color="#BD271E" data-euiicon-type="dot" /> </div> @@ -739,7 +738,7 @@ exports[`DonutChart component renders a donut chart 1`] = ` class="euiFlexItem emotion-euiFlexItem-growZero" > <span - color="#d3dae6" + color="#D3DAE6" data-euiicon-type="dot" /> </div> @@ -856,7 +855,7 @@ exports[`DonutChart component renders a green check when all monitors are up 1`] class="euiFlexItem emotion-euiFlexItem-growZero" > <span - color="#bd271e" + color="#BD271E" data-euiicon-type="dot" /> </div> @@ -898,7 +897,7 @@ exports[`DonutChart component renders a green check when all monitors are up 1`] class="euiFlexItem emotion-euiFlexItem-growZero" > <span - color="#d3dae6" + color="#D3DAE6" data-euiicon-type="dot" /> </div> diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/monitor_bar_series.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/monitor_bar_series.test.tsx.snap index 7b5da3fc3159..230fe13e1072 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/monitor_bar_series.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/monitor_bar_series.test.tsx.snap @@ -32,39 +32,28 @@ exports[`MonitorBarSeries component renders if the data series is present 1`] = `; exports[`MonitorBarSeries component shallow renders a series when there are down items 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -72,74 +61,32 @@ exports[`MonitorBarSeries component shallow renders a series when there are down "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ - Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, + <MonitorBarSeries + histogramSeries={ + Array [ + Object { + "down": 1, + "timestamp": 124, + "up": 0, + }, + Object { + "down": 1, + "timestamp": 125, + "up": 0, + }, + Object { + "down": 1, + "timestamp": 126, + "up": 0, }, - "push": [Function], - "replace": [Function], - } + ] } - > - <CompatRouter> - <MonitorBarSeries - histogramSeries={ - Array [ - Object { - "down": 1, - "timestamp": 124, - "up": 0, - }, - Object { - "down": 1, - "timestamp": 125, - "up": 0, - }, - Object { - "down": 1, - "timestamp": 126, - "up": 0, - }, - ] - } - minInterval={10} - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + minInterval={10} + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/chart_wrapper.test.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/chart_wrapper.test.tsx index 1a1a74ab753b..adeb1d00facc 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/chart_wrapper.test.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/chart_wrapper.test.tsx @@ -13,7 +13,15 @@ import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { ChartWrapper } from './chart_wrapper'; import { SnapshotHeading } from '../../overview/snapshot/snapshot_heading'; import { DonutChart } from './donut_chart'; +import { mockCore } from '../../../lib/helper/rtl_helpers'; const SNAPSHOT_CHART_HEIGHT = 144; + +jest.mock('@kbn/kibana-react-plugin/public', () => ({ + useKibana: jest.fn().mockImplementation(() => ({ + services: mockCore(), + })), +})); + describe('ChartWrapper component', () => { it('renders the component with loading false', () => { const component = shallowWithIntl( diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart.test.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart.test.tsx index ff83c51900cd..d74a7ba7e437 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart.test.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart.test.tsx @@ -8,6 +8,13 @@ import { DonutChart } from './donut_chart'; import { renderWithIntl, shallowWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; +import { mockCore } from '../../../lib/helper/rtl_helpers'; + +jest.mock('@kbn/kibana-react-plugin/public', () => ({ + useKibana: jest.fn().mockImplementation(() => ({ + services: mockCore(), + })), +})); describe('DonutChart component', () => { it('passes correct props without errors for valid props', () => { diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart.tsx index e5fc97a52600..1a5ff52eb10e 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart.tsx @@ -5,13 +5,14 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; -import React, { useContext } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, useEuiTheme } from '@elastic/eui'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import { Chart, Datum, Partition, Settings, PartitionLayout, PartialTheme } from '@elastic/charts'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ClientPluginsStart } from '../../../../plugin'; import { DonutChartLegend } from './donut_chart_legend'; -import { UptimeThemeContext } from '../../../contexts'; interface DonutChartProps { down: number; @@ -42,11 +43,14 @@ const themeOverrides: PartialTheme = { }; export const DonutChart = ({ height, down, up }: DonutChartProps) => { - const { - colors: { danger, gray }, - chartTheme, - } = useContext(UptimeThemeContext); + const theme = useEuiTheme(); + const danger = theme.euiTheme.colors.danger; + const gray = theme.euiTheme.colors.lightShade; + const { + services: { charts }, + } = useKibana<ClientPluginsStart>(); + const baseTheme = charts.theme.useChartsBaseTheme(); return ( <EuiFlexGroup alignItems="center" responsive={false}> <EuiFlexItem grow={false} style={{ position: 'relative' }}> @@ -58,11 +62,7 @@ export const DonutChart = ({ height, down, up }: DonutChartProps) => { values: { down, total: up + down }, })} > - <Settings - theme={[themeOverrides, chartTheme.theme ?? {}]} - baseTheme={chartTheme.baseTheme} - locale={i18n.getLocale()} - /> + <Settings theme={[themeOverrides]} baseTheme={baseTheme} locale={i18n.getLocale()} /> <Partition id="spec_1" data={[ diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart_legend.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart_legend.tsx index 5b9ad9102454..777c43300f91 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart_legend.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/donut_chart_legend.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import { EuiSpacer } from '@elastic/eui'; -import React, { useContext } from 'react'; +import { EuiSpacer, useEuiTheme } from '@elastic/eui'; +import React from 'react'; import styled from 'styled-components'; import { DonutChartLegendRow } from './donut_chart_legend_row'; -import { UptimeThemeContext } from '../../../contexts'; import { STATUS_DOWN_LABEL, STATUS_UP_LABEL, @@ -30,9 +29,10 @@ interface Props { } export const DonutChartLegend = ({ down, up }: Props) => { - const { - colors: { gray, danger }, - } = useContext(UptimeThemeContext); + const theme = useEuiTheme(); + const danger = theme.euiTheme.colors.danger; + const gray = theme.euiTheme.colors.lightShade; + return ( <LegendContainer> <DonutChartLegendRow diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/duration_chart.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/duration_chart.tsx index 7acaa9ac2f93..d6d96518e10d 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/duration_chart.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/duration_chart.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useContext, useState } from 'react'; +import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -19,6 +19,7 @@ import { LegendItemListener, } from '@elastic/charts'; import { useSelector } from 'react-redux'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; import { getChartDateLabel } from '../../../lib/helper'; import { LocationDurationLine } from '../../../../../common/types'; import { DurationLineSeriesList } from './duration_line_series_list'; @@ -28,11 +29,11 @@ import { getTickFormat } from './get_tick_format'; import { ChartEmptyState } from './chart_empty_state'; import { DurationAnomaliesBar } from './duration_line_bar_list'; import { AnomalyRecords } from '../../../state/actions'; -import { UptimeThemeContext } from '../../../contexts'; import { MONITOR_CHART_HEIGHT } from '../../monitor'; import { monitorStatusSelector } from '../../../state/selectors'; import { microToMilli, microToSec } from '../../../lib/formatting'; import { MS_LABEL, SECONDS_LABEL } from '../../../../../common/translations/translations'; +import { ClientPluginsStart } from '../../../../plugin'; interface DurationChartProps { /** @@ -66,7 +67,10 @@ export const DurationChartComponent = ({ const [hiddenLegends, setHiddenLegends] = useState<string[]>([]); - const { chartTheme } = useContext(UptimeThemeContext); + const { + services: { charts }, + } = useKibana<ClientPluginsStart>(); + const baseTheme = charts.theme.useChartsBaseTheme(); const onBrushEnd: BrushEndListener = ({ x }) => { if (!x) { @@ -110,8 +114,7 @@ export const DurationChartComponent = ({ onBrushEnd={onBrushEnd} onLegendItemClick={legendToggleVisibility} locale={i18n.getLocale()} - // TODO connect to charts.theme service see src/plugins/charts/public/services/theme/README.md - {...chartTheme} + baseTheme={baseTheme} /> <Axis id="bottom" diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/monitor_bar_series.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/monitor_bar_series.tsx index 25579d56fb13..6012a08b2891 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/monitor_bar_series.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/monitor_bar_series.tsx @@ -18,15 +18,16 @@ import { ElementClickListener, } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import React, { useContext } from 'react'; +import React from 'react'; import moment from 'moment'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiText, EuiToolTip } from '@elastic/eui'; +import { EuiText, EuiToolTip, useEuiTheme } from '@elastic/eui'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; import { HistogramPoint } from '../../../../../common/runtime_types'; import { getChartDateLabel, seriesHasDownValues } from '../../../lib/helper'; import { useUrlParams } from '../../../hooks'; -import { UptimeThemeContext } from '../../../contexts'; import { getDateRangeFromChartElement } from './utils'; +import { ClientPluginsStart } from '../../../../plugin'; export interface MonitorBarSeriesProps { /** @@ -44,9 +45,13 @@ export interface MonitorBarSeriesProps { */ export const MonitorBarSeries = ({ histogramSeries, minInterval }: MonitorBarSeriesProps) => { const { - colors: { danger }, - chartTheme, - } = useContext(UptimeThemeContext); + services: { charts }, + } = useKibana<ClientPluginsStart>(); + const baseTheme = charts.theme.useChartsBaseTheme(); + + const theme = useEuiTheme(); + const danger = theme.euiTheme.colors.danger; + const [getUrlParams, updateUrlParams] = useUrlParams(); const { absoluteDateRangeStart, absoluteDateRangeEnd } = getUrlParams(); @@ -79,8 +84,7 @@ export const MonitorBarSeries = ({ histogramSeries, minInterval }: MonitorBarSer onBrushEnd={onBrushEnd} onElementClick={onBarClicked} locale={i18n.getLocale()} - // TODO connect to charts.theme service see src/plugins/charts/public/services/theme/README.md - {...chartTheme} + baseTheme={baseTheme} /> <Axis hide @@ -112,11 +116,21 @@ export const MonitorBarSeries = ({ histogramSeries, minInterval }: MonitorBarSer <FormattedMessage id="xpack.uptime.monitorList.noDownHistory" defaultMessage="This monitor has never been {emphasizedText} during the selected time range." - values={{ emphasizedText: <strong>down</strong> }} + values={{ + emphasizedText: ( + <strong> + {i18n.translate('xpack.uptime.monitorBarSeries.strong.downLabel', { + defaultMessage: 'down', + })} + </strong> + ), + }} /> } > - <EuiText color="success">--</EuiText> + <EuiText color="success"> + {i18n.translate('xpack.uptime.monitorBarSeries.TextLabel', { defaultMessage: '--' })} + </EuiText> </EuiToolTip> ); }; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/ping_histogram.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/ping_histogram.tsx index a1471dfe1715..1bad832a87d0 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/ping_histogram.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/common/charts/ping_histogram.tsx @@ -17,15 +17,15 @@ import { ElementClickListener, ScaleType, } from '@elastic/charts'; -import { EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiTitle, EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useContext } from 'react'; +import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import numeral from '@elastic/numeral'; import moment from 'moment'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; import { getChartDateLabel } from '../../../lib/helper'; import { ChartWrapper } from './chart_wrapper'; -import { UptimeThemeContext } from '../../../contexts'; import { HistogramResult } from '../../../../../common/runtime_types'; import { useUrlParams } from '../../../hooks'; import { ChartEmptyState } from './chart_empty_state'; @@ -34,6 +34,7 @@ import { STATUS_DOWN_LABEL, STATUS_UP_LABEL, } from '../../../../../common/translations/translations'; +import { ClientPluginsStart } from '../../../../plugin'; export interface PingHistogramComponentProps { /** @@ -72,9 +73,13 @@ export const PingHistogramComponent: React.FC<PingHistogramComponentProps> = ({ timeZone, }) => { const { - colors: { danger, gray }, - chartTheme, - } = useContext(UptimeThemeContext); + services: { charts }, + } = useKibana<ClientPluginsStart>(); + const baseTheme = charts.theme.useChartsBaseTheme(); + + const theme = useEuiTheme(); + const danger = theme.euiTheme.colors.danger; + const gray = theme.euiTheme.colors.lightShade; const [_getUrlParams, updateUrlParams] = useUrlParams(); @@ -143,8 +148,7 @@ export const PingHistogramComponent: React.FC<PingHistogramComponentProps> = ({ onBrushEnd={onBrushEnd} onElementClick={onBarClicked} locale={i18n.getLocale()} - // TODO connect to charts.theme service see src/plugins/charts/public/services/theme/README.md - {...chartTheme} + baseTheme={baseTheme} /> <Axis id={i18n.translate('xpack.uptime.snapshotHistogram.xAxisId', { diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/__snapshots__/monitor_charts.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/__snapshots__/monitor_charts.test.tsx.snap index 4fb746efe281..554d34c88acf 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/__snapshots__/monitor_charts.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/__snapshots__/monitor_charts.test.tsx.snap @@ -1,39 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`MonitorCharts component renders the component without errors 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -41,55 +30,13 @@ exports[`MonitorCharts component renders the component without errors 1`] = ` "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ - Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - "push": [Function], - "replace": [Function], - } - } - > - <CompatRouter> - <MonitorCharts - monitorId="something" - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + <MonitorCharts + monitorId="something" + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/ml/__snapshots__/ml_integerations.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/ml/__snapshots__/ml_integerations.test.tsx.snap index 5b8d20ae63ba..1edd320bcc71 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/ml/__snapshots__/ml_integerations.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/ml/__snapshots__/ml_integerations.test.tsx.snap @@ -29,39 +29,28 @@ exports[`ML Integrations renders without errors 1`] = ` `; exports[`ML Integrations shallow renders without errors 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -69,53 +58,11 @@ exports[`ML Integrations shallow renders without errors 1`] = ` "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ - Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - "push": [Function], - "replace": [Function], - } - } - > - <CompatRouter> - <MLIntegrationComponent /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + <MLIntegrationComponent /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/ping_list/columns/ping_status.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/ping_list/columns/ping_status.tsx index e63bc4f08e6a..9e5fef631507 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/ping_list/columns/ping_status.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/ping_list/columns/ping_status.tsx @@ -5,13 +5,12 @@ * 2.0. */ -import React, { useContext } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; -import { EuiBadge, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiBadge, EuiSpacer, EuiText, useEuiTheme } from '@elastic/eui'; import { Ping } from '../../../../../../common/runtime_types/ping'; import { MONITOR_TYPES, STATUS } from '../../../../../../common/constants'; -import { UptimeThemeContext } from '../../../../contexts'; import { STATUS_COMPLETE_LABEL, STATUS_DOWN_LABEL, @@ -32,9 +31,8 @@ const getPingStatusLabel = (status: string, ping: Ping) => { }; export const PingStatusColumn = ({ pingStatus, item }: Props) => { - const { - colors: { dangerBehindText }, - } = useContext(UptimeThemeContext); + const theme = useEuiTheme(); + const dangerBehindText = theme.euiTheme.colors.textDanger; const timeStamp = moment(item.timestamp); diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap index d8acb8a3cd71..84360ef93c8f 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap @@ -59,7 +59,7 @@ Array [ <a class="euiLink emotion-euiLink-primary" data-test-subj="syntheticsMonitorTagsSetTagsLink" - href="undefined/monitor-options.html#monitor-tags" + href="https://www.elastic.co/guide/en/beats/heartbeat/mocked-test-branch/monitor-options.html#monitor-tags" rel="noopener noreferrer" target="_blank" > diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/ssl_certificate.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/ssl_certificate.test.tsx.snap index 1e50379a0955..4729f1942f4c 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/ssl_certificate.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/ssl_certificate.test.tsx.snap @@ -65,39 +65,28 @@ Array [ `; exports[`SSL Certificate component shallow renders 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -105,60 +94,18 @@ exports[`SSL Certificate component shallow renders 1`] = ` "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ + <MonitorSSLCertificate + tls={ Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - "push": [Function], - "replace": [Function], + "certificate_not_valid_after": "2020-04-24T11:41:38.200Z", + "certificate_not_valid_before": "2019-04-24T11:41:38.200Z", } } - > - <CompatRouter> - <MonitorSSLCertificate - tls={ - Object { - "certificate_not_valid_after": "2020-04-24T11:41:38.200Z", - "certificate_not_valid_before": "2019-04-24T11:41:38.200Z", - } - } - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/availability_reporting/location_status_tags.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/availability_reporting/location_status_tags.tsx index b50b84235dad..f53636d5a753 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/availability_reporting/location_status_tags.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/monitor/status_details/availability_reporting/location_status_tags.tsx @@ -5,10 +5,10 @@ * 2.0. */ -import React, { useContext } from 'react'; +import React from 'react'; import moment from 'moment'; import styled from 'styled-components'; -import { UptimeThemeContext } from '../../../../contexts'; +import { useEuiTheme } from '@elastic/eui'; import { MonitorLocation } from '../../../../../../common/runtime_types'; import { SHORT_TIMESPAN_LOCALE, SHORT_TS_LOCALE } from '../../../../../../common/constants'; import { AvailabilityReporting } from '..'; @@ -33,9 +33,9 @@ export interface StatusTag { } export const LocationStatusTags = ({ locations }: Props) => { - const { - colors: { gray, danger }, - } = useContext(UptimeThemeContext); + const theme = useEuiTheme(); + const danger = theme.euiTheme.colors.danger; + const gray = theme.euiTheme.colors.lightShade; const allLocations: StatusTag[] = []; const prevLocal: string = moment.locale() ?? 'en'; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/__snapshots__/filter_status_button.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/__snapshots__/filter_status_button.test.tsx.snap index 8a2095bfdfdf..dd4e3c9ff1e7 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/__snapshots__/filter_status_button.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/__snapshots__/filter_status_button.test.tsx.snap @@ -21,39 +21,28 @@ exports[`FilterStatusButton renders without errors for valid props 1`] = ` `; exports[`FilterStatusButton shallow renders without errors for valid props 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -61,59 +50,17 @@ exports[`FilterStatusButton shallow renders without errors for valid props 1`] = "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ - Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - "push": [Function], - "replace": [Function], - } - } - > - <CompatRouter> - <FilterStatusButton - content="Up" - dataTestSubj="foo" - isActive={true} - value="up" - withNext={true} - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + <FilterStatusButton + content="Up" + dataTestSubj="foo" + isActive={true} + value="up" + withNext={true} + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap index d25aecb486f6..a9d2b7baf2cc 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap @@ -59,39 +59,28 @@ exports[`StatusFilterComponent renders without errors for valid props 1`] = ` `; exports[`StatusFilterComponent shallow renders without errors for valid props 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -99,53 +88,11 @@ exports[`StatusFilterComponent shallow renders without errors for valid props 1` "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ - Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - "push": [Function], - "replace": [Function], - } - } - > - <CompatRouter> - <StatusFilter /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + <StatusFilter /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/columns/status_badge.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/columns/status_badge.tsx index 04311ff66aa8..5abf56c0189e 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/columns/status_badge.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/columns/status_badge.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import { EuiBadge, EuiToolTip } from '@elastic/eui'; -import React, { useContext } from 'react'; +import { EuiBadge, EuiToolTip, useEuiTheme } from '@elastic/eui'; +import React from 'react'; import { STATUS } from '../../../../../../common/constants'; import { getHealthMessage } from './monitor_status_column'; -import { UptimeThemeContext } from '../../../../contexts'; import { PingError } from '../../../../../../common/runtime_types'; export const StatusBadge = ({ @@ -19,9 +18,8 @@ export const StatusBadge = ({ status: string; summaryError?: PingError; }) => { - const { - colors: { dangerBehindText }, - } = useContext(UptimeThemeContext); + const theme = useEuiTheme(); + const dangerBehindText = theme.euiTheme.colors.textDanger; if (status === STATUS.UP) { return ( diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/monitor_list_drawer.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/monitor_list_drawer.test.tsx.snap index c596dbb57eaa..ac50a33f03af 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/monitor_list_drawer.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/monitor_list_drawer.test.tsx.snap @@ -1,39 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`MonitorListDrawer component renders a MonitorListDrawer when there are many checks 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -41,183 +30,130 @@ exports[`MonitorListDrawer component renders a MonitorListDrawer when there are "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ + <MonitorListDrawerComponent + loading={false} + monitorDetails={ Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, + "error": Object { + "message": "Get https://expired.badssl.com: x509: certificate has expired or is not yet valid", + "type": "io", }, - "push": [Function], - "replace": [Function], + "monitorId": "bad-ssl", } } - > - <CompatRouter> - <MonitorListDrawerComponent - loading={false} - monitorDetails={ - Object { - "error": Object { - "message": "Get https://expired.badssl.com: x509: certificate has expired or is not yet valid", - "type": "io", - }, - "monitorId": "bad-ssl", - } - } - summary={ - Object { - "monitor_id": "foo", - "state": Object { + summary={ + Object { + "monitor_id": "foo", + "state": Object { + "monitor": Object { + "type": "http", + }, + "summary": Object { + "down": 0, + "up": 1, + }, + "summaryPings": Array [ + Object { + "docId": "foo", "monitor": Object { - "type": "http", - }, - "summary": Object { - "down": 0, - "up": 1, - }, - "summaryPings": Array [ - Object { - "docId": "foo", - "monitor": Object { - "check_group": "myCheckGroup", - "duration": Object { - "us": 121, - }, - "id": "foo", - "ip": "127.0.0.1", - "name": undefined, - "status": "up", - "type": "icmp", - }, - "timestamp": "121", + "check_group": "myCheckGroup", + "duration": Object { + "us": 121, }, - Object { - "docId": "foo-0", - "monitor": Object { - "check_group": "myCheckGroup", - "duration": Object { - "us": 100000, - }, - "id": "foo", - "ip": "127.0.0.1", - "name": undefined, - "status": "down", - "type": "icmp", - }, - "timestamp": "0", + "id": "foo", + "ip": "127.0.0.1", + "name": undefined, + "status": "up", + "type": "icmp", + }, + "timestamp": "121", + }, + Object { + "docId": "foo-0", + "monitor": Object { + "check_group": "myCheckGroup", + "duration": Object { + "us": 100000, }, - Object { - "docId": "foo-1", - "monitor": Object { - "check_group": "myCheckGroup", - "duration": Object { - "us": 1, - }, - "id": "foo", - "ip": "127.0.0.2", - "name": undefined, - "status": "up", - "type": "icmp", - }, - "timestamp": "1", + "id": "foo", + "ip": "127.0.0.1", + "name": undefined, + "status": "down", + "type": "icmp", + }, + "timestamp": "0", + }, + Object { + "docId": "foo-1", + "monitor": Object { + "check_group": "myCheckGroup", + "duration": Object { + "us": 1, }, - Object { - "docId": "foo-2", - "monitor": Object { - "check_group": "myCheckGroup", - "duration": Object { - "us": 2, - }, - "id": "foo", - "ip": "127.0.0.3", - "name": undefined, - "status": "down", - "type": "icmp", - }, - "timestamp": "2", + "id": "foo", + "ip": "127.0.0.2", + "name": undefined, + "status": "up", + "type": "icmp", + }, + "timestamp": "1", + }, + Object { + "docId": "foo-2", + "monitor": Object { + "check_group": "myCheckGroup", + "duration": Object { + "us": 2, }, - ], - "timestamp": "123", - "url": Object { - "domain": "expired.badssl.com", - "full": "https://expired.badssl.com", + "id": "foo", + "ip": "127.0.0.3", + "name": undefined, + "status": "down", + "type": "icmp", }, + "timestamp": "2", }, - } - } - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + ], + "timestamp": "123", + "url": Object { + "domain": "expired.badssl.com", + "full": "https://expired.badssl.com", + }, + }, + } + } + /> +</Router> `; exports[`MonitorListDrawer component renders a MonitorListDrawer when there is only one check 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -225,100 +161,58 @@ exports[`MonitorListDrawer component renders a MonitorListDrawer when there is o "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ + <MonitorListDrawerComponent + loading={false} + monitorDetails={ Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, + "error": Object { + "message": "Get https://expired.badssl.com: x509: certificate has expired or is not yet valid", + "type": "io", }, - "push": [Function], - "replace": [Function], + "monitorId": "bad-ssl", } } - > - <CompatRouter> - <MonitorListDrawerComponent - loading={false} - monitorDetails={ - Object { - "error": Object { - "message": "Get https://expired.badssl.com: x509: certificate has expired or is not yet valid", - "type": "io", - }, - "monitorId": "bad-ssl", - } - } - summary={ - Object { - "monitor_id": "foo", - "state": Object { + summary={ + Object { + "monitor_id": "foo", + "state": Object { + "monitor": Object { + "type": "http", + }, + "summary": Object { + "down": 0, + "up": 1, + }, + "summaryPings": Array [ + Object { + "docId": "foo", "monitor": Object { - "type": "http", - }, - "summary": Object { - "down": 0, - "up": 1, - }, - "summaryPings": Array [ - Object { - "docId": "foo", - "monitor": Object { - "check_group": "myCheckGroup", - "duration": Object { - "us": 121, - }, - "id": "foo", - "ip": "127.0.0.1", - "name": undefined, - "status": "up", - "type": "icmp", - }, - "timestamp": "121", + "check_group": "myCheckGroup", + "duration": Object { + "us": 121, }, - ], - "timestamp": "123", - "url": Object { - "domain": "expired.badssl.com", - "full": "https://expired.badssl.com", + "id": "foo", + "ip": "127.0.0.1", + "name": undefined, + "status": "up", + "type": "icmp", }, + "timestamp": "121", }, - } - } - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + ], + "timestamp": "123", + "url": Object { + "domain": "expired.badssl.com", + "full": "https://expired.badssl.com", + }, + }, + } + } + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/settings/__snapshots__/certificate_form.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/settings/__snapshots__/certificate_form.test.tsx.snap index aa44f05e3763..c5ba5f3d0bcf 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/settings/__snapshots__/certificate_form.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/settings/__snapshots__/certificate_form.test.tsx.snap @@ -1,39 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`CertificateForm shallow renders expected elements for valid props 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -41,66 +30,24 @@ exports[`CertificateForm shallow renders expected elements for valid props 1`] = "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ + <CertificateExpirationForm + fieldErrors={null} + formFields={ Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - "push": [Function], - "replace": [Function], + "certAgeThreshold": 36, + "certExpirationThreshold": 7, + "defaultConnectors": Array [], + "heartbeatIndices": "heartbeat-8*", } } - > - <CompatRouter> - <CertificateExpirationForm - fieldErrors={null} - formFields={ - Object { - "certAgeThreshold": 36, - "certExpirationThreshold": 7, - "defaultConnectors": Array [], - "heartbeatIndices": "heartbeat-8*", - } - } - isDisabled={false} - loading={false} - onChange={[MockFunction]} - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + isDisabled={false} + loading={false} + onChange={[MockFunction]} + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/settings/__snapshots__/indices_form.test.tsx.snap b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/settings/__snapshots__/indices_form.test.tsx.snap index e12ec38a52d8..4b7dc3cc1dec 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/settings/__snapshots__/indices_form.test.tsx.snap +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/settings/__snapshots__/indices_form.test.tsx.snap @@ -1,39 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`CertificateForm shallow renders expected elements for valid props 1`] = ` -<ContextProvider - value={ +<Router + history={ Object { - "history": Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { + "action": "POP", + "block": [Function], + "canGo": [Function], + "createHref": [Function], + "entries": Array [ + Object { "hash": "", "key": "TestKeyForTesting", "pathname": "/", "search": "", "state": undefined, }, - "push": [Function], - "replace": [Function], - }, + ], + "go": [Function], + "goBack": [Function], + "goForward": [Function], + "index": 0, + "length": 1, + "listen": [Function], "location": Object { "hash": "", "key": "TestKeyForTesting", @@ -41,66 +30,24 @@ exports[`CertificateForm shallow renders expected elements for valid props 1`] = "search": "", "state": undefined, }, - "match": Object { - "isExact": true, - "params": Object {}, - "path": "/", - "url": "/", - }, - "staticContext": undefined, + "push": [Function], + "replace": [Function], } } > - <ContextProvider - value={ + <IndicesForm + fieldErrors={null} + formFields={ Object { - "action": "POP", - "block": [Function], - "canGo": [Function], - "createHref": [Function], - "entries": Array [ - Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - ], - "go": [Function], - "goBack": [Function], - "goForward": [Function], - "index": 0, - "length": 1, - "listen": [Function], - "location": Object { - "hash": "", - "key": "TestKeyForTesting", - "pathname": "/", - "search": "", - "state": undefined, - }, - "push": [Function], - "replace": [Function], + "certAgeThreshold": 36, + "certExpirationThreshold": 7, + "defaultConnectors": Array [], + "heartbeatIndices": "heartbeat-8*", } } - > - <CompatRouter> - <IndicesForm - fieldErrors={null} - formFields={ - Object { - "certAgeThreshold": 36, - "certExpirationThreshold": 7, - "defaultConnectors": Array [], - "heartbeatIndices": "heartbeat-8*", - } - } - isDisabled={false} - loading={false} - onChange={[MockFunction]} - /> - </CompatRouter> - </ContextProvider> -</ContextProvider> + isDisabled={false} + loading={false} + onChange={[MockFunction]} + /> +</Router> `; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/console_event.test.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/console_event.test.tsx index 6a3c6d02151e..2db3b2ec004f 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/console_event.test.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/console_event.test.tsx @@ -46,7 +46,7 @@ describe('ConsoleEvent component', () => { grow={false} style={ Object { - "color": "#bd271e", + "color": "#BD271E", } } > diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/console_event.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/console_event.tsx index aeceef2477e2..fb37ff680422 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/console_event.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/console_event.tsx @@ -5,9 +5,8 @@ * 2.0. */ -import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; -import React, { useContext, FC } from 'react'; -import { UptimeThemeContext } from '../../contexts'; +import { EuiFlexItem, EuiFlexGroup, useEuiTheme } from '@elastic/eui'; +import React, { FC } from 'react'; import { JourneyStep } from '../../../../common/runtime_types/ping'; interface Props { @@ -15,9 +14,8 @@ interface Props { } export const ConsoleEvent: FC<Props> = ({ event }) => { - const { - colors: { danger }, - } = useContext(UptimeThemeContext); + const theme = useEuiTheme(); + const danger = theme.euiTheme.colors.danger; let typeColor: string | undefined; if (event.synthetics?.type === 'stderr') { diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/status_badge.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/status_badge.tsx index a499f2f65885..49c466e8daf5 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/status_badge.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/status_badge.tsx @@ -5,11 +5,9 @@ * 2.0. */ -import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText, useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useContext, FC } from 'react'; -import { UptimeAppColors } from '../../app/uptime_app'; -import { UptimeThemeContext } from '../../contexts'; +import React, { FC } from 'react'; interface StatusBadgeProps { isMobile?: boolean; @@ -17,12 +15,18 @@ interface StatusBadgeProps { stepNo: number; } -export function colorFromStatus(color: UptimeAppColors, status?: string) { +export function colorFromStatus( + color: { + success: string; + danger: string; + }, + status?: string +) { switch (status) { case 'succeeded': return color.success; case 'failed': - return color.dangerBehindText; + return color.danger; default: return 'default'; } @@ -48,7 +52,8 @@ export function textFromStatus(status?: string) { } export const StatusBadge: FC<StatusBadgeProps> = ({ status, stepNo, isMobile }) => { - const theme = useContext(UptimeThemeContext); + const theme = useEuiTheme(); + return ( <EuiFlexGroup alignItems="center" gutterSize="s"> {!isMobile && ( @@ -59,7 +64,17 @@ export const StatusBadge: FC<StatusBadgeProps> = ({ status, stepNo, isMobile }) </EuiFlexItem> )} <EuiFlexItem grow={false}> - <EuiBadge color={colorFromStatus(theme.colors, status)}>{textFromStatus(status)}</EuiBadge> + <EuiBadge + color={colorFromStatus( + { + danger: theme.euiTheme.colors.danger, + success: theme.euiTheme.colors.success, + }, + status + )} + > + {textFromStatus(status)} + </EuiBadge> </EuiFlexItem> </EuiFlexGroup> ); diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/step_screenshot_display.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/step_screenshot_display.tsx index 2e5b8aac0d72..8c158c86fd05 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/step_screenshot_display.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/components/synthetics/step_screenshot_display.tsx @@ -12,6 +12,7 @@ import { EuiImage, EuiLoadingSpinner, EuiText, + useEuiTheme, } from '@elastic/eui'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; @@ -23,7 +24,7 @@ import { isScreenshotRef as isAScreenshotRef, ScreenshotRefImageData, } from '../../../../common/runtime_types'; -import { UptimeRefreshContext, UptimeSettingsContext, UptimeThemeContext } from '../../contexts'; +import { UptimeRefreshContext, UptimeSettingsContext } from '../../contexts'; import { getJourneyScreenshot } from '../../state/api/journey'; import { useCompositeImage } from '../../hooks'; @@ -103,9 +104,9 @@ export const StepScreenshotDisplay: FC<StepScreenshotDisplayProps> = ({ lazyLoad = true, }) => { const containerRef = useRef(null); - const { - colors: { lightestShade: pageBackground }, - } = useContext(UptimeThemeContext); + + const theme = useEuiTheme(); + const pageBackground = theme.euiTheme.colors.lightestShade; const { basePath } = useContext(UptimeSettingsContext); diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/contexts/index.ts b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/contexts/index.ts index 8467691064bf..cd4f2b841e8f 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/contexts/index.ts +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/contexts/index.ts @@ -8,7 +8,6 @@ export { UptimeRefreshContext, UptimeRefreshContextProvider } from './uptime_refresh_context'; export type { UptimeSettingsContextValues } from './uptime_settings_context'; export { UptimeSettingsContext, UptimeSettingsContextProvider } from './uptime_settings_context'; -export { UptimeThemeContextProvider, UptimeThemeContext } from './uptime_theme_context'; export { UptimeStartupPluginsContext, UptimeStartupPluginsContextProvider, diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/contexts/uptime_theme_context.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/contexts/uptime_theme_context.tsx deleted file mode 100644 index 97787ba9db4e..000000000000 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/contexts/uptime_theme_context.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { euiLightVars, euiDarkVars } from '@kbn/ui-theme'; -import React, { createContext, useMemo, FC, PropsWithChildren } from 'react'; -import { DARK_THEME, LIGHT_THEME, PartialTheme, Theme } from '@elastic/charts'; -import { UptimeAppColors } from '../app/uptime_app'; - -export interface UptimeThemeContextValues { - colors: UptimeAppColors; - chartTheme: { - baseTheme?: Theme; - theme?: PartialTheme; - }; -} - -/** - * These are default values for the context. These defaults are typically - * overwritten by the Uptime App upon its invocation. - */ -const defaultContext: UptimeThemeContextValues = { - colors: { - danger: euiLightVars.euiColorDanger, - dangerBehindText: euiDarkVars.euiColorVis9_behindText, - mean: euiLightVars.euiColorPrimary, - range: euiLightVars.euiFocusBackgroundColor, - success: euiLightVars.euiColorSuccess, - warning: euiLightVars.euiColorWarning, - gray: euiLightVars.euiColorLightShade, - lightestShade: euiLightVars.euiColorLightestShade, - }, - chartTheme: { - baseTheme: LIGHT_THEME, - }, -}; - -export const UptimeThemeContext = createContext(defaultContext); - -interface ThemeContextProps { - darkMode: boolean; -} - -export const UptimeThemeContextProvider: FC<PropsWithChildren<ThemeContextProps>> = ({ - darkMode, - children, -}) => { - let colors: UptimeAppColors; - if (darkMode) { - colors = { - danger: euiDarkVars.euiColorVis9, - dangerBehindText: euiDarkVars.euiColorVis9_behindText, - mean: euiDarkVars.euiColorPrimary, - gray: euiDarkVars.euiColorLightShade, - range: euiDarkVars.euiFocusBackgroundColor, - success: euiDarkVars.euiColorSuccess, - warning: euiDarkVars.euiColorWarning, - lightestShade: euiDarkVars.euiColorLightestShade, - }; - } else { - colors = { - danger: euiLightVars.euiColorVis9, - dangerBehindText: euiLightVars.euiColorVis9_behindText, - mean: euiLightVars.euiColorPrimary, - gray: euiLightVars.euiColorLightShade, - range: euiLightVars.euiFocusBackgroundColor, - success: euiLightVars.euiColorSuccess, - warning: euiLightVars.euiColorWarning, - lightestShade: euiLightVars.euiColorLightestShade, - }; - } - const value = useMemo(() => { - return { - colors, - chartTheme: { - baseTheme: darkMode ? DARK_THEME : LIGHT_THEME, - }, - }; - }, [colors, darkMode]); - - return <UptimeThemeContext.Provider value={value} children={children} />; -}; diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/enzyme_helpers.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/enzyme_helpers.tsx index 984344de2e5e..540e0fdac2c6 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/enzyme_helpers.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/enzyme_helpers.tsx @@ -10,10 +10,11 @@ import { Router } from '@kbn/shared-ux-router'; import { MemoryHistory } from 'history/createMemoryHistory'; import { createMemoryHistory, History } from 'history'; import { mountWithIntl, renderWithI18nProvider, shallowWithIntl } from '@kbn/test-jest-helpers'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { MountWithReduxProvider } from './helper_with_redux'; import { AppState } from '../../state'; import { mockState } from '../__mocks__/uptime_store.mock'; -import { KibanaProviderOptions, MockRouter } from './rtl_helpers'; +import { KibanaProviderOptions, mockCore, MockRouter } from './rtl_helpers'; interface RenderRouterOptions<ExtraCore> extends KibanaProviderOptions<ExtraCore> { history?: History; @@ -31,7 +32,11 @@ const helperWithRouter: <R>( history.location.key = 'TestKeyForTesting'; - const routerWrapper = <Router history={history}>{component}</Router>; + const routerWrapper = ( + <KibanaContextProvider services={{ ...mockCore() }}> + <Router history={history}>{component}</Router> + </KibanaContextProvider> + ); if (wrapReduxStore) { return helper( diff --git a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/rtl_helpers.tsx b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/rtl_helpers.tsx index 8f9264f4671b..395c9d8bfb23 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/rtl_helpers.tsx +++ b/x-pack/solutions/observability/plugins/uptime/public/legacy_uptime/lib/helper/rtl_helpers.tsx @@ -6,6 +6,7 @@ */ import React, { ReactElement, ReactNode } from 'react'; +import { i18n } from '@kbn/i18n'; import { of } from 'rxjs'; // eslint-disable-next-line import/no-extraneous-dependencies import { @@ -22,14 +23,16 @@ import { createMemoryHistory, History } from 'history'; import { CoreStart } from '@kbn/core/public'; import { I18nProvider } from '@kbn/i18n-react'; import { coreMock } from '@kbn/core/public/mocks'; -import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { KibanaContextProvider, KibanaServices } from '@kbn/kibana-react-plugin/public'; + import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { Store } from 'redux'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; +import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { stringifyUrlParams } from './url_params/stringify_url_params'; import { mockState } from '../__mocks__/uptime_store.mock'; import { MountWithReduxProvider } from './helper_with_redux'; @@ -158,9 +161,16 @@ export const mockCore: () => Partial<CoreStart> = () => { exploratoryView: { createExploratoryViewUrl: jest.fn(), getAppDataView: jest.fn(), - ExploratoryViewEmbeddable: () => <div>Embeddable exploratory view</div>, + ExploratoryViewEmbeddable: () => ( + <div> + {i18n.translate('xpack.uptime.core.div.embeddableExploratoryViewLabel', { + defaultMessage: 'Embeddable exploratory view', + })} + </div> + ), }, unifiedSearch: unifiedSearchPluginMock.createStartContract(), + charts: chartPluginMock.createStartContract(), }; return core; diff --git a/x-pack/solutions/observability/plugins/uptime/public/plugin.ts b/x-pack/solutions/observability/plugins/uptime/public/plugin.ts index b2558fe83e33..dca8c8baa19e 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/uptime/public/plugin.ts @@ -55,6 +55,7 @@ import { ObservabilityAIAssistantPublicStart, ObservabilityAIAssistantPublicSetup, } from '@kbn/observability-ai-assistant-plugin/public'; +import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import { PLUGIN } from '../common/constants/plugin'; import { UptimeConfig } from '../common/config'; import { @@ -105,6 +106,7 @@ export interface ClientPluginsStart { docLinks: DocLinksStart; uiSettings: CoreStart['uiSettings']; usageCollection: UsageCollectionStart; + charts: ChartsPluginStart; } export interface UptimePluginServices extends Partial<CoreStart> { diff --git a/x-pack/solutions/observability/plugins/uptime/tsconfig.json b/x-pack/solutions/observability/plugins/uptime/tsconfig.json index 5de407dc03b8..6761601deb20 100644 --- a/x-pack/solutions/observability/plugins/uptime/tsconfig.json +++ b/x-pack/solutions/observability/plugins/uptime/tsconfig.json @@ -36,7 +36,6 @@ "@kbn/core-http-browser", "@kbn/es-query", "@kbn/data-views-plugin", - "@kbn/ui-theme", "@kbn/rule-data-utils", "@kbn/kibana-utils-plugin", "@kbn/data-plugin", @@ -79,6 +78,7 @@ "@kbn/deeplinks-observability", "@kbn/ebt-tools", "@kbn/core-rendering-browser", + "@kbn/charts-plugin", ], "exclude": ["target/**/*"] } diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_investigation.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_investigation.tsx index 081b4ec28c6a..bd57082ba4cb 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_investigation.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_investigation.tsx @@ -126,21 +126,46 @@ const useGraphPopovers = ( }; interface GraphInvestigationProps { - dataView: DataView; - eventIds: string[]; - timestamp: string | null; + /** + * The initial state to use for the graph investigation view. + */ + initialState: { + /** + * The data view to use for the graph investigation view. + */ + dataView: DataView; + + /** + * The origin events for the graph investigation view. + */ + originEventIds: Array<{ + /** + * The ID of the origin event. + */ + id: string; + + /** + * A flag indicating whether the origin event is an alert or not. + */ + isAlert: boolean; + }>; + + /** + * The initial timerange for the graph investigation view. + */ + timeRange: TimeRange; + }; } /** * Graph investigation view allows the user to expand nodes and view related entities. */ export const GraphInvestigation: React.FC<GraphInvestigationProps> = memo( - ({ dataView, eventIds, timestamp = new Date().toISOString() }: GraphInvestigationProps) => { + ({ + initialState: { dataView, originEventIds, timeRange: initialTimeRange }, + }: GraphInvestigationProps) => { const [searchFilters, setSearchFilters] = useState<Filter[]>(() => []); - const [timeRange, setTimeRange] = useState<TimeRange>({ - from: `${timestamp}||-30m`, - to: `${timestamp}||+30m`, - }); + const [timeRange, setTimeRange] = useState<TimeRange>(initialTimeRange); const { services: { uiSettings }, @@ -153,7 +178,7 @@ export const GraphInvestigation: React.FC<GraphInvestigationProps> = memo( [...searchFilters], getEsQueryConfig(uiSettings as Parameters<typeof getEsQueryConfig>[0]) ), - [searchFilters, dataView, uiSettings] + [dataView, searchFilters, uiSettings] ); const { nodeExpandPopover, openPopoverCallback } = useGraphPopovers( @@ -166,7 +191,7 @@ export const GraphInvestigation: React.FC<GraphInvestigationProps> = memo( const { data, refresh, isFetching } = useFetchGraphData({ req: { query: { - eventIds, + originEventIds, esQuery: query, start: timeRange.from, end: timeRange.to, diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/hooks/use_fetch_graph_data.test.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/hooks/use_fetch_graph_data.test.tsx index e494ff0957ec..da5eaee9bfbf 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/hooks/use_fetch_graph_data.test.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/hooks/use_fetch_graph_data.test.tsx @@ -33,7 +33,7 @@ describe('useFetchGraphData', () => { return useFetchGraphData({ req: { query: { - eventIds: [], + originEventIds: [], start: '2021-09-01T00:00:00.000Z', end: '2021-09-01T23:59:59.999Z', }, @@ -52,7 +52,7 @@ describe('useFetchGraphData', () => { return useFetchGraphData({ req: { query: { - eventIds: [], + originEventIds: [], start: '2021-09-01T00:00:00.000Z', end: '2021-09-01T23:59:59.999Z', }, @@ -75,7 +75,7 @@ describe('useFetchGraphData', () => { return useFetchGraphData({ req: { query: { - eventIds: [], + originEventIds: [], start: '2021-09-01T00:00:00.000Z', end: '2021-09-01T23:59:59.999Z', }, @@ -98,7 +98,7 @@ describe('useFetchGraphData', () => { return useFetchGraphData({ req: { query: { - eventIds: [], + originEventIds: [], start: '2021-09-01T00:00:00.000Z', end: '2021-09-01T23:59:59.999Z', }, diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/hooks/use_fetch_graph_data.ts b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/hooks/use_fetch_graph_data.ts index 74cca4693e80..477492a3bbb7 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/hooks/use_fetch_graph_data.ts +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/hooks/use_fetch_graph_data.ts @@ -81,13 +81,13 @@ export const useFetchGraphData = ({ options, }: UseFetchGraphDataParams): UseFetchGraphDataResult => { const queryClient = useQueryClient(); - const { esQuery, eventIds, start, end } = req.query; + const { esQuery, originEventIds, start, end } = req.query; const { services: { http }, } = useKibana(); const QUERY_KEY = useMemo( - () => ['useFetchGraphData', eventIds, start, end, esQuery], - [end, esQuery, eventIds, start] + () => ['useFetchGraphData', originEventIds, start, end, esQuery], + [end, esQuery, originEventIds, start] ); const { isLoading, isError, data, isFetching } = useQuery<GraphResponse>( diff --git a/packages/kbn-securitysolution-autocomplete/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/README.md similarity index 100% rename from packages/kbn-securitysolution-autocomplete/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/index.ts new file mode 100644 index 000000000000..f3768e8cd37f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/index.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. + */ + +export * from './src/check_empty_value'; +export * from './src/es_field_selector'; +export * from './src/field_value_exists'; +export * from './src/field_value_lists'; +export * from './src/field_value_match'; +export * from './src/field_value_match_any'; +export * from './src/field_value_wildcard'; +export * from './src/filter_field_to_list'; +export * from './src/get_generic_combo_box_props'; +export * from './src/get_operators'; +export * from './src/hooks'; +export * from './src/operator'; +export * from './src/param_is_valid'; +export * from './src/param_contains_space'; + +export { default as autoCompletei18n } from './src/translations'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/jest.config.js b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/jest.config.js new file mode 100644 index 000000000000..7384086c5871 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete'], +}; diff --git a/packages/kbn-securitysolution-autocomplete/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-autocomplete/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/package.json new file mode 100644 index 000000000000..fcce028035c4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/securitysolution-autocomplete", + "version": "1.0.0", + "description": "Security Solution auto complete", + "license": "Elastic License 2.0", + "private": true, + "sideEffects": false +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/autocomplete/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/autocomplete/index.mock.ts new file mode 100644 index 000000000000..2372ad727e9f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/autocomplete/index.mock.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. + */ + +// Copied from "src/plugins/data/public/mocks.ts" but without any type information +// TODO: Remove this in favor of the data/public/mocks if/when they become available, https://github.com/elastic/kibana/issues/100715 +export const autocompleteStartMock = { + getQuerySuggestions: jest.fn(), + getValueSuggestions: jest.fn(), + hasQuerySuggestions: jest.fn(), +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.test.ts new file mode 100644 index 000000000000..4f99799bb468 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.test.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 { checkEmptyValue } from '.'; +import { getField } from '../fields/index.mock'; +import * as i18n from '../translations'; + +describe('check_empty_value', () => { + test('returns no errors if no field has been selected', () => { + const isValid = checkEmptyValue('', undefined, true, false); + + expect(isValid).toBeUndefined(); + }); + + test('returns error string if user has touched a required input and left empty', () => { + const isValid = checkEmptyValue(undefined, getField('@timestamp'), true, true); + + expect(isValid).toEqual(i18n.FIELD_REQUIRED_ERR); + }); + + test('returns no errors if required input is empty but user has not yet touched it', () => { + const isValid = checkEmptyValue(undefined, getField('@timestamp'), true, false); + + expect(isValid).toBeUndefined(); + }); + + test('returns no errors if user has touched an input that is not required and left empty', () => { + const isValid = checkEmptyValue(undefined, getField('@timestamp'), false, true); + + expect(isValid).toBeUndefined(); + }); + + test('returns no errors if user has touched an input that is not required and left empty string', () => { + const isValid = checkEmptyValue('', getField('@timestamp'), false, true); + + expect(isValid).toBeUndefined(); + }); + + test('returns null if input value is not empty string or undefined', () => { + const isValid = checkEmptyValue('hellooo', getField('@timestamp'), false, true); + + expect(isValid).toBeNull(); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.ts new file mode 100644 index 000000000000..285c91842f2f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/check_empty_value/index.ts @@ -0,0 +1,33 @@ +/* + * 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 { DataViewFieldBase } from '@kbn/es-query'; +import * as i18n from '../translations'; + +/** + * Determines if empty value is ok + */ +export const checkEmptyValue = ( + param: string | undefined, + field: DataViewFieldBase | undefined, + isRequired: boolean, + touched: boolean +): string | undefined | null => { + if (isRequired && touched && (param == null || param.trim() === '')) { + return i18n.FIELD_REQUIRED_ERR; + } + + if ( + field == null || + (isRequired && !touched) || + (!isRequired && (param == null || param === '')) + ) { + return undefined; + } + + return null; +}; diff --git a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/__snapshots__/index.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/__snapshots__/index.test.tsx.snap similarity index 100% rename from packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/__snapshots__/index.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/__snapshots__/index.test.tsx.snap diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/disabled_types_with_tooltip_text.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/disabled_types_with_tooltip_text.test.ts new file mode 100644 index 000000000000..743ba33fbbe5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/disabled_types_with_tooltip_text.test.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { disabledTypesWithTooltipText } from '../disabled_types_with_tooltip_text'; + +jest.mock('../../translations', () => ({ + BINARY_TYPE_NOT_SUPPORTED: 'Binary fields are currently unsupported', +})); +describe('disabledTypesWithTooltipText', () => { + it('should return Binary fields are currently unsupported for binary type', () => { + const type = 'binary'; + expect(disabledTypesWithTooltipText[type]).toEqual('Binary fields are currently unsupported'); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/index.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/index.test.tsx new file mode 100644 index 000000000000..10db3fcce5f5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/index.test.tsx @@ -0,0 +1,142 @@ +/* + * 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 { fireEvent, render, waitFor, within } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +import { EsFieldSelector } from '..'; +import { fields, getField } from '../../fields/index.mock'; + +describe('FieldComponent', () => { + it('should render the component enabled and displays the selected field correctly', () => { + const wrapper = render( + <EsFieldSelector + isClearable={false} + isDisabled={false} + isLoading={false} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + expect(wrapper.container).toMatchSnapshot(); + const comboBox = wrapper.getByTestId('fieldAutocompleteComboBox'); + const input = within(comboBox).getByRole('combobox'); + expect(input).toHaveAttribute('value', 'machine.os.raw'); + }); + it('should render the component disabled if isDisabled is true', () => { + const wrapper = render( + <EsFieldSelector + isClearable={false} + isDisabled={true} + isLoading={false} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + expect(wrapper.container).toMatchSnapshot(); + expect(wrapper.getByTestId('fieldAutocompleteComboBox').querySelector('input')).toBeDisabled(); + }); + it('should render the loading spinner if isLoading is true when clicked', () => { + const wrapper = render( + <EsFieldSelector + isClearable={false} + isDisabled={true} + isLoading={true} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + const fieldAutocompleteComboBox = wrapper.getByTestId('fieldAutocompleteComboBox'); + expect(wrapper.container).toMatchSnapshot(); + fireEvent.click(fieldAutocompleteComboBox); + expect(wrapper.getByRole('progressbar')).toBeInTheDocument(); + }); + it('should allow user to clear values if isClearable is true', () => { + const wrapper = render( + <EsFieldSelector + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={true} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + expect(wrapper.container).toMatchSnapshot(); + expect(wrapper.getByTestId('comboBoxClearButton')).toBeInTheDocument(); + }); + it('should change the selected value', async () => { + const wrapper = render( + <EsFieldSelector + isClearable={false} + isDisabled={true} + isLoading={false} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + const fieldAutocompleteComboBox = wrapper.getByTestId('comboBoxSearchInput'); + fireEvent.change(fieldAutocompleteComboBox, { target: { value: '_source' } }); + expect(fieldAutocompleteComboBox).toHaveValue('_source'); + }); + + it('it allows custom user input if "acceptsCustomOptions" is "true"', async () => { + const mockOnChange = jest.fn(); + const wrapper = render( + <EsFieldSelector + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + placeholder="Placeholder text" + selectedField={undefined} + acceptsCustomOptions + /> + ); + + const fieldAutocompleteComboBox = wrapper.getByTestId('comboBoxSearchInput'); + fireEvent.change(fieldAutocompleteComboBox, { target: { value: 'custom' } }); + await waitFor(() => + expect(wrapper.getByTestId('fieldAutocompleteComboBox')).toHaveTextContent('custom') + ); + }); +}); diff --git a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/use_es_field.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/use_es_field.test.ts similarity index 97% rename from packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/use_es_field.test.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/use_es_field.test.ts index 942ccbc29c38..1fb07e21fc91 100644 --- a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/use_es_field.test.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/use_es_field.test.ts @@ -1,10 +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", 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". + * 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 { DataViewFieldBase } from '@kbn/es-query'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/disabled_types_with_tooltip_text.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/disabled_types_with_tooltip_text.ts new file mode 100644 index 000000000000..5b24ecb73605 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/disabled_types_with_tooltip_text.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +interface DisabledTypesTextType { + [typeName: string]: string; +} +import * as i18n from '../translations'; + +export const disabledTypesWithTooltipText: DisabledTypesTextType = { + binary: i18n.BINARY_TYPE_NOT_SUPPORTED, +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/index.tsx new file mode 100644 index 000000000000..8ce7e7ed5507 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/index.tsx @@ -0,0 +1,107 @@ +/* + * 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 { EuiComboBox } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { FieldBaseProps } from './types'; +import { useEsField } from './use_es_field'; + +const AS_PLAIN_TEXT = { asPlainText: true }; + +interface EsFieldSelectorProps extends FieldBaseProps { + isClearable?: boolean; + isDisabled?: boolean; + isLoading?: boolean; + placeholder: string; + acceptsCustomOptions?: boolean; + showMappingConflicts?: boolean; + 'aria-label'?: string; +} + +export function EsFieldSelector({ + fieldInputWidth, + fieldTypeFilter = [], + indexPattern, + isClearable = false, + isDisabled = false, + isLoading = false, + isRequired = false, + onChange, + placeholder, + selectedField, + acceptsCustomOptions = false, + showMappingConflicts = false, + 'aria-label': ariaLabel, +}: EsFieldSelectorProps): JSX.Element { + const { + isInvalid, + comboOptions, + selectedComboOptions, + fieldWidth, + renderFields, + handleTouch, + handleValuesChange, + handleCreateCustomOption, + } = useEsField({ + indexPattern, + fieldTypeFilter, + isRequired, + selectedField, + fieldInputWidth, + showMappingConflicts, + onChange, + }); + + if (acceptsCustomOptions) { + return ( + <EuiComboBox + placeholder={placeholder} + options={comboOptions} + selectedOptions={selectedComboOptions} + onChange={handleValuesChange} + isLoading={isLoading} + isDisabled={isDisabled} + isClearable={isClearable} + isInvalid={isInvalid} + onFocus={handleTouch} + singleSelection={AS_PLAIN_TEXT} + data-test-subj="fieldAutocompleteComboBox" + style={fieldWidth} + onCreateOption={handleCreateCustomOption} + customOptionText={i18n.translate('autocomplete.customOptionText', { + defaultMessage: 'Add {searchValuePlaceholder} as a custom field', + values: { searchValuePlaceholder: '{searchValue}' }, + })} + fullWidth + renderOption={renderFields} + aria-label={ariaLabel} + /> + ); + } + + return ( + <EuiComboBox + placeholder={placeholder} + options={comboOptions} + selectedOptions={selectedComboOptions} + onChange={handleValuesChange} + isLoading={isLoading} + isDisabled={isDisabled} + isClearable={isClearable} + isInvalid={isInvalid} + onFocus={handleTouch} + singleSelection={AS_PLAIN_TEXT} + data-test-subj="fieldAutocompleteComboBox" + style={fieldWidth} + fullWidth + renderOption={renderFields} + aria-label={ariaLabel} + /> + ); +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/types.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/types.ts new file mode 100644 index 000000000000..e300df89e1c3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/types.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 { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; +import { FieldConflictsInfo } from '@kbn/securitysolution-list-utils'; +import { GetGenericComboBoxPropsReturn } from '../get_generic_combo_box_props'; + +export interface FieldBaseProps { + indexPattern: DataViewBase | undefined; + fieldTypeFilter?: string[]; + isRequired?: boolean; + selectedField?: DataViewFieldBase | undefined; + fieldInputWidth?: number; + showMappingConflicts?: boolean; + onChange: (a: DataViewFieldBase[]) => void; +} + +export interface ComboBoxFields { + availableFields: DataViewField[]; + selectedFields: DataViewField[]; +} + +export interface GetFieldComboBoxPropsReturn extends GetGenericComboBoxPropsReturn { + disabledLabelTooltipTexts: { [label: string]: string }; + mappingConflictsTooltipInfo: { [label: string]: FieldConflictsInfo[] }; +} + +export interface DataViewField extends DataViewFieldBase { + esTypes?: string[]; +} diff --git a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/use_es_field.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/use_es_field.tsx similarity index 94% rename from packages/kbn-securitysolution-autocomplete/src/es_field_selector/use_es_field.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/use_es_field.tsx index 615571d98960..1d6b84618af3 100644 --- a/packages/kbn-securitysolution-autocomplete/src/es_field_selector/use_es_field.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/es_field_selector/use_es_field.tsx @@ -1,14 +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", 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". + * 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 { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { EuiComboBoxOptionOption, EuiIcon, diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.test.tsx new file mode 100644 index 000000000000..9fc10f6ba3bc --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.test.tsx @@ -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 React from 'react'; +import { mount } from 'enzyme'; + +import { AutocompleteFieldExistsComponent } from '.'; + +describe('AutocompleteFieldExistsComponent', () => { + test('it renders field disabled', () => { + const wrapper = mount(<AutocompleteFieldExistsComponent placeholder="Placeholder text" />); + + expect( + wrapper + .find(`[data-test-subj="valuesAutocompleteComboBox existsComboxBox"] input`) + .prop('disabled') + ).toBeTruthy(); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.tsx new file mode 100644 index 000000000000..ccd27f559e12 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_exists/index.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; + +const NO_OPTIONS_FOR_EXIST: EuiComboBoxOptionOption[] = []; + +interface AutocompleteFieldExistsProps { + placeholder: string; + rowLabel?: string; + 'aria-label'?: string; +} + +export const AutocompleteFieldExistsComponent: React.FC<AutocompleteFieldExistsProps> = ({ + placeholder, + rowLabel, + 'aria-label': ariaLabel, +}): JSX.Element => ( + <EuiFormRow label={rowLabel} fullWidth> + <EuiComboBox + placeholder={placeholder} + options={NO_OPTIONS_FOR_EXIST} + selectedOptions={NO_OPTIONS_FOR_EXIST} + onChange={undefined} + isDisabled + data-test-subj="valuesAutocompleteComboBox existsComboxBox" + aria-label={ariaLabel} + fullWidth + /> + </EuiFormRow> +); + +AutocompleteFieldExistsComponent.displayName = 'AutocompleteFieldExists'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.test.tsx new file mode 100644 index 000000000000..30323eba2685 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.test.tsx @@ -0,0 +1,268 @@ +/* + * 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 { mount } from 'enzyme'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { waitFor } from '@testing-library/react'; +import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { getField } from '../fields/index.mock'; +import { AutocompleteFieldListsComponent } from '.'; +import { + getListResponseMock, + getFoundListsBySizeSchemaMock, + DATE_NOW, + IMMUTABLE, + VERSION, +} from '../list_schema/index.mock'; + +// TODO: Once these mocks are available, use them instead of hand mocking, https://github.com/elastic/kibana/issues/100715 +// const mockKibanaHttpService = coreMock.createStart().http; +// import { coreMock } from '../../../../../../../../../../src/core/public/mocks'; +const mockKibanaHttpService = jest.fn(); +const mockShowValueListModal = jest.fn(); +const MockedShowValueListModal = (props: unknown) => { + mockShowValueListModal(props); + return <></>; +}; +const mockStart = jest.fn(); +const mockKeywordList: ListSchema = { + ...getListResponseMock(), + id: 'keyword_list', + name: 'keyword list', + type: 'keyword', +}; +const mockResult = { ...getFoundListsBySizeSchemaMock() }; +mockResult.smallLists = [...mockResult.smallLists, mockKeywordList]; +mockResult.largeLists = []; +jest.mock('@kbn/securitysolution-list-hooks', () => { + const originalModule = jest.requireActual('@kbn/securitysolution-list-hooks'); + + return { + ...originalModule, + useFindListsBySize: () => ({ + error: undefined, + loading: false, + result: mockResult, + start: mockStart.mockReturnValue(mockResult), + }), + }; +}); + +describe('AutocompleteFieldListsComponent', () => { + test('it renders disabled if "isDisabled" is true', async () => { + const wrapper = mount( + <AutocompleteFieldListsComponent + httpService={mockKibanaHttpService} + isClearable={true} + isDisabled + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="some-list-id" + showValueListModal={MockedShowValueListModal} + /> + ); + + expect( + wrapper + .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] input`) + .prop('disabled') + ).toBeTruthy(); + }); + + test('it renders loading if "isLoading" is true', async () => { + const wrapper = mount( + <AutocompleteFieldListsComponent + httpService={mockKibanaHttpService} + isClearable={false} + isDisabled={false} + isLoading + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('@tags')} + selectedValue="" + showValueListModal={MockedShowValueListModal} + /> + ); + + wrapper + .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] button`) + .at(0) + .simulate('click'); + expect( + wrapper + .find( + `EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteComboBox listsComboxBox-optionsList"]` + ) + .prop('isLoading') + ).toBeTruthy(); + }); + + test('it allows user to clear values if "isClearable" is true', async () => { + const wrapper = mount( + <AutocompleteFieldListsComponent + httpService={mockKibanaHttpService} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="" + showValueListModal={MockedShowValueListModal} + /> + ); + expect( + wrapper + .find('EuiComboBox[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]') + .prop('options') + ).toEqual([{ label: 'some name', disabled: false }]); + }); + + test('it correctly displays lists that match the selected "keyword" field esType', () => { + const wrapper = mount( + <AutocompleteFieldListsComponent + httpService={mockKibanaHttpService} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('@tags')} + selectedValue="" + showValueListModal={MockedShowValueListModal} + /> + ); + + wrapper.find('[data-test-subj="comboBoxToggleListButton"] button').simulate('click'); + + expect( + wrapper + .find('EuiComboBox[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]') + .prop('options') + ).toEqual([{ label: 'keyword list', disabled: false }]); + }); + + test('it correctly displays lists that match the selected "ip" field esType', () => { + const wrapper = mount( + <AutocompleteFieldListsComponent + httpService={mockKibanaHttpService} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="" + showValueListModal={MockedShowValueListModal} + /> + ); + + wrapper.find('[data-test-subj="comboBoxToggleListButton"] button').simulate('click'); + + expect( + wrapper + .find('EuiComboBox[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]') + .prop('options') + ).toEqual([{ label: 'some name', disabled: false }]); + }); + + test('it correctly displays selected list', async () => { + const wrapper = mount( + <AutocompleteFieldListsComponent + httpService={mockKibanaHttpService} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="some-list-id" + showValueListModal={MockedShowValueListModal} + /> + ); + + expect( + wrapper + .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] input`) + .at(0) + .props().value + ).toEqual('some name'); + }); + + test('it invokes "onChange" when option selected', async () => { + const mockOnChange = jest.fn(); + const wrapper = mount( + <AutocompleteFieldListsComponent + httpService={mockKibanaHttpService} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="" + showValueListModal={MockedShowValueListModal} + /> + ); + + ( + wrapper.find(EuiComboBox).props() as unknown as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + } + ).onChange([{ label: 'some name', disabled: false }]); + + await waitFor(() => { + expect(mockOnChange).toHaveBeenCalledWith({ + '@timestamp': DATE_NOW, + _version: undefined, + created_at: DATE_NOW, + created_by: 'some user', + description: 'some description', + deserializer: undefined, + id: 'some-list-id', + immutable: IMMUTABLE, + meta: {}, + name: 'some name', + serializer: undefined, + tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', + type: 'ip', + updated_at: DATE_NOW, + updated_by: 'some user', + version: VERSION, + }); + }); + }); + + test('it render the value list modal', async () => { + mockShowValueListModal.mockReset(); + mount( + <AutocompleteFieldListsComponent + httpService={mockKibanaHttpService} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="some-list-id" + showValueListModal={MockedShowValueListModal} + /> + ); + + expect(mockShowValueListModal).toHaveBeenCalledWith( + expect.objectContaining({ + children: 'Show value list', + listId: 'some-list-id', + shouldShowContentIfModalNotAvailable: false, + }) + ); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.tsx new file mode 100644 index 000000000000..480791b001c5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.tsx @@ -0,0 +1,182 @@ +/* + * 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, { ElementType, useCallback, useEffect, useMemo, useState } from 'react'; +import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow, EuiLink, EuiText } from '@elastic/eui'; +import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { useFindListsBySize } from '@kbn/securitysolution-list-hooks'; +import { DataViewFieldBase } from '@kbn/es-query'; +import { getDocLinks } from '@kbn/doc-links'; + +import { filterFieldToList } from '../filter_field_to_list'; +import { getGenericComboBoxProps } from '../get_generic_combo_box_props'; + +// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 +// import { HttpStart } from '@kbn/core/public'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type HttpStart = any; + +import * as i18n from '../translations'; + +const SINGLE_SELECTION = { asPlainText: true }; + +interface AutocompleteFieldListsProps { + httpService: HttpStart; + isClearable: boolean; + isDisabled: boolean; + isLoading: boolean; + onChange: (arg: ListSchema) => void; + placeholder: string; + rowLabel?: string; + selectedField: DataViewFieldBase | undefined; + selectedValue: string | undefined; + allowLargeValueLists?: boolean; + 'aria-label'?: string; + showValueListModal: ElementType; +} + +export interface AutocompleteListsData { + smallLists: ListSchema[]; + largeLists: ListSchema[]; +} + +export const AutocompleteFieldListsComponent: React.FC<AutocompleteFieldListsProps> = ({ + httpService, + isClearable = false, + isDisabled = false, + isLoading = false, + onChange, + placeholder, + rowLabel, + selectedField, + selectedValue, + allowLargeValueLists = false, + 'aria-label': ariaLabel, + showValueListModal, +}): JSX.Element => { + const [error, setError] = useState<string | undefined>(undefined); + const [listData, setListData] = useState<AutocompleteListsData>({ + smallLists: [], + largeLists: [], + }); + const { loading, result, start } = useFindListsBySize(); + const getLabel = useCallback(({ name }: ListSchema) => name, []); + + const optionsMemo = useMemo( + () => filterFieldToList(listData, selectedField), + [listData, selectedField] + ); + const selectedOptionsMemo = useMemo(() => { + if (selectedValue != null) { + const combinedLists = [...listData.smallLists, ...listData.largeLists]; + const list = combinedLists.filter(({ id }) => id === selectedValue); + return list ?? []; + } else { + return []; + } + }, [selectedValue, listData]); + const { comboOptions, labels, selectedComboOptions } = useMemo( + () => + getGenericComboBoxProps<ListSchema>({ + getLabel, + options: [...optionsMemo.smallLists, ...optionsMemo.largeLists], + selectedOptions: selectedOptionsMemo, + disabledOptions: allowLargeValueLists ? undefined : optionsMemo.largeLists, // Disable large lists if the rule type doesn't allow it + }), + [optionsMemo, selectedOptionsMemo, getLabel, allowLargeValueLists] + ); + + const handleValuesChange = useCallback( + (newOptions: EuiComboBoxOptionOption[]) => { + const combinedLists = [...optionsMemo.smallLists, ...optionsMemo.largeLists]; + const [newValue] = newOptions.map(({ label }) => combinedLists[labels.indexOf(label)]); + onChange(newValue ?? ''); + }, + [labels, optionsMemo, onChange] + ); + + const setIsTouchedValue = useCallback((): void => { + setError(selectedValue == null ? i18n.FIELD_REQUIRED_ERR : undefined); + }, [selectedValue]); + + useEffect(() => { + if (result != null) { + setListData(result); + } + }, [result]); + + useEffect(() => { + if (selectedField != null && httpService != null) { + start({ + http: httpService, + pageIndex: 1, + pageSize: 500, + }); + } + }, [selectedField, start, httpService]); + + const isLoadingState = useMemo((): boolean => isLoading || loading, [isLoading, loading]); + const ShowValueListModal = showValueListModal; + + const helpText = useMemo(() => { + return ( + <> + {selectedValue && ( + <ShowValueListModal shouldShowContentIfModalNotAvailable={false} listId={selectedValue}> + {i18n.SHOW_VALUE_LIST_MODAL} + </ShowValueListModal> + )} + {!allowLargeValueLists && ( + <EuiText size="xs"> + {i18n.LISTS_TOOLTIP_INFO}{' '} + <EuiLink + external + target="_blank" + href={ + getDocLinks({ + kibanaBranch: 'main', + buildFlavor: 'traditional', + }).securitySolution.exceptions.value_lists + } + > + {i18n.SEE_DOCUMENTATION} + </EuiLink> + </EuiText> + )} + </> + ); + }, [allowLargeValueLists, selectedValue, ShowValueListModal]); + return ( + <EuiFormRow + label={rowLabel} + error={error} + isInvalid={error != null} + helpText={helpText} + fullWidth + > + <EuiComboBox + async + data-test-subj="valuesAutocompleteComboBox listsComboxBox" + fullWidth + isClearable={isClearable} + isDisabled={isDisabled} + isInvalid={error != null} + isLoading={isLoadingState} + onBlur={setIsTouchedValue} + onChange={handleValuesChange} + options={comboOptions} + placeholder={placeholder} + selectedOptions={selectedComboOptions} + singleSelection={SINGLE_SELECTION} + sortMatchesBy="startsWith" + aria-label={ariaLabel} + /> + </EuiFormRow> + ); +}; + +AutocompleteFieldListsComponent.displayName = 'AutocompleteFieldList'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx new file mode 100644 index 000000000000..8ca17a78764c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx @@ -0,0 +1,629 @@ +/* + * 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 { ReactWrapper, mount } from 'enzyme'; +import { + EuiComboBox, + EuiComboBoxOptionOption, + EuiFormHelpText, + EuiSuperSelect, +} from '@elastic/eui'; +import { act, waitFor } from '@testing-library/react'; +import { AutocompleteFieldMatchComponent } from '.'; +import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; +import { fields, getField } from '../fields/index.mock'; +import { autocompleteStartMock } from '../autocomplete/index.mock'; + +jest.mock('../hooks/use_field_value_autocomplete'); +jest.mock('../translations', () => ({ + FIELD_SPACE_WARNING: 'Warning: there is a space', +})); +describe('AutocompleteFieldMatchComponent', () => { + let wrapper: ReactWrapper; + + const getValueSuggestionsMock = jest + .fn() + .mockResolvedValue([false, true, ['value 3', 'value 4'], jest.fn()]); + + const findEuiComboBox = () => + wrapper.find(EuiComboBox).props() as unknown as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + onSearchChange: (a: string) => void; + onCreateOption: (a: string) => void; + }; + + beforeEach(() => { + (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ + false, + true, + ['value 1', 'value 2'], + getValueSuggestionsMock, + ]); + }); + + afterEach(() => { + jest.clearAllMocks(); + wrapper.unmount(); + }); + + test('it renders row label if one passed in', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('ip')} + selectedValue="127.0.0.1" + /> + ); + + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatchLabel"] label').at(0).text() + ).toEqual('Row Label'); + }); + + test('it renders disabled if "isDisabled" is true', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="127.0.0.1" + /> + ); + + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatch"] input').prop('disabled') + ).toBeTruthy(); + }); + + test('it renders loading if "isLoading" is true', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="127.0.0.1" + /> + ); + wrapper.find('[data-test-subj="valuesAutocompleteMatch"] button').at(0).simulate('click'); + expect( + wrapper + .find('EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteMatch-optionsList"]') + .prop('isLoading') + ).toBeTruthy(); + }); + + test('it allows user to clear values if "isClearable" is true', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={true} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="127.0.0.1" + /> + ); + + expect(wrapper.find(`[data-test-subj="comboBoxClearButton"]`)).toBeTruthy(); + }); + + test('it correctly displays selected value', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="127.0.0.1" + /> + ); + + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatch"] input').at(0).props().value + ).toEqual('127.0.0.1'); + }); + + test('it invokes "onChange" when new value created', () => { + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ip')} + selectedValue="" + /> + ); + + findEuiComboBox().onCreateOption('127.0.0.1'); + + expect(mockOnChange).toHaveBeenCalledWith('127.0.0.1'); + }); + + test('it invokes "onChange" when new value selected', () => { + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + selectedValue="" + /> + ); + + findEuiComboBox().onChange([{ label: 'value 1' }]); + + expect(mockOnChange).toHaveBeenCalledWith('value 1'); + }); + + test('it invokes "onChange" with empty value (i.e. clears selection) when new value searched', () => { + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + selectedValue="value 1" + /> + ); + + act(() => { + findEuiComboBox().onSearchChange('value 12'); + }); + + expect(mockOnChange).toHaveBeenCalledWith(''); + }); + + test('should show the warning helper text if the new value contains spaces when change', async () => { + (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ + false, + true, + [' value 1 ', 'value 2'], + getValueSuggestionsMock, + ]); + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + rowLabel="Test" + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + selectedValue="" + /> + ); + + await waitFor(() => findEuiComboBox().onChange([{ label: ' value 1 ' }])); + wrapper.update(); + expect(mockOnChange).toHaveBeenCalledWith(' value 1 '); + + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeTruthy(); + }); + + test('it refreshes autocomplete with search query when new value searched', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + selectedValue="" + /> + ); + act(() => { + findEuiComboBox().onSearchChange('value 1'); + }); + + expect(useFieldValueAutocomplete).toHaveBeenCalledWith({ + autocompleteService: autocompleteStartMock, + fieldValue: '', + indexPattern: { + fields, + id: '1234', + title: 'logstash-*', + }, + operatorType: 'match', + query: 'value 1', + selectedField: getField('machine.os.raw'), + }); + }); + + test('it refreshes autocomplete with search query when input field is cleared', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + selectedValue="windows" + /> + ); + + act(() => { + findEuiComboBox().onSearchChange('value 1'); + }); + act(() => { + findEuiComboBox().onSearchChange(''); + }); + + // 1st call is initial render, 2nd call sets the search query: + expect(useFieldValueAutocomplete).toHaveBeenNthCalledWith(2, { + autocompleteService: autocompleteStartMock, + fieldValue: 'windows', + indexPattern: { fields, id: '1234', title: 'logstash-*' }, + operatorType: 'match', + query: 'value 1', + selectedField: getField('machine.os.raw'), + }); + // last call is the refresh when input field is cleared + expect(useFieldValueAutocomplete).toHaveBeenLastCalledWith({ + autocompleteService: autocompleteStartMock, + fieldValue: 'windows', + indexPattern: { fields, id: '1234', title: 'logstash-*' }, + operatorType: 'match', + query: '', + selectedField: getField('machine.os.raw'), + }); + }); + + test('should show the warning helper text if the new value contains spaces when searching a new query', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + selectedValue="" + /> + ); + act(() => { + findEuiComboBox().onSearchChange(' value 1'); + }); + + wrapper.update(); + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeTruthy(); + expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); + }); + + test('should show the warning helper text if selectedValue contains spaces when editing', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + selectedValue=" leading and trailing space " + /> + ); + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeTruthy(); + expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); + }); + + test('should not show the warning helper text if selectedValue is falsy', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + selectedValue="" + /> + ); + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeFalsy(); + }); + + describe('boolean type', () => { + const valueSuggestionsMock = jest.fn().mockResolvedValue([false, false, [], jest.fn()]); + + beforeEach(() => { + (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ + false, + false, + [], + valueSuggestionsMock, + ]); + }); + + test('it displays only two options - "true" or "false"', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ssl')} + selectedValue="" + /> + ); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatchBoolean"]').exists() + ).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatchBoolean"]').at(0).prop('options') + ).toEqual([ + { + inputDisplay: 'true', + value: 'true', + }, + { + inputDisplay: 'false', + value: 'false', + }, + ]); + }); + + test('it invokes "onChange" with "true" when selected', () => { + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ssl')} + selectedValue="" + /> + ); + + ( + wrapper.find(EuiSuperSelect).props() as unknown as { + onChange: (a: string) => void; + } + ).onChange('true'); + + expect(mockOnChange).toHaveBeenCalledWith('true'); + }); + + test('it invokes "onChange" with "false" when selected', () => { + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('ssl')} + selectedValue="" + /> + ); + + ( + wrapper.find(EuiSuperSelect).props() as unknown as { + onChange: (a: string) => void; + } + ).onChange('false'); + + expect(mockOnChange).toHaveBeenCalledWith('false'); + }); + }); + + describe('number type', () => { + const valueSuggestionsMock = jest.fn().mockResolvedValue([false, false, [], jest.fn()]); + + beforeEach(() => { + (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ + false, + false, + [], + valueSuggestionsMock, + ]); + }); + + test('it number input when field type is number', () => { + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('bytes')} + selectedValue="" + /> + ); + + expect( + wrapper.find('[data-test-subj="valueAutocompleteFieldMatchNumber"]').exists() + ).toBeTruthy(); + }); + + test('it invokes "onChange" with numeric value when inputted', () => { + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldMatchComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('bytes')} + selectedValue="" + /> + ); + wrapper + .find('[data-test-subj="valueAutocompleteFieldMatchNumber"] input') + .at(0) + .simulate('change', { target: { value: '8' } }); + + expect(mockOnChange).toHaveBeenCalledWith('8'); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx new file mode 100644 index 000000000000..a938ad2f7a99 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx @@ -0,0 +1,376 @@ +/* + * 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, useMemo, useState, useEffect } from 'react'; +import { + EuiSuperSelect, + EuiFormRow, + EuiFieldNumber, + EuiComboBoxOptionOption, + EuiComboBox, +} from '@elastic/eui'; +import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; + +import { uniq } from 'lodash'; + +import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 +// import { AutocompleteStart } from '../../../../../../../../../../src/plugins/unified_search/public'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AutocompleteStart = any; + +import * as i18n from '../translations'; +import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; +import { + getGenericComboBoxProps, + GetGenericComboBoxPropsReturn, +} from '../get_generic_combo_box_props'; +import { paramIsValid } from '../param_is_valid'; +import { paramContainsSpace } from '../param_contains_space'; + +const BOOLEAN_OPTIONS = [ + { inputDisplay: 'true', value: 'true' }, + { inputDisplay: 'false', value: 'false' }, +]; + +const SINGLE_SELECTION = { asPlainText: true }; + +type Warning = string | React.ReactNode; + +interface AutocompleteFieldMatchProps { + placeholder: string; + selectedField: DataViewFieldBase | undefined; + selectedValue: string | undefined; + indexPattern: DataViewBase | undefined; + isLoading?: boolean; + isDisabled?: boolean; + isClearable?: boolean; + isRequired?: boolean; + fieldInputWidth?: number; + rowLabel?: string; + autocompleteService: AutocompleteStart; + onChange: (arg: string) => void; + onError?: (arg: boolean) => void; + onWarning?: (arg: boolean) => void; + warning?: Warning; + 'aria-label'?: string; +} + +export const AutocompleteFieldMatchComponent: React.FC<AutocompleteFieldMatchProps> = ({ + placeholder, + rowLabel, + selectedField, + selectedValue, + indexPattern, + isLoading = false, + isDisabled = false, + isClearable = false, + isRequired = false, + fieldInputWidth, + autocompleteService, + onChange, + onError, + onWarning, + warning, + 'aria-label': ariaLabel, +}): JSX.Element => { + const [searchQuery, setSearchQuery] = useState(''); + const [touched, setIsTouched] = useState(false); + const [error, setError] = useState<string | undefined>(undefined); + const [showSpacesWarning, setShowSpacesWarning] = useState<boolean>(false); + const [isLoadingSuggestions, isSuggestingValues, suggestions] = useFieldValueAutocomplete({ + autocompleteService, + fieldValue: selectedValue, + indexPattern, + operatorType: OperatorTypeEnum.MATCH, + query: searchQuery, + selectedField, + }); + const getLabel = useCallback((option: string): string => option, []); + + const optionsMemo = useMemo((): string[] => { + const valueAsStr = String(selectedValue); + return selectedValue != null && selectedValue.trim() !== '' + ? uniq([valueAsStr, ...suggestions]) + : suggestions; + }, [suggestions, selectedValue]); + + const selectedOptionsMemo = useMemo((): string[] => { + const valueAsStr = String(selectedValue); + return selectedValue ? [valueAsStr] : []; + }, [selectedValue]); + + const handleSpacesWarning = useCallback( + (param: string | undefined) => { + if (!param) return setShowSpacesWarning(false); + setShowSpacesWarning(!!paramContainsSpace(param)); + }, + [setShowSpacesWarning] + ); + + const handleError = useCallback( + (err: string | undefined): void => { + setError((existingErr): string | undefined => { + const oldErr = existingErr != null; + const newErr = err != null; + if (oldErr !== newErr && onError != null) { + onError(newErr); + } + + return err; + }); + }, + [setError, onError] + ); + + const handleWarning = useCallback( + (warn: Warning | undefined): void => { + if (onWarning) { + onWarning(warn !== undefined); + } + }, + [onWarning] + ); + + const { comboOptions, labels, selectedComboOptions } = useMemo( + (): GetGenericComboBoxPropsReturn => + getGenericComboBoxProps<string>({ + getLabel, + options: optionsMemo, + selectedOptions: selectedOptionsMemo, + }), + [optionsMemo, selectedOptionsMemo, getLabel] + ); + + const handleValuesChange = useCallback( + (newOptions: EuiComboBoxOptionOption[]): void => { + const [newValue] = newOptions.map(({ label }) => optionsMemo[labels.indexOf(label)]); + + handleSpacesWarning(newValue); + handleError(undefined); + handleWarning(undefined); + onChange(newValue ?? ''); + }, + [handleError, handleWarning, handleSpacesWarning, labels, onChange, optionsMemo] + ); + + const handleSearchChange = useCallback( + (searchVal: string): void => { + if (searchVal !== '' && selectedField != null) { + const err = paramIsValid(searchVal, selectedField, isRequired, touched); + handleError(err); + handleWarning(warning); + + if (!err) handleSpacesWarning(searchVal); + } + + if (searchVal) { + // Clear selected option when user types to allow user to modify value without {backspace} + onChange(''); + } + + // Update search query unconditionally to show correct suggestions even when input is cleared + setSearchQuery(searchVal); + }, + [ + selectedField, + onChange, + isRequired, + touched, + handleError, + handleWarning, + warning, + handleSpacesWarning, + ] + ); + + const handleCreateOption = useCallback( + (option: string): boolean | undefined => { + const err = paramIsValid(option, selectedField, isRequired, touched); + handleError(err); + handleWarning(warning); + + if (err != null) { + // Explicitly reject the user's input + setShowSpacesWarning(false); + return false; + } + + handleSpacesWarning(option); + onChange(option); + return undefined; + }, + [ + isRequired, + onChange, + selectedField, + touched, + handleError, + handleSpacesWarning, + handleWarning, + warning, + ] + ); + + const handleNonComboBoxInputChange = useCallback( + (event: React.ChangeEvent<HTMLInputElement>): void => { + const newValue = event.target.value; + onChange(newValue); + }, + [onChange] + ); + + const handleBooleanInputChange = useCallback( + (newOption: string): void => { + onChange(newOption); + }, + [onChange] + ); + + const setIsTouchedValue = useCallback((): void => { + setIsTouched(true); + + const err = paramIsValid(selectedValue, selectedField, isRequired, true); + handleError(err); + handleWarning(warning); + }, [setIsTouched, handleError, selectedValue, selectedField, isRequired, warning, handleWarning]); + + const inputPlaceholder = useMemo((): string => { + if (isLoading || isLoadingSuggestions) { + return i18n.LOADING; + } else if (selectedField == null) { + return i18n.SELECT_FIELD_FIRST; + } else { + return placeholder; + } + }, [isLoading, selectedField, isLoadingSuggestions, placeholder]); + + const isLoadingState = useMemo( + (): boolean => isLoading || isLoadingSuggestions, + [isLoading, isLoadingSuggestions] + ); + + useEffect((): void => { + setError(undefined); + if (onError != null) onError(false); + + handleSpacesWarning(selectedValue); + // Looks like selectedField return new object every time when we for example add "and" entry + // that's why we need to check for name and type here + // Probably we should use some kind of memoization on parent components for entries + }, [selectedField?.name, selectedField?.type, selectedValue, handleSpacesWarning, onError]); + + const defaultInput = useMemo((): JSX.Element => { + return ( + <EuiFormRow + label={rowLabel} + error={error} + isInvalid={selectedField != null && error != null} + data-test-subj="valuesAutocompleteMatchLabel" + fullWidth + helpText={warning || (showSpacesWarning && i18n.FIELD_SPACE_WARNING)} + > + <EuiComboBox + placeholder={inputPlaceholder} + isDisabled={isDisabled || !selectedField} + isLoading={isLoadingState} + isClearable={isClearable} + options={comboOptions} + selectedOptions={selectedComboOptions} + onChange={handleValuesChange} + singleSelection={SINGLE_SELECTION} + onSearchChange={handleSearchChange} + onCreateOption={handleCreateOption} + isInvalid={selectedField != null && error != null} + onBlur={setIsTouchedValue} + sortMatchesBy="startsWith" + data-test-subj="valuesAutocompleteMatch" + style={fieldInputWidth ? { width: `${fieldInputWidth}px` } : {}} + aria-label={ariaLabel} + fullWidth + async + /> + </EuiFormRow> + ); + }, [ + rowLabel, + error, + selectedField, + showSpacesWarning, + inputPlaceholder, + isDisabled, + isLoadingState, + isClearable, + comboOptions, + selectedComboOptions, + handleValuesChange, + handleSearchChange, + handleCreateOption, + setIsTouchedValue, + warning, + fieldInputWidth, + ariaLabel, + ]); + + if (!isSuggestingValues && selectedField != null) { + switch (selectedField.type) { + case 'number': + return ( + <EuiFormRow + label={rowLabel} + error={error} + isInvalid={selectedField != null && error != null} + data-test-subj="valuesAutocompleteMatchLabel" + fullWidth + > + <EuiFieldNumber + placeholder={inputPlaceholder} + onBlur={setIsTouchedValue} + value={ + typeof selectedValue === 'string' && selectedValue.trim().length > 0 + ? parseFloat(selectedValue) + : selectedValue ?? '' + } + onChange={handleNonComboBoxInputChange} + data-test-subj="valueAutocompleteFieldMatchNumber" + style={fieldInputWidth ? { width: `${fieldInputWidth}px` } : {}} + aria-label={ariaLabel} + fullWidth + /> + </EuiFormRow> + ); + case 'boolean': + return ( + <EuiFormRow + label={rowLabel} + error={error} + isInvalid={selectedField != null && error != null} + data-test-subj="valuesAutocompleteMatchLabel" + fullWidth + > + <EuiSuperSelect + isLoading={isLoadingState} + options={BOOLEAN_OPTIONS} + valueOfSelected={selectedValue ?? 'true'} + onChange={handleBooleanInputChange} + data-test-subj="valuesAutocompleteMatchBoolean" + style={fieldInputWidth ? { width: `${fieldInputWidth}px` } : {}} + aria-label={ariaLabel} + fullWidth + /> + </EuiFormRow> + ); + default: + return defaultInput; + } + } else { + return defaultInput; + } +}; + +AutocompleteFieldMatchComponent.displayName = 'AutocompleteFieldMatch'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.test.tsx new file mode 100644 index 000000000000..33355e2cb382 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.test.tsx @@ -0,0 +1,398 @@ +/* + * 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 { ReactWrapper, mount } from 'enzyme'; +import { EuiComboBox, EuiComboBoxOptionOption, EuiFormHelpText } from '@elastic/eui'; +import { act, waitFor } from '@testing-library/react'; + +import { AutocompleteFieldMatchAnyComponent } from '.'; +import { getField, fields } from '../fields/index.mock'; +import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; +import { autocompleteStartMock } from '../autocomplete/index.mock'; + +jest.mock('../hooks/use_field_value_autocomplete', () => { + const actual = jest.requireActual('../hooks/use_field_value_autocomplete'); + return { + ...actual, + useFieldValueAutocomplete: jest.fn(), + }; +}); +jest.mock('../translations', () => ({ + FIELD_SPACE_WARNING: 'Warning: there is a space', +})); + +describe('AutocompleteFieldMatchAnyComponent', () => { + let wrapper: ReactWrapper; + const getValueSuggestionsMock = jest + .fn() + .mockResolvedValue([false, true, ['value 3', 'value 4'], jest.fn()]); + + beforeEach(() => { + (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ + false, + true, + ['value 1', 'value 2'], + getValueSuggestionsMock, + ]); + }); + + afterEach(() => { + jest.clearAllMocks(); + wrapper.unmount(); + }); + + test('it renders disabled if "isDisabled" is true', () => { + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={true} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('ip')} + selectedValue={['127.0.0.1']} + /> + ); + + expect( + wrapper.find(`[data-test-subj="valuesAutocompleteMatchAny"] input`).prop('disabled') + ).toBeTruthy(); + }); + + test('it renders loading if "isLoading" is true', () => { + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={true} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('ip')} + selectedValue={[]} + /> + ); + wrapper.find(`[data-test-subj="valuesAutocompleteMatchAny"] button`).at(0).simulate('click'); + expect( + wrapper + .find(`EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteMatchAny-optionsList"]`) + .prop('isLoading') + ).toBeTruthy(); + }); + + test('it allows user to clear values if "isClearable" is true', () => { + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={true} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('ip')} + selectedValue={['127.0.0.1']} + /> + ); + + expect(wrapper.find(`[data-test-subj="comboBoxClearButton"]`)).toBeTruthy(); + }); + + test('it correctly displays selected value', () => { + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('ip')} + selectedValue={['127.0.0.1']} + /> + ); + + expect( + wrapper.find(`[data-test-subj="valuesAutocompleteMatchAny"] EuiComboBoxPill`).at(0).text() + ).toEqual('127.0.0.1'); + }); + + test('it invokes "onChange" when new value created', async () => { + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('ip')} + selectedValue={[]} + /> + ); + + ( + wrapper.find(EuiComboBox).props() as unknown as { + onCreateOption: (a: string) => void; + } + ).onCreateOption('127.0.0.1'); + + expect(mockOnChange).toHaveBeenCalledWith(['127.0.0.1']); + }); + + test('it invokes "onChange" when new value selected', async () => { + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isLoading={false} + isClearable={false} + isDisabled={false} + onChange={mockOnChange} + onError={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('machine.os.raw')} + selectedValue={[]} + /> + ); + + ( + wrapper.find(EuiComboBox).props() as unknown as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + } + ).onChange([{ label: 'value 1' }]); + + expect(mockOnChange).toHaveBeenCalledWith(['value 1']); + }); + + test('it refreshes autocomplete with search query when new value searched', () => { + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('machine.os.raw')} + selectedValue={[]} + /> + ); + act(() => { + ( + wrapper.find(EuiComboBox).props() as unknown as { + onSearchChange: (a: string) => void; + } + ).onSearchChange('value 1'); + }); + expect(useFieldValueAutocomplete).toHaveBeenCalledWith({ + autocompleteService: autocompleteStartMock, + fieldValue: [], + indexPattern: { + fields, + id: '1234', + title: 'logstash-*', + }, + operatorType: 'match_any', + query: 'value 1', + selectedField: getField('machine.os.raw'), + }); + }); + test('should show the warning helper text if the new value contains spaces when change', async () => { + (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ + false, + true, + [' value 1 ', 'value 2'], + getValueSuggestionsMock, + ]); + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('machine.os.raw')} + selectedValue={[]} + /> + ); + + await waitFor(() => + ( + wrapper.find(EuiComboBox).props() as unknown as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + } + ).onChange([{ label: ' value 1 ' }]) + ); + wrapper.update(); + expect(mockOnChange).toHaveBeenCalledWith([' value 1 ']); + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeTruthy(); + }); + test('should show the warning helper text if the new value contains spaces when searching a new query', () => { + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('machine.os.raw')} + selectedValue={[]} + /> + ); + act(() => { + ( + wrapper.find(EuiComboBox).props() as unknown as { + onSearchChange: (a: string) => void; + } + ).onSearchChange(' value 1'); + }); + + wrapper.update(); + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeTruthy(); + expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); + }); + test('should show the warning helper text if selectedValue contains spaces when editing', () => { + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('machine.os.raw')} + selectedValue={['value with trailing space ', 'value 1']} + /> + ); + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeTruthy(); + expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); + }); + test('should not show the warning helper text if selectedValue is falsy', () => { + wrapper = mount( + <AutocompleteFieldMatchAnyComponent + autocompleteService={{ + ...autocompleteStartMock, + }} + indexPattern={{ + fields, + id: '1234', + title: 'logstash-*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('machine.os.raw')} + selectedValue={[]} + /> + ); + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeFalsy(); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.tsx new file mode 100644 index 000000000000..be7fac088ec1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.tsx @@ -0,0 +1,252 @@ +/* + * 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, useState } from 'react'; +import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; +import { uniq } from 'lodash'; +import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; + +// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 +// import { AutocompleteStart } from '../../../../../../../../../../src/plugins/unified_search/public'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AutocompleteStart = any; + +import * as i18n from '../translations'; +import { + getGenericComboBoxProps, + GetGenericComboBoxPropsReturn, +} from '../get_generic_combo_box_props'; +import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; +import { paramIsValid } from '../param_is_valid'; +import { paramContainsSpace } from '../param_contains_space'; + +interface AutocompleteFieldMatchAnyProps { + placeholder: string; + selectedField: DataViewFieldBase | undefined; + selectedValue: string[]; + indexPattern: DataViewBase | undefined; + isLoading: boolean; + isDisabled: boolean; + isClearable: boolean; + isRequired?: boolean; + rowLabel?: string; + autocompleteService: AutocompleteStart; + onChange: (arg: string[]) => void; + onError?: (arg: boolean) => void; + 'aria-label'?: string; +} + +export const AutocompleteFieldMatchAnyComponent: React.FC<AutocompleteFieldMatchAnyProps> = ({ + placeholder, + rowLabel, + selectedField, + selectedValue, + indexPattern, + isLoading, + isDisabled = false, + isClearable = false, + isRequired = false, + onChange, + onError, + autocompleteService, + 'aria-label': ariaLabel, +}): JSX.Element => { + const [searchQuery, setSearchQuery] = useState(''); + const [touched, setIsTouched] = useState(false); + const [error, setError] = useState<string | undefined>(undefined); + const [showSpacesWarning, setShowSpacesWarning] = useState<boolean>(false); + const [isLoadingSuggestions, isSuggestingValues, suggestions] = useFieldValueAutocomplete({ + autocompleteService, + fieldValue: selectedValue, + indexPattern, + operatorType: OperatorTypeEnum.MATCH_ANY, + query: searchQuery, + selectedField, + }); + const getLabel = useCallback((option: string): string => option, []); + const optionsMemo = useMemo( + (): string[] => (selectedValue ? uniq([...selectedValue, ...suggestions]) : suggestions), + [suggestions, selectedValue] + ); + const { comboOptions, labels, selectedComboOptions } = useMemo( + (): GetGenericComboBoxPropsReturn => + getGenericComboBoxProps<string>({ + getLabel, + options: optionsMemo, + selectedOptions: selectedValue, + }), + [optionsMemo, selectedValue, getLabel] + ); + const handleSpacesWarning = useCallback( + (params: string[]) => + setShowSpacesWarning(!!params.find((param: string) => paramContainsSpace(param))), + [setShowSpacesWarning] + ); + const handleError = useCallback( + (err: string | undefined): void => { + setError((existingErr): string | undefined => { + const oldErr = existingErr != null; + const newErr = err != null; + if (oldErr !== newErr && onError != null) { + onError(newErr); + } + + return err; + }); + }, + [setError, onError] + ); + + const handleValuesChange = useCallback( + (newOptions: EuiComboBoxOptionOption[]): void => { + const newValues: string[] = newOptions.map(({ label }) => optionsMemo[labels.indexOf(label)]); + handleError(undefined); + handleSpacesWarning(newValues); + onChange(newValues); + }, + [handleError, handleSpacesWarning, labels, onChange, optionsMemo] + ); + + const handleSearchChange = useCallback( + (searchVal: string) => { + if (searchVal === '') { + handleError(undefined); + } + + if (searchVal !== '' && selectedField != null) { + const err = paramIsValid(searchVal, selectedField, isRequired, touched); + handleError(err); + + if (!err) handleSpacesWarning([searchVal]); + + setSearchQuery(searchVal); + } + }, + [handleError, handleSpacesWarning, isRequired, selectedField, touched] + ); + + const handleCreateOption = useCallback( + (option: string): boolean => { + const err = paramIsValid(option, selectedField, isRequired, touched); + handleError(err); + + if (err != null) { + // Explicitly reject the user's input + setShowSpacesWarning(false); + return false; + } + + onChange([...(selectedValue || []), option]); + handleSpacesWarning([option]); + return true; + }, + [handleError, handleSpacesWarning, isRequired, onChange, selectedField, selectedValue, touched] + ); + + const setIsTouchedValue = useCallback((): void => { + handleError(selectedComboOptions.length === 0 ? i18n.FIELD_REQUIRED_ERR : undefined); + setIsTouched(true); + }, [setIsTouched, handleError, selectedComboOptions]); + + const inputPlaceholder = useMemo( + (): string => (isLoading || isLoadingSuggestions ? i18n.LOADING : placeholder), + [isLoading, isLoadingSuggestions, placeholder] + ); + + const isLoadingState = useMemo( + (): boolean => isLoading || isLoadingSuggestions, + [isLoading, isLoadingSuggestions] + ); + useEffect((): void => { + handleSpacesWarning(selectedValue); + }, [selectedField, selectedValue, handleSpacesWarning]); + + const defaultInput = useMemo((): JSX.Element => { + return ( + <EuiFormRow + label={rowLabel} + error={error} + isInvalid={selectedField != null && error != null} + helpText={showSpacesWarning && i18n.FIELD_SPACE_WARNING} + fullWidth + > + <EuiComboBox + placeholder={inputPlaceholder} + isLoading={isLoadingState} + isClearable={isClearable} + isDisabled={isDisabled} + options={comboOptions} + selectedOptions={selectedComboOptions} + onChange={handleValuesChange} + onSearchChange={handleSearchChange} + onCreateOption={handleCreateOption} + isInvalid={selectedField != null && error != null} + isCaseSensitive + onBlur={setIsTouchedValue} + data-test-subj="valuesAutocompleteMatchAny" + aria-label={ariaLabel} + fullWidth + async + /> + </EuiFormRow> + ); + }, [ + rowLabel, + error, + selectedField, + showSpacesWarning, + inputPlaceholder, + isLoadingState, + isClearable, + isDisabled, + comboOptions, + selectedComboOptions, + handleValuesChange, + handleSearchChange, + handleCreateOption, + setIsTouchedValue, + ariaLabel, + ]); + + if (!isSuggestingValues && selectedField != null) { + switch (selectedField.type) { + case 'number': + return ( + <EuiFormRow + label={rowLabel} + error={error} + isInvalid={selectedField != null && error != null} + fullWidth + > + <EuiComboBox + noSuggestions + placeholder={inputPlaceholder} + isLoading={isLoadingState} + isClearable={isClearable} + isDisabled={isDisabled} + selectedOptions={selectedComboOptions} + onChange={handleValuesChange} + onSearchChange={handleSearchChange} + onCreateOption={handleCreateOption} + isInvalid={selectedField != null && error != null} + onFocus={setIsTouchedValue} + data-test-subj="valuesAutocompleteMatchAnyNumber" + aria-label={ariaLabel} + fullWidth + /> + </EuiFormRow> + ); + default: + return defaultInput; + } + } + + return defaultInput; +}; + +AutocompleteFieldMatchAnyComponent.displayName = 'AutocompleteFieldMatchAny'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx new file mode 100644 index 000000000000..2c2411f0a0c1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx @@ -0,0 +1,514 @@ +/* + * 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 { ReactWrapper, mount } from 'enzyme'; +import { EuiComboBox, EuiComboBoxOptionOption, EuiFormHelpText } from '@elastic/eui'; +import { act, waitFor } from '@testing-library/react'; +import { AutocompleteFieldWildcardComponent } from '.'; +import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; +import { fields, getField } from '../fields/index.mock'; +import { autocompleteStartMock } from '../autocomplete/index.mock'; +import { WILDCARD_WARNING, FILEPATH_WARNING } from '@kbn/securitysolution-utils'; + +jest.mock('../hooks/use_field_value_autocomplete'); +jest.mock('../translations', () => ({ + FIELD_SPACE_WARNING: 'Warning: there is a space', +})); +describe('AutocompleteFieldWildcardComponent', () => { + let wrapper: ReactWrapper; + + const getValueSuggestionsMock = jest + .fn() + .mockResolvedValue([false, true, ['value 3', 'value 4'], jest.fn()]); + + beforeEach(() => { + (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ + false, + true, + ['value 1', 'value 2'], + getValueSuggestionsMock, + ]); + }); + + afterEach(() => { + jest.clearAllMocks(); + wrapper.unmount(); + }); + + test('it renders row label if one passed in', () => { + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + rowLabel={'Row Label'} + selectedField={getField('file.path.text')} + selectedValue="/opt/bin/app.dmg" + /> + ); + + expect( + wrapper.find('[data-test-subj="valuesAutocompleteWildcardLabel"] label').at(0).text() + ).toEqual('Row Label'); + }); + + test('it renders disabled if "isDisabled" is true', () => { + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="/opt/*/app.dmg" + /> + ); + + expect( + wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] input').prop('disabled') + ).toBeTruthy(); + }); + + test('it renders loading if "isLoading" is true', () => { + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading + onChange={jest.fn()} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="/opt/*/app.dmg" + /> + ); + wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] button').at(0).simulate('click'); + expect( + wrapper + .find('EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteWildcard-optionsList"]') + .prop('isLoading') + ).toBeTruthy(); + }); + + test('it allows user to clear values if "isClearable" is true', () => { + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={true} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="/opt/*/app.dmg" + /> + ); + + expect(wrapper.find(`[data-test-subj="comboBoxClearButton"]`)).toBeTruthy(); + }); + + test('it correctly displays selected value', () => { + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="/opt/*/app.dmg" + /> + ); + + expect( + wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] input').at(0).props().value + ).toEqual('/opt/*/app.dmg'); + }); + + test('it invokes "onChange" when new value created', async () => { + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="" + /> + ); + + ( + wrapper.find(EuiComboBox).props() as unknown as { + onCreateOption: (a: string) => void; + } + ).onCreateOption('/opt/*/app.dmg'); + + expect(mockOnChange).toHaveBeenCalledWith('/opt/*/app.dmg'); + }); + + test('it invokes "onChange" when new value selected', async () => { + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="" + /> + ); + + ( + wrapper.find(EuiComboBox).props() as unknown as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + } + ).onChange([{ label: 'value 1' }]); + + expect(mockOnChange).toHaveBeenCalledWith('value 1'); + }); + + test('it refreshes autocomplete with search query when new value searched', () => { + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="" + /> + ); + act(() => { + ( + wrapper.find(EuiComboBox).props() as unknown as { + onSearchChange: (a: string) => void; + } + ).onSearchChange('A:\\Some Folder\\inc*.exe'); + }); + + expect(useFieldValueAutocomplete).toHaveBeenCalledWith({ + autocompleteService: autocompleteStartMock, + fieldValue: '', + indexPattern: { + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }, + operatorType: 'wildcard', + query: 'A:\\Some Folder\\inc*.exe', + selectedField: getField('file.path.text'), + }); + }); + + test('it does not invoke "onWarning" when no warning exists', () => { + const mockOnWarning = jest.fn(); + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={mockOnWarning} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="invalid path" + /> + ); + + act(() => { + ( + wrapper.find(EuiComboBox).props() as unknown as { + onBlur: () => void; + } + ).onBlur(); + }); + + expect(mockOnWarning).not.toHaveBeenCalledWith(true); + }); + + test('it invokes "onWarning" when warning exists', () => { + const mockOnWarning = jest.fn(); + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={mockOnWarning} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="invalid path" + warning={FILEPATH_WARNING} + /> + ); + + act(() => { + ( + wrapper.find(EuiComboBox).props() as unknown as { + onBlur: () => void; + } + ).onBlur(); + }); + + expect(mockOnWarning).toHaveBeenCalledWith(true); + expect( + wrapper + .find('[data-test-subj="valuesAutocompleteWildcardLabel"] div.euiFormHelpText') + .at(0) + .text() + ).toEqual(FILEPATH_WARNING); + }); + + test('it invokes "onWarning" when warning exists and is wildcard warning', () => { + const mockOnWarning = jest.fn(); + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={mockOnWarning} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="invalid path" + warning={WILDCARD_WARNING} + /> + ); + + act(() => { + ( + wrapper.find(EuiComboBox).props() as unknown as { + onBlur: () => void; + } + ).onBlur(); + }); + + expect(mockOnWarning).toHaveBeenCalledWith(true); + const helpText = wrapper + .find('[data-test-subj="valuesAutocompleteWildcardLabel"] div.euiFormHelpText') + .at(0); + expect(helpText.text()).toEqual(WILDCARD_WARNING); + expect(helpText.find('.euiToolTipAnchor')).toBeTruthy(); + }); + test('should show the warning helper text if the new value contains spaces when change', async () => { + (useFieldValueAutocomplete as jest.Mock).mockReturnValue([ + false, + true, + [' value 1 ', 'value 2'], + getValueSuggestionsMock, + ]); + const mockOnChange = jest.fn(); + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="invalid path" + warning={WILDCARD_WARNING} + /> + ); + + await waitFor(() => + ( + wrapper.find(EuiComboBox).props() as unknown as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + } + ).onChange([{ label: ' value 1 ' }]) + ); + wrapper.update(); + expect(mockOnChange).toHaveBeenCalledWith(' value 1 '); + + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeTruthy(); + }); + test('should show the warning helper text if the new value contains spaces when searching a new query', () => { + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="invalid path" + warning={''} + /> + ); + act(() => { + ( + wrapper.find(EuiComboBox).props() as unknown as { + onSearchChange: (a: string) => void; + } + ).onSearchChange(' value 1'); + }); + + wrapper.update(); + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeTruthy(); + expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); + }); + test('should show the warning helper text if selectedValue contains spaces when editing', () => { + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue=" leading space" + warning={''} + /> + ); + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeTruthy(); + expect(euiFormHelptext.text()).toEqual('Warning: there is a space'); + }); + test('should not show the warning helper text if selectedValue is falsy', () => { + wrapper = mount( + <AutocompleteFieldWildcardComponent + autocompleteService={autocompleteStartMock} + indexPattern={{ + fields, + id: '1234', + title: 'logs-endpoint.events.*', + }} + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + onError={jest.fn()} + onWarning={jest.fn()} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + selectedValue="" + warning={''} + /> + ); + const euiFormHelptext = wrapper.find(EuiFormHelpText); + expect(euiFormHelptext.length).toBeFalsy(); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.tsx new file mode 100644 index 000000000000..c095578bf0ff --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.tsx @@ -0,0 +1,285 @@ +/* + * 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, useMemo, useState, useEffect, memo } from 'react'; +import { EuiFormRow, EuiComboBoxOptionOption, EuiComboBox } from '@elastic/eui'; +import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; + +import { uniq } from 'lodash'; + +import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 +// import { AutocompleteStart } from '../../../../../../../../../../src/plugins/unified_search/public'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AutocompleteStart = any; + +import * as i18n from '../translations'; +import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; +import { + getGenericComboBoxProps, + GetGenericComboBoxPropsReturn, +} from '../get_generic_combo_box_props'; +import { paramIsValid } from '../param_is_valid'; +import { paramContainsSpace } from '../param_contains_space'; + +const SINGLE_SELECTION = { asPlainText: true }; + +type Warning = string | React.ReactNode; +interface AutocompleteFieldWildcardProps { + placeholder: string; + selectedField: DataViewFieldBase | undefined; + selectedValue: string | undefined; + indexPattern: DataViewBase | undefined; + isLoading: boolean; + isDisabled?: boolean; + isClearable?: boolean; + isRequired?: boolean; + fieldInputWidth?: number; + rowLabel?: string; + autocompleteService: AutocompleteStart; + onChange: (arg: string) => void; + onError: (arg: boolean) => void; + onWarning: (arg: boolean) => void; + warning?: Warning; + 'aria-label'?: string; +} + +export const AutocompleteFieldWildcardComponent: React.FC<AutocompleteFieldWildcardProps> = memo( + ({ + autocompleteService, + placeholder, + rowLabel, + selectedField, + selectedValue, + indexPattern, + isLoading, + isDisabled = false, + isClearable = false, + isRequired = false, + fieldInputWidth, + onChange, + onError, + onWarning, + warning, + 'aria-label': ariaLabel, + }): JSX.Element => { + const [searchQuery, setSearchQuery] = useState(''); + const [touched, setIsTouched] = useState(false); + const [error, setError] = useState<string | undefined>(undefined); + const [showSpacesWarning, setShowSpacesWarning] = useState<boolean>(false); + const [isLoadingSuggestions, , suggestions] = useFieldValueAutocomplete({ + autocompleteService, + fieldValue: selectedValue, + indexPattern, + operatorType: OperatorTypeEnum.WILDCARD, + query: searchQuery, + selectedField, + }); + const getLabel = useCallback((option: string): string => option, []); + const optionsMemo = useMemo((): string[] => { + const valueAsStr = String(selectedValue); + return selectedValue != null && selectedValue.trim() !== '' + ? uniq([valueAsStr, ...suggestions]) + : suggestions; + }, [suggestions, selectedValue]); + const selectedOptionsMemo = useMemo((): string[] => { + const valueAsStr = String(selectedValue); + return selectedValue ? [valueAsStr] : []; + }, [selectedValue]); + + const handleSpacesWarning = useCallback( + (param: string | undefined) => { + if (!param) return setShowSpacesWarning(false); + setShowSpacesWarning(!!paramContainsSpace(param)); + }, + [setShowSpacesWarning] + ); + const handleError = useCallback( + (err: string | undefined): void => { + setError((existingErr): string | undefined => { + const oldErr = existingErr != null; + const newErr = err != null; + if (oldErr !== newErr && onError != null) { + onError(newErr); + } + + return err; + }); + }, + [setError, onError] + ); + + const handleWarning = useCallback( + (warn: Warning | undefined): void => { + onWarning(warn !== undefined); + }, + [onWarning] + ); + + const { comboOptions, labels, selectedComboOptions } = useMemo( + (): GetGenericComboBoxPropsReturn => + getGenericComboBoxProps<string>({ + getLabel, + options: optionsMemo, + selectedOptions: selectedOptionsMemo, + }), + [optionsMemo, selectedOptionsMemo, getLabel] + ); + + const handleValuesChange = useCallback( + (newOptions: EuiComboBoxOptionOption[]): void => { + const [newValue] = newOptions.map(({ label }) => optionsMemo[labels.indexOf(label)]); + handleError(undefined); + handleSpacesWarning(newValue); + setShowSpacesWarning(false); + + onChange(newValue ?? ''); + }, + [handleError, handleSpacesWarning, labels, onChange, optionsMemo] + ); + + const handleSearchChange = useCallback( + (searchVal: string): void => { + if (searchVal.trim() !== '' && selectedField != null) { + const err = paramIsValid(searchVal, selectedField, isRequired, touched); + handleError(err); + handleWarning(warning); + if (!err) handleSpacesWarning(searchVal); + + setSearchQuery(searchVal); + } + }, + [handleError, handleSpacesWarning, isRequired, selectedField, touched, warning, handleWarning] + ); + + const handleCreateOption = useCallback( + (option: string): boolean | undefined => { + const err = paramIsValid(option, selectedField, isRequired, touched); + handleError(err); + handleWarning(warning); + + if (err != null) { + // Explicitly reject the user's input + setShowSpacesWarning(false); + return false; + } + + handleSpacesWarning(option); + onChange(option); + return undefined; + }, + [ + isRequired, + handleSpacesWarning, + onChange, + selectedField, + touched, + handleError, + handleWarning, + warning, + ] + ); + + const setIsTouchedValue = useCallback((): void => { + setIsTouched(true); + + const err = paramIsValid(selectedValue, selectedField, isRequired, true); + handleError(err); + handleWarning(warning); + }, [ + setIsTouched, + handleError, + selectedValue, + selectedField, + isRequired, + handleWarning, + warning, + ]); + + const inputPlaceholder = useMemo((): string => { + if (isLoading || isLoadingSuggestions) { + return i18n.LOADING; + } else if (selectedField == null) { + return i18n.SELECT_FIELD_FIRST; + } else { + return placeholder; + } + }, [isLoading, selectedField, isLoadingSuggestions, placeholder]); + + const isLoadingState = useMemo( + (): boolean => isLoading || isLoadingSuggestions, + [isLoading, isLoadingSuggestions] + ); + + useEffect((): void => { + setError(undefined); + if (onError != null) { + onError(false); + } + handleSpacesWarning(selectedValue); + + onWarning(false); + }, [selectedField, selectedValue, onError, onWarning, handleSpacesWarning]); + + const defaultInput = useMemo((): JSX.Element => { + return ( + <EuiFormRow + label={rowLabel} + error={error} + helpText={warning || (showSpacesWarning && i18n.FIELD_SPACE_WARNING)} + isInvalid={selectedField != null && error != null} + data-test-subj="valuesAutocompleteWildcardLabel" + fullWidth + > + <EuiComboBox + placeholder={inputPlaceholder} + isDisabled={isDisabled || !selectedField} + isLoading={isLoadingState} + isClearable={isClearable} + options={comboOptions} + selectedOptions={selectedComboOptions} + onChange={handleValuesChange} + singleSelection={SINGLE_SELECTION} + onSearchChange={handleSearchChange} + onCreateOption={handleCreateOption} + isInvalid={selectedField != null && error != null} + onBlur={setIsTouchedValue} + sortMatchesBy="startsWith" + data-test-subj="valuesAutocompleteWildcard" + style={fieldInputWidth ? { width: `${fieldInputWidth}px` } : {}} + fullWidth + async + aria-label={ariaLabel} + /> + </EuiFormRow> + ); + }, [ + rowLabel, + error, + warning, + showSpacesWarning, + selectedField, + inputPlaceholder, + isDisabled, + isLoadingState, + isClearable, + comboOptions, + selectedComboOptions, + handleValuesChange, + handleSearchChange, + handleCreateOption, + setIsTouchedValue, + fieldInputWidth, + ariaLabel, + ]); + + return defaultInput; + } +); + +AutocompleteFieldWildcardComponent.displayName = 'AutocompleteFieldWildcard'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/fields/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/fields/index.mock.ts new file mode 100644 index 000000000000..32e9d6019786 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/fields/index.mock.ts @@ -0,0 +1,321 @@ +/* + * 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 { DataViewFieldBase } from '@kbn/es-query'; + +// Copied from "src/plugins/data/common/index_patterns/fields/fields.mocks.ts" but with the types changed to "DataViewFieldBase" since that type is compatible. +// TODO: This should move out once those mocks are directly useable or in their own package, https://github.com/elastic/kibana/issues/100715 + +export const fields: DataViewFieldBase[] = [ + { + name: 'bytes', + type: 'number', + esTypes: ['long'], + count: 10, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'ssl', + type: 'boolean', + esTypes: ['boolean'], + count: 20, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: '@timestamp', + type: 'date', + esTypes: ['date'], + count: 30, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'time', + type: 'date', + esTypes: ['date'], + count: 30, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: '@tags', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'utc_time', + type: 'date', + esTypes: ['date'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'phpmemory', + type: 'number', + esTypes: ['integer'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'ip', + type: 'ip', + esTypes: ['ip'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'request_body', + type: 'attachment', + esTypes: ['attachment'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'point', + type: 'geo_point', + esTypes: ['geo_point'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'area', + type: 'geo_shape', + esTypes: ['geo_shape'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'hashed', + type: 'murmur3', + esTypes: ['murmur3'], + count: 0, + scripted: false, + searchable: true, + aggregatable: false, + readFromDocValues: false, + }, + { + name: 'geo.coordinates', + type: 'geo_point', + esTypes: ['geo_point'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'extension', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'machine.os', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'machine.os.raw', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + subType: { multi: { parent: 'machine.os' } }, + }, + { + name: 'geo.src', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: '_id', + type: 'string', + esTypes: ['_id'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: '_type', + type: 'string', + esTypes: ['_type'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: '_source', + type: '_source', + esTypes: ['_source'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'non-filterable', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: false, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'non-sortable', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: false, + aggregatable: false, + readFromDocValues: false, + }, + { + name: 'custom_user_field', + type: 'conflict', + esTypes: ['long', 'text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'script string', + type: 'string', + count: 0, + scripted: true, + script: "'i am a string'", + lang: 'expression', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'script number', + type: 'number', + count: 0, + scripted: true, + script: '1234', + lang: 'expression', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'script date', + type: 'date', + count: 0, + scripted: true, + script: '1234', + lang: 'painless', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'script murmur3', + type: 'murmur3', + count: 0, + scripted: true, + script: '1234', + lang: 'expression', + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + { + name: 'nestedField.child', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: false, + readFromDocValues: false, + subType: { nested: { path: 'nestedField' } }, + }, + { + name: 'nestedField.nestedChild.doublyNestedChild', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: false, + readFromDocValues: false, + subType: { nested: { path: 'nestedField.nestedChild' } }, + }, + { + name: 'file.path.text', + type: 'string', + esTypes: ['text'], + searchable: true, + aggregatable: false, + subType: { multi: { parent: 'file.path' } }, + }, +] as unknown as DataViewFieldBase[]; + +export const getField = (name: string) => fields.find((field) => field.name === name); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.test.ts new file mode 100644 index 000000000000..3328af4e2fa2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.test.ts @@ -0,0 +1,123 @@ +/* + * 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 { filterFieldToList } from '.'; + +import { getListResponseMock } from '../list_schema/index.mock'; +import { DataViewFieldBase } from '@kbn/es-query'; +import { AutocompleteListsData } from '../field_value_lists'; + +const emptyListData: AutocompleteListsData = { smallLists: [], largeLists: [] }; + +describe('#filterFieldToList', () => { + test('it returns empty list data object if given a undefined for field', () => { + const filter = filterFieldToList(emptyListData, undefined); + expect(filter).toEqual(emptyListData); + }); + + test('it returns empty list data object if filed does not contain esTypes', () => { + const field: DataViewFieldBase = { + name: 'some-name', + type: 'some-type', + }; + const filter = filterFieldToList(emptyListData, field); + expect(filter).toEqual(emptyListData); + }); + + test('it returns filtered lists of ip_range -> ip', () => { + const field: DataViewFieldBase & { esTypes: string[] } = { + esTypes: ['ip'], + name: 'some-name', + type: 'ip', + }; + const listData: AutocompleteListsData = { + smallLists: [{ ...getListResponseMock(), type: 'ip_range' }], + largeLists: [], + }; + const filter = filterFieldToList(listData, field); + const expected = listData; + expect(filter).toEqual(expected); + }); + + test('it returns filtered lists of ip -> ip', () => { + const field: DataViewFieldBase & { esTypes: string[] } = { + esTypes: ['ip'], + name: 'some-name', + type: 'ip', + }; + const listData: AutocompleteListsData = { + smallLists: [{ ...getListResponseMock(), type: 'ip' }], + largeLists: [], + }; + const filter = filterFieldToList(listData, field); + const expected = listData; + expect(filter).toEqual(expected); + }); + + test('it returns filtered lists of keyword -> keyword', () => { + const field: DataViewFieldBase & { esTypes: string[] } = { + esTypes: ['keyword'], + name: 'some-name', + type: 'keyword', + }; + const listData: AutocompleteListsData = { + smallLists: [{ ...getListResponseMock(), type: 'keyword' }], + largeLists: [], + }; + const filter = filterFieldToList(listData, field); + const expected = listData; + expect(filter).toEqual(expected); + }); + + test('it returns filtered lists of text -> text', () => { + const field: DataViewFieldBase & { esTypes: string[] } = { + esTypes: ['text'], + name: 'some-name', + type: 'text', + }; + const listData: AutocompleteListsData = { + smallLists: [{ ...getListResponseMock(), type: 'text' }], + largeLists: [], + }; + const filter = filterFieldToList(listData, field); + const expected = listData; + expect(filter).toEqual(expected); + }); + + test('it returns small and large filtered lists of ip_range -> ip', () => { + const field: DataViewFieldBase & { esTypes: string[] } = { + esTypes: ['ip'], + name: 'some-name', + type: 'ip', + }; + const listData: AutocompleteListsData = { + smallLists: [{ ...getListResponseMock(), type: 'ip_range' }], + largeLists: [{ ...getListResponseMock(), type: 'ip_range' }], + }; + const filter = filterFieldToList(listData, field); + const expected = listData; + expect(filter).toEqual(expected); + }); + + test('it returns 1 filtered lists of ip_range -> ip if the 2nd is not compatible type', () => { + const field: DataViewFieldBase & { esTypes: string[] } = { + esTypes: ['ip'], + name: 'some-name', + type: 'ip', + }; + const listData: AutocompleteListsData = { + smallLists: [{ ...getListResponseMock(), type: 'ip_range' }], + largeLists: [{ ...getListResponseMock(), type: 'text' }], + }; + const filter = filterFieldToList(listData, field); + const expected: AutocompleteListsData = { + smallLists: [{ ...getListResponseMock(), type: 'ip_range' }], + largeLists: [], + }; + expect(filter).toEqual(expected); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.ts new file mode 100644 index 000000000000..a4df119766a9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.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 { DataViewFieldBase } from '@kbn/es-query'; +import { typeMatch } from '../type_match'; +import { AutocompleteListsData } from '../field_value_lists'; + +/** + * Given an array of lists and optionally a field this will return all + * the lists that match against the field based on the types from the field + * + * NOTE: That we support one additional property from "FieldSpec" located here: + * src/plugins/data/common/index_patterns/fields/types.ts + * This type property is esTypes. If it exists and is on there we will read off the esTypes. + * @param lists The lists to match against the field + * @param field The field to check against the list to see if they are compatible + */ +export const filterFieldToList = ( + lists: AutocompleteListsData, + field?: DataViewFieldBase & { esTypes?: string[] } +): AutocompleteListsData => { + if (field != null) { + const { esTypes = [] } = field; + return { + smallLists: lists.smallLists.filter(({ type }) => + esTypes.some((esType: string) => typeMatch(type, esType)) + ), + largeLists: lists.largeLists.filter(({ type }) => + esTypes.some((esType: string) => typeMatch(type, esType)) + ), + }; + } else { + return { smallLists: [], largeLists: [] }; + } +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.test.tsx new file mode 100644 index 000000000000..f333e7c6ebdd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.test.tsx @@ -0,0 +1,124 @@ +/* + * 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 { getGenericComboBoxProps } from '.'; + +describe('get_generic_combo_box_props', () => { + test('it returns empty arrays if "options" is empty array', () => { + const result = getGenericComboBoxProps<string>({ + options: [], + selectedOptions: ['option1'], + getLabel: (t: string) => t, + }); + + expect(result).toEqual({ comboOptions: [], labels: [], selectedComboOptions: [] }); + }); + + test('it returns formatted props if "options" array is not empty', () => { + const result = getGenericComboBoxProps<string>({ + options: ['option1', 'option2', 'option3'], + selectedOptions: [], + getLabel: (t: string) => t, + }); + + expect(result).toEqual({ + comboOptions: [ + { + label: 'option1', + }, + { + label: 'option2', + }, + { + label: 'option3', + }, + ], + labels: ['option1', 'option2', 'option3'], + selectedComboOptions: [], + }); + }); + + test('it does not return "selectedOptions" items that do not appear in "options"', () => { + const result = getGenericComboBoxProps<string>({ + options: ['option1', 'option2', 'option3'], + selectedOptions: ['option4'], + getLabel: (t: string) => t, + }); + + expect(result).toEqual({ + comboOptions: [ + { + label: 'option1', + }, + { + label: 'option2', + }, + { + label: 'option3', + }, + ], + labels: ['option1', 'option2', 'option3'], + selectedComboOptions: [], + }); + }); + + test('it returns "selectedOptions" items that do appear in "options"', () => { + const result = getGenericComboBoxProps<string>({ + options: ['option1', 'option2', 'option3'], + selectedOptions: ['option2'], + getLabel: (t: string) => t, + }); + + expect(result).toEqual({ + comboOptions: [ + { + label: 'option1', + }, + { + label: 'option2', + }, + { + label: 'option3', + }, + ], + labels: ['option1', 'option2', 'option3'], + selectedComboOptions: [ + { + label: 'option2', + }, + ], + }); + }); + + test('it returns "disabledOptions" items that do appear in "options" as disabled', () => { + const result = getGenericComboBoxProps<string>({ + options: ['option1', 'option2', 'option3'], + selectedOptions: [], + disabledOptions: ['option2'], + getLabel: (t: string) => t, + }); + + expect(result).toEqual({ + comboOptions: [ + { + label: 'option1', + disabled: false, + }, + { + label: 'option2', + disabled: true, + }, + { + label: 'option3', + disabled: false, + }, + ], + labels: ['option1', 'option2', 'option3'], + selectedComboOptions: [], + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.ts new file mode 100644 index 000000000000..f043fc978836 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_generic_combo_box_props/index.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiComboBoxOptionOption } from '@elastic/eui'; + +export interface GetGenericComboBoxPropsReturn { + comboOptions: EuiComboBoxOptionOption[]; + labels: string[]; + selectedComboOptions: EuiComboBoxOptionOption[]; +} + +/** + * Determines the options, selected values and option labels for EUI combo box + * @param options options user can select from + * @param selectedOptions user selection if any + * @param getLabel helper function to know which property to use for labels + */ +export const getGenericComboBoxProps = <T>({ + getLabel, + options, + selectedOptions, + disabledOptions, +}: { + getLabel: (value: T) => string; + options: T[]; + selectedOptions: T[]; + disabledOptions?: T[]; +}): GetGenericComboBoxPropsReturn => { + const newLabels = options.map(getLabel); + const disabledLabels = disabledOptions?.map(getLabel); + const newComboOptions: EuiComboBoxOptionOption[] = newLabels.map((label) => ({ + label, + disabled: disabledLabels && disabledLabels.length !== 0 && disabledLabels.includes(label), + })); + const newSelectedComboOptions = selectedOptions + .map(getLabel) + .filter((option) => { + return newLabels.indexOf(option) !== -1; + }) + .map((option) => { + return newComboOptions[newLabels.indexOf(option)]; + }); + + return { + comboOptions: newComboOptions, + labels: newLabels, + selectedComboOptions: newSelectedComboOptions, + }; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_operators/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_operators/index.test.ts new file mode 100644 index 000000000000..db031a65e0bb --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_operators/index.test.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + doesNotExistOperator, + EVENT_FILTERS_OPERATORS, + ALL_OPERATORS, + existsOperator, + isNotOperator, + isOperator, +} from '@kbn/securitysolution-list-utils'; +import { getOperators } from '.'; +import { getField } from '../fields/index.mock'; + +describe('#getOperators', () => { + test('it returns "isOperator" if passed in field is "undefined"', () => { + const operator = getOperators(undefined); + + expect(operator).toEqual([isOperator]); + }); + + test('it returns expected operators when field type is "boolean"', () => { + const operator = getOperators(getField('ssl')); + + expect(operator).toEqual([isOperator, isNotOperator, existsOperator, doesNotExistOperator]); + }); + + test('it returns "isOperator" when field type is "nested"', () => { + const operator = getOperators({ + name: 'nestedField', + scripted: false, + subType: { nested: { path: 'nestedField' } }, + type: 'nested', + }); + + expect(operator).toEqual([isOperator]); + }); + + test('it includes a "matches" operator when field is "file.path.text"', () => { + const operator = getOperators({ + name: 'file.path.text', + type: 'simple', + }); + + expect(operator).toEqual(EVENT_FILTERS_OPERATORS); + }); + + test('it returns all operator types when field type is not null, boolean, or nested', () => { + const operator = getOperators(getField('machine.os.raw')); + + expect(operator).toEqual(ALL_OPERATORS); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_operators/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_operators/index.ts new file mode 100644 index 000000000000..451d4f5c21a7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/get_operators/index.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataViewFieldBase } from '@kbn/es-query'; + +import { + ALL_OPERATORS, + EVENT_FILTERS_OPERATORS, + OperatorOption, + doesNotExistOperator, + existsOperator, + isNotOperator, + isOperator, +} from '@kbn/securitysolution-list-utils'; + +/** + * Returns the appropriate operators given a field type + * + * @param field DataViewFieldBase selected field + * + */ +export const getOperators = (field: DataViewFieldBase | undefined): OperatorOption[] => { + if (field == null) { + return [isOperator]; + } else if (field.type === 'boolean') { + return [isOperator, isNotOperator, existsOperator, doesNotExistOperator]; + } else if (field.type === 'nested') { + return [isOperator]; + } else if (field.name === 'file.path.text') { + return EVENT_FILTERS_OPERATORS; + } else { + return ALL_OPERATORS; + } +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/hooks/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/hooks/index.ts new file mode 100644 index 000000000000..e9d4507b1400 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/hooks/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 * from './use_field_value_autocomplete'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.test.ts new file mode 100644 index 000000000000..7d776db12413 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.test.ts @@ -0,0 +1,298 @@ +/* + * 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 { waitFor, renderHook } from '@testing-library/react'; +import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { UseFieldValueAutocompleteReturn, useFieldValueAutocomplete } from '.'; +import { getField } from '../../fields/index.mock'; +import { autocompleteStartMock } from '../../autocomplete/index.mock'; +import { DataViewFieldBase } from '@kbn/es-query'; + +// Copied from "src/plugins/data/common/index_patterns/index_pattern.stub.ts" +// TODO: Remove this in favor of the above if/when it is ported, https://github.com/elastic/kibana/issues/100715 +export const stubIndexPatternWithFields = { + id: '1234', + title: 'logstash-*', + fields: [ + { + name: 'response', + type: 'number', + esTypes: ['integer'], + aggregatable: true, + filterable: true, + searchable: true, + }, + ], +}; + +describe('use_field_value_autocomplete', () => { + const onErrorMock = jest.fn(); + const getValueSuggestionsMock = jest.fn().mockResolvedValue(['value 1', 'value 2']); + + afterEach(() => { + onErrorMock.mockClear(); + getValueSuggestionsMock.mockClear(); + }); + + test('initializes hook', async () => { + const { result } = renderHook(() => + useFieldValueAutocomplete({ + autocompleteService: { + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }, + fieldValue: '', + indexPattern: undefined, + operatorType: OperatorTypeEnum.MATCH, + query: '', + selectedField: undefined, + }) + ); + await waitFor(() => expect(result.current).toEqual([false, true, [], result.current[3]])); + }); + + test('does not call autocomplete service if "operatorType" is "exists"', async () => { + const { result } = renderHook(() => + useFieldValueAutocomplete({ + autocompleteService: { + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }, + fieldValue: '', + indexPattern: stubIndexPatternWithFields, + operatorType: OperatorTypeEnum.EXISTS, + query: '', + selectedField: getField('machine.os'), + }) + ); + + await waitFor(() => { + const expectedResult: UseFieldValueAutocompleteReturn = [false, true, [], result.current[3]]; + + expect(result.current).toEqual(expectedResult); + expect(getValueSuggestionsMock).not.toHaveBeenCalled(); + }); + }); + + test('does not call autocomplete service if "selectedField" is undefined', async () => { + const { result } = renderHook(() => + useFieldValueAutocomplete({ + autocompleteService: { + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }, + fieldValue: '', + indexPattern: stubIndexPatternWithFields, + operatorType: OperatorTypeEnum.EXISTS, + query: '', + selectedField: undefined, + }) + ); + + await waitFor(() => { + const expectedResult: UseFieldValueAutocompleteReturn = [false, true, [], result.current[3]]; + + expect(result.current).toEqual(expectedResult); + expect(getValueSuggestionsMock).not.toHaveBeenCalled(); + }); + }); + + test('does not call autocomplete service if "indexPattern" is undefined', async () => { + const { result } = renderHook(() => + useFieldValueAutocomplete({ + autocompleteService: { + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }, + fieldValue: '', + indexPattern: undefined, + operatorType: OperatorTypeEnum.EXISTS, + query: '', + selectedField: getField('machine.os'), + }) + ); + + await waitFor(() => { + const expectedResult: UseFieldValueAutocompleteReturn = [false, true, [], result.current[3]]; + + expect(result.current).toEqual(expectedResult); + expect(getValueSuggestionsMock).not.toHaveBeenCalled(); + }); + }); + + test('it uses full path name for nested fields to fetch suggestions', async () => { + const suggestionsMock = jest.fn().mockResolvedValue([]); + + const selectedField: DataViewFieldBase | undefined = getField('nestedField.child'); + if (selectedField == null) { + throw new TypeError('selectedField for this test should always be defined'); + } + + const { signal } = new AbortController(); + renderHook(() => + useFieldValueAutocomplete({ + autocompleteService: { + ...autocompleteStartMock, + getValueSuggestions: suggestionsMock, + }, + fieldValue: '', + indexPattern: stubIndexPatternWithFields, + operatorType: OperatorTypeEnum.MATCH, + query: '', + selectedField: { ...selectedField, name: 'child' }, + }) + ); + + await waitFor(() => + expect(suggestionsMock).toHaveBeenCalledWith({ + field: { ...getField('nestedField.child'), name: 'nestedField.child' }, + indexPattern: { + fields: [ + { + aggregatable: true, + esTypes: ['integer'], + filterable: true, + name: 'response', + searchable: true, + type: 'number', + }, + ], + id: '1234', + title: 'logstash-*', + }, + query: '', + signal, + useTimeRange: false, + }) + ); + }); + + test('returns "isSuggestingValues" of false if field type is boolean', async () => { + const { result } = renderHook(() => + useFieldValueAutocomplete({ + autocompleteService: { + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }, + fieldValue: '', + indexPattern: stubIndexPatternWithFields, + operatorType: OperatorTypeEnum.MATCH, + query: '', + selectedField: getField('ssl'), + }) + ); + + await waitFor(() => { + const expectedResult: UseFieldValueAutocompleteReturn = [false, false, [], result.current[3]]; + + expect(result.current).toEqual(expectedResult); + expect(getValueSuggestionsMock).not.toHaveBeenCalled(); + }); + }); + + test('returns "isSuggestingValues" of false to note that autocomplete service is not in use if no autocomplete suggestions available', async () => { + const suggestionsMock = jest.fn().mockResolvedValue([]); + + const { result } = renderHook(() => + useFieldValueAutocomplete({ + autocompleteService: { + ...autocompleteStartMock, + getValueSuggestions: suggestionsMock, + }, + fieldValue: '', + indexPattern: stubIndexPatternWithFields, + operatorType: OperatorTypeEnum.MATCH, + query: '', + selectedField: getField('bytes'), + }) + ); + + await waitFor(() => { + const expectedResult: UseFieldValueAutocompleteReturn = [false, false, [], result.current[3]]; + + expect(suggestionsMock).toHaveBeenCalled(); + expect(result.current).toEqual(expectedResult); + }); + }); + + test('returns suggestions', async () => { + const { signal } = new AbortController(); + const { result } = renderHook(() => + useFieldValueAutocomplete({ + autocompleteService: { + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }, + fieldValue: '', + indexPattern: stubIndexPatternWithFields, + operatorType: OperatorTypeEnum.MATCH, + query: '', + selectedField: getField('@tags'), + }) + ); + + await waitFor(() => { + const expectedResult: UseFieldValueAutocompleteReturn = [ + false, + true, + ['value 1', 'value 2'], + result.current[3], + ]; + + expect(getValueSuggestionsMock).toHaveBeenCalledWith({ + field: getField('@tags'), + indexPattern: stubIndexPatternWithFields, + query: '', + signal, + useTimeRange: false, + }); + expect(result.current).toEqual(expectedResult); + }); + }); + + test('returns new suggestions on subsequent calls', async () => { + const { result } = renderHook(() => + useFieldValueAutocomplete({ + autocompleteService: { + ...autocompleteStartMock, + getValueSuggestions: getValueSuggestionsMock, + }, + fieldValue: '', + indexPattern: stubIndexPatternWithFields, + operatorType: OperatorTypeEnum.MATCH, + query: '', + selectedField: getField('@tags'), + }) + ); + + await waitFor(() => expect(result.current[3]).not.toBeNull()); + + // Added check for typescripts sake, if null, + // would not reach below logic as test would stop above + if (result.current[3] != null) { + result.current[3]({ + fieldSelected: getField('@tags'), + patterns: stubIndexPatternWithFields, + searchQuery: '', + value: 'hello', + }); + } + + await waitFor(() => { + const expectedResult: UseFieldValueAutocompleteReturn = [ + false, + true, + ['value 1', 'value 2'], + result.current[3], + ]; + + expect(getValueSuggestionsMock).toHaveBeenCalledTimes(2); + expect(result.current).toEqual(expectedResult); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.ts new file mode 100644 index 000000000000..dbbe321014e2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect, useRef, useState } from 'react'; +import { debounce } from 'lodash'; +import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { DataViewBase, DataViewFieldBase, getDataViewFieldSubtypeNested } from '@kbn/es-query'; + +// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 +// import { AutocompleteStart } from '../../../../../../../../../../../src/plugins/unified_search/public'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AutocompleteStart = any; + +interface FuncArgs { + fieldSelected: DataViewFieldBase | undefined; + patterns: DataViewBase | undefined; + searchQuery: string; + value: string | string[] | undefined; +} + +type Func = (args: FuncArgs) => void; + +export type UseFieldValueAutocompleteReturn = [boolean, boolean, string[], Func | null]; + +export interface UseFieldValueAutocompleteProps { + autocompleteService: AutocompleteStart; + fieldValue: string | string[] | undefined; + indexPattern: DataViewBase | undefined; + operatorType: OperatorTypeEnum; + query: string; + selectedField: DataViewFieldBase | undefined; +} +/** + * Hook for using the field value autocomplete service + */ +export const useFieldValueAutocomplete = ({ + selectedField, + operatorType, + fieldValue, + query, + indexPattern, + autocompleteService, +}: UseFieldValueAutocompleteProps): UseFieldValueAutocompleteReturn => { + const [isLoading, setIsLoading] = useState(false); + const [isSuggestingValues, setIsSuggestingValues] = useState(true); + const [suggestions, setSuggestions] = useState<string[]>([]); + const updateSuggestions = useRef<Func | null>(null); + + useEffect(() => { + let isSubscribed = true; + const abortCtrl = new AbortController(); + + const fetchSuggestions = debounce( + async ({ fieldSelected, patterns, searchQuery }: FuncArgs) => { + try { + if (isSubscribed) { + if (fieldSelected == null || patterns == null) { + return; + } + + if (fieldSelected.type === 'boolean') { + setIsSuggestingValues(false); + return; + } + + setIsLoading(true); + const subTypeNested = getDataViewFieldSubtypeNested(fieldSelected); + const field = subTypeNested + ? { + ...fieldSelected, + name: `${subTypeNested.nested.path}.${fieldSelected.name}`, + } + : fieldSelected; + + const newSuggestions = await autocompleteService.getValueSuggestions({ + field, + indexPattern: patterns, + query: searchQuery, + signal: abortCtrl.signal, + useTimeRange: false, + }); + + if (newSuggestions.length === 0) { + setIsSuggestingValues(false); + } + + setIsLoading(false); + setSuggestions([...newSuggestions]); + } + } catch (error) { + if (isSubscribed) { + setSuggestions([]); + setIsLoading(false); + } + } + }, + 500 + ); + + if (operatorType !== OperatorTypeEnum.EXISTS) { + fetchSuggestions({ + fieldSelected: selectedField, + patterns: indexPattern, + searchQuery: query, + value: fieldValue, + }); + } + + updateSuggestions.current = fetchSuggestions; + + return (): void => { + isSubscribed = false; + abortCtrl.abort(); + }; + }, [selectedField, operatorType, fieldValue, indexPattern, query, autocompleteService]); + + return [isLoading, isSuggestingValues, suggestions, updateSuggestions.current]; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/list_schema/index.mock.ts new file mode 100644 index 000000000000..aade602f1950 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/list_schema/index.mock.ts @@ -0,0 +1,60 @@ +/* + * 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 { + FoundListSchema, + ListSchema, + FoundListsBySizeSchema, +} from '@kbn/securitysolution-io-ts-list-types'; + +// TODO: Once this mock is available within packages, use it instead, https://github.com/elastic/kibana/issues/100715 +// import { getFoundListSchemaMock } from '../../../../../../../../lists/common/schemas/response/found_list_schema.mock'; +export const getFoundListSchemaMock = (): FoundListSchema => ({ + cursor: '123', + data: [getListResponseMock()], + page: 1, + per_page: 1, + total: 1, +}); + +export const getFoundListsBySizeSchemaMock = (): FoundListsBySizeSchema => ({ + smallLists: [getListResponseMock()], + largeLists: [getListResponseMock()], +}); + +// TODO: Once these mocks are available from packages use it instead, https://github.com/elastic/kibana/issues/100715 +export const DATE_NOW = '2020-04-20T15:25:31.830Z'; +export const USER = 'some user'; +export const IMMUTABLE = false; +export const VERSION = 1; +export const DESCRIPTION = 'some description'; +export const TIE_BREAKER = '6a76b69d-80df-4ab2-8c3e-85f466b06a0e'; +export const LIST_ID = 'some-list-id'; +export const META = {}; +export const TYPE = 'ip'; +export const NAME = 'some name'; + +// TODO: Once this mock is available within packages, use it instead, https://github.com/elastic/kibana/issues/100715 +// import { getListResponseMock } from '../../../../../../../../lists/common/schemas/response/list_schema.mock'; +export const getListResponseMock = (): ListSchema => ({ + '@timestamp': DATE_NOW, + _version: undefined, + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, + deserializer: undefined, + id: LIST_ID, + immutable: IMMUTABLE, + meta: META, + name: NAME, + serializer: undefined, + tie_breaker_id: TIE_BREAKER, + type: TYPE, + updated_at: DATE_NOW, + updated_by: USER, + version: VERSION, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx new file mode 100644 index 000000000000..eef8738b67c5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx @@ -0,0 +1,253 @@ +/* + * 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 { mount } from 'enzyme'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { isNotOperator, isOperator } from '@kbn/securitysolution-list-utils'; + +import { OperatorComponent } from '.'; +import { getField } from '../fields/index.mock'; + +describe('operator', () => { + test('it renders disabled if "isDisabled" is true', () => { + const wrapper = mount( + <OperatorComponent + isClearable={false} + isDisabled={true} + isLoading={false} + onChange={jest.fn()} + operator={isOperator} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + + expect( + wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"] input`).prop('disabled') + ).toBeTruthy(); + }); + + test('it renders loading if "isLoading" is true', () => { + const wrapper = mount( + <OperatorComponent + isClearable={false} + isDisabled={false} + isLoading={true} + onChange={jest.fn()} + operator={isOperator} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"] button`).at(0).simulate('click'); + expect( + wrapper + .find(`EuiComboBoxOptionsList[data-test-subj="operatorAutocompleteComboBox-optionsList"]`) + .prop('isLoading') + ).toBeTruthy(); + }); + + test('it allows user to clear values if "isClearable" is true', () => { + const wrapper = mount( + <OperatorComponent + isClearable={true} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + operator={isOperator} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + + expect(wrapper.find(`button[data-test-subj="comboBoxClearButton"]`).exists()).toBeTruthy(); + }); + + test('it displays "operatorOptions" if param is passed in with items', () => { + const wrapper = mount( + <OperatorComponent + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + operator={isOperator} + operatorOptions={[isNotOperator]} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + + expect( + wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') + ).toEqual([{ label: 'is not' }]); + }); + + test('it does not display "operatorOptions" if param is passed in with no items', () => { + const wrapper = mount( + <OperatorComponent + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + operator={isOperator} + operatorOptions={[]} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + + expect( + wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') + ).toEqual([ + { + label: 'is', + }, + { + label: 'is not', + }, + { + label: 'is one of', + }, + { + label: 'is not one of', + }, + { + label: 'exists', + }, + { + label: 'does not exist', + }, + { + label: 'is in list', + }, + { + label: 'is not in list', + }, + { + label: 'matches', + }, + { + label: 'does not match', + }, + ]); + }); + + test('it correctly displays selected operator', () => { + const wrapper = mount( + <OperatorComponent + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + operator={isOperator} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').at(0).props().value + ).toEqual('is'); + }); + + test('it only displays subset of operators if field type is nested', () => { + const wrapper = mount( + <OperatorComponent + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + operator={isOperator} + placeholder="Placeholder text" + selectedField={{ + name: 'nestedField', + scripted: false, + subType: { nested: { path: 'nestedField' } }, + type: 'nested', + }} + /> + ); + + expect( + wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') + ).toEqual([{ label: 'is' }]); + }); + + test('it only displays subset of operators if field type is boolean', () => { + const wrapper = mount( + <OperatorComponent + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + operator={isOperator} + placeholder="Placeholder text" + selectedField={getField('ssl')} + /> + ); + + expect( + wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') + ).toEqual([ + { label: 'is' }, + { label: 'is not' }, + { label: 'exists' }, + { label: 'does not exist' }, + ]); + }); + + test('it only displays subset of operators if field name is "file.path.text"', () => { + const wrapper = mount( + <OperatorComponent + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={jest.fn()} + operator={isOperator} + placeholder="Placeholder text" + selectedField={getField('file.path.text')} + /> + ); + + expect( + wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"]`).at(0).prop('options') + ).toEqual([ + { label: 'is' }, + { label: 'is not' }, + { label: 'is one of' }, + { label: 'is not one of' }, + { label: 'matches' }, + { label: 'does not match' }, + ]); + }); + + test('it invokes "onChange" when option selected', () => { + const mockOnChange = jest.fn(); + const wrapper = mount( + <OperatorComponent + isClearable={false} + isDisabled={false} + isLoading={false} + onChange={mockOnChange} + operator={isOperator} + placeholder="Placeholder text" + selectedField={getField('machine.os.raw')} + /> + ); + + ( + wrapper.find(EuiComboBox).props() as unknown as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + } + ).onChange([{ label: 'is not' }]); + + expect(mockOnChange).toHaveBeenCalledWith([ + { message: 'is not', operator: 'excluded', type: 'match', value: 'is_not' }, + ]); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/operator/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/operator/index.tsx new file mode 100644 index 000000000000..6c91f7e70b94 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/operator/index.tsx @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { OperatorOption } from '@kbn/securitysolution-list-utils'; +import { DataViewFieldBase } from '@kbn/es-query'; + +import { getOperators } from '../get_operators'; +import { + getGenericComboBoxProps, + GetGenericComboBoxPropsReturn, +} from '../get_generic_combo_box_props'; + +const AS_PLAIN_TEXT = { asPlainText: true }; + +interface OperatorState { + isClearable: boolean; + isDisabled: boolean; + isLoading: boolean; + onChange: (arg: OperatorOption[]) => void; + operator: OperatorOption; + operatorInputWidth?: number; + operatorOptions?: OperatorOption[]; + placeholder: string; + selectedField: DataViewFieldBase | undefined; + 'aria-label'?: string; +} + +export const OperatorComponent: React.FC<OperatorState> = ({ + isClearable = false, + isDisabled = false, + isLoading = false, + onChange, + operator, + operatorOptions, + operatorInputWidth = 150, + placeholder, + selectedField, + 'aria-label': ariaLabel, +}): JSX.Element => { + const getLabel = useCallback(({ message }: OperatorOption): string => message, []); + const optionsMemo = useMemo( + (): OperatorOption[] => + operatorOptions != null && operatorOptions.length > 0 + ? operatorOptions + : getOperators(selectedField), + [operatorOptions, selectedField] + ); + const selectedOptionsMemo = useMemo( + (): OperatorOption[] => (operator ? [operator] : []), + [operator] + ); + const { comboOptions, labels, selectedComboOptions } = useMemo( + (): GetGenericComboBoxPropsReturn => + getGenericComboBoxProps<OperatorOption>({ + getLabel, + options: optionsMemo, + selectedOptions: selectedOptionsMemo, + }), + [optionsMemo, selectedOptionsMemo, getLabel] + ); + + const handleValuesChange = useCallback( + (newOptions: EuiComboBoxOptionOption[]): void => { + const newValues: OperatorOption[] = newOptions.map( + ({ label }) => optionsMemo[labels.indexOf(label)] + ); + onChange(newValues); + }, + [labels, onChange, optionsMemo] + ); + + const inputWidth = useMemo(() => { + return { width: `${operatorInputWidth}px` }; + }, [operatorInputWidth]); + + return ( + <EuiComboBox + placeholder={placeholder} + options={comboOptions} + selectedOptions={selectedComboOptions} + onChange={handleValuesChange} + isLoading={isLoading} + isDisabled={isDisabled} + isClearable={isClearable} + singleSelection={AS_PLAIN_TEXT} + data-test-subj="operatorAutocompleteComboBox" + style={inputWidth} + aria-label={ariaLabel} + /> + ); +}; + +OperatorComponent.displayName = 'Operator'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.test.ts new file mode 100644 index 000000000000..d0571d157ec2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.test.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 { paramContainsSpace } from '.'; + +describe('param_contains_space', () => { + test('should return true if leading spaces were found', () => { + expect(paramContainsSpace(' test')).toBeTruthy(); + }); + test('should return true if trailing spaces were found', () => { + expect(paramContainsSpace('test ')).toBeTruthy(); + }); + test('should return true if both trailing and leading spaces were found', () => { + expect(paramContainsSpace(' test ')).toBeTruthy(); + }); + test('should return true if tabs was found', () => { + expect(paramContainsSpace('\ttest')).toBeTruthy(); + }); + test('should return false if no spaces were found', () => { + expect(paramContainsSpace('test test')).toBeFalsy(); + }); + test('should return false if param is falsy', () => { + expect(paramContainsSpace('')).toBeFalsy(); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_contains_space/index.ts new file mode 100644 index 000000000000..3d7bb99ae3d0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_contains_space/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 const paramContainsSpace = (param: string) => param && param.trim().length !== param.length; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.test.ts new file mode 100644 index 000000000000..f93ad28eb4ad --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { paramIsValid } from '.'; +import { getField } from '../fields/index.mock'; +import * as i18n from '../translations'; +import moment from 'moment'; + +describe('params_is_valid', () => { + beforeEach(() => { + // Disable momentJS deprecation warning and it looks like it is not typed either so + // we have to disable the type as well and cannot extend it easily. + ( + moment as unknown as { + suppressDeprecationWarnings: boolean; + } + ).suppressDeprecationWarnings = true; + }); + + afterEach(() => { + // Re-enable momentJS deprecation warning and it looks like it is not typed either so + // we have to disable the type as well and cannot extend it easily. + ( + moment as unknown as { + suppressDeprecationWarnings: boolean; + } + ).suppressDeprecationWarnings = false; + }); + + test('returns no errors if no field has been selected', () => { + const isValid = paramIsValid('', undefined, true, false); + + expect(isValid).toBeUndefined(); + }); + + test('returns error string if user has touched a required input and left empty', () => { + const isValid = paramIsValid(undefined, getField('@timestamp'), true, true); + + expect(isValid).toEqual(i18n.FIELD_REQUIRED_ERR); + }); + + test('returns no errors if required input is empty but user has not yet touched it', () => { + const isValid = paramIsValid(undefined, getField('@timestamp'), true, false); + + expect(isValid).toBeUndefined(); + }); + + test('returns no errors if user has touched an input that is not required and left empty', () => { + const isValid = paramIsValid(undefined, getField('@timestamp'), false, true); + + expect(isValid).toBeUndefined(); + }); + + test('returns no errors if user has touched an input that is not required and left empty string', () => { + const isValid = paramIsValid('', getField('@timestamp'), false, true); + + expect(isValid).toBeUndefined(); + }); + + test('returns no errors if field is of type date and value is valid', () => { + const isValid = paramIsValid('1994-11-05T08:15:30-05:00', getField('@timestamp'), false, true); + + expect(isValid).toBeUndefined(); + }); + + test('returns errors if filed is of type date and value is not valid', () => { + const isValid = paramIsValid('1593478826', getField('@timestamp'), false, true); + + expect(isValid).toEqual(i18n.DATE_ERR); + }); + + test('returns no errors if field is of type number and value is an integer', () => { + const isValid = paramIsValid('4', getField('bytes'), true, true); + + expect(isValid).toBeUndefined(); + }); + + test('returns no errors if field is of type number and value is a float', () => { + const isValid = paramIsValid('4.3', getField('bytes'), true, true); + + expect(isValid).toBeUndefined(); + }); + + test('returns no errors if field is of type number and value is a long', () => { + const isValid = paramIsValid('-9223372036854775808', getField('bytes'), true, true); + + expect(isValid).toBeUndefined(); + }); + + test('returns errors if field is of type number and value is "hello"', () => { + const isValid = paramIsValid('hello', getField('bytes'), true, true); + + expect(isValid).toEqual(i18n.NUMBER_ERR); + }); + + test('returns errors if field is of type number and value is "123abc"', () => { + const isValid = paramIsValid('123abc', getField('bytes'), true, true); + + expect(isValid).toEqual(i18n.NUMBER_ERR); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.ts new file mode 100644 index 000000000000..3d091b7a9b21 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.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 dateMath from '@kbn/datemath'; +import { DataViewFieldBase } from '@kbn/es-query'; +import { checkEmptyValue } from '../check_empty_value'; + +import * as i18n from '../translations'; + +/** + * Very basic validation for values + * @param param the value being checked + * @param field the selected field + * @param isRequired whether or not an empty value is allowed + * @param touched has field been touched by user + * @returns undefined if valid, string with error message if invalid + */ +export const paramIsValid = ( + param: string | undefined, + field: DataViewFieldBase | undefined, + isRequired: boolean, + touched: boolean +): string | undefined => { + if (field == null) { + return undefined; + } + + const emptyValueError = checkEmptyValue(param, field, isRequired, touched); + if (emptyValueError !== null) { + return emptyValueError; + } + + switch (field.type) { + case 'date': + const moment = dateMath.parse(param ?? ''); + const isDate = Boolean(moment && moment.isValid()); + return isDate ? undefined : i18n.DATE_ERR; + case 'number': + const isNum = param != null && param.trim() !== '' && !isNaN(+param); + return isNum ? undefined : i18n.NUMBER_ERR; + default: + return undefined; + } +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/translations/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/translations/index.ts new file mode 100644 index 000000000000..51790e96ba68 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/translations/index.ts @@ -0,0 +1,79 @@ +/* + * 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'; + +export const LOADING = i18n.translate('autocomplete.loadingDescription', { + defaultMessage: 'Loading...', +}); + +export const SELECT_FIELD_FIRST = i18n.translate('autocomplete.selectField', { + defaultMessage: 'Please select a field first...', +}); + +export const FIELD_REQUIRED_ERR = i18n.translate('autocomplete.fieldRequiredError', { + defaultMessage: 'Value cannot be empty', +}); + +export const NUMBER_ERR = i18n.translate('autocomplete.invalidNumberError', { + defaultMessage: 'Not a valid number', +}); + +export const DATE_ERR = i18n.translate('autocomplete.invalidDateError', { + defaultMessage: 'Not a valid date', +}); + +export const BINARY_TYPE_NOT_SUPPORTED = i18n.translate('autocomplete.invalidBinaryType', { + defaultMessage: 'Binary fields are currently unsupported', +}); +export const FIELD_SPACE_WARNING = i18n.translate('autocomplete.fieldSpaceWarning', { + defaultMessage: "Warning: Spaces at the start or end of this value aren't being displayed.", +}); + +export const LISTS_TOOLTIP_INFO = i18n.translate('autocomplete.listsTooltipWarning', { + defaultMessage: "Lists that aren't able to be processed by this rule type will be disabled.", +}); + +export const SEE_DOCUMENTATION = i18n.translate('autocomplete.seeDocumentation', { + defaultMessage: 'See Documentation', +}); + +export const FIELD_CONFLICT_INDICES_WARNING_TITLE = i18n.translate( + 'autocomplete.conflictIndicesWarning.title', + { + defaultMessage: 'Mapping Conflict', + } +); + +export const FIELD_CONFLICT_INDICES_WARNING_DESCRIPTION = i18n.translate( + 'autocomplete.conflictIndicesWarning.description', + { + defaultMessage: + 'This field is defined as different types across the following indices or is unmapped. This can cause unexpected query results.', + } +); + +export const CONFLICT_MULTIPLE_INDEX_DESCRIPTION = (name: string, count: number): string => + i18n.translate('autocomplete.conflictIndicesWarning.index.description', { + defaultMessage: '{name} ({count} indices)', + values: { count, name }, + }); + +export const SHOW_VALUE_LIST_MODAL = i18n.translate('autocomplete.showValueListModal', { + defaultMessage: 'Show value list', +}); + +// eslint-disable-next-line import/no-default-export +export default { + LOADING, + SELECT_FIELD_FIRST, + FIELD_REQUIRED_ERR, + NUMBER_ERR, + DATE_ERR, + FIELD_SPACE_WARNING, + BINARY_TYPE_NOT_SUPPORTED, +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/type_match/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/type_match/index.test.ts new file mode 100644 index 000000000000..74c713f63d36 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/type_match/index.test.ts @@ -0,0 +1,58 @@ +/* + * 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 { typeMatch } from '.'; + +describe('type_match', () => { + test('ip -> ip is true', () => { + expect(typeMatch('ip', 'ip')).toEqual(true); + }); + + test('keyword -> keyword is true', () => { + expect(typeMatch('keyword', 'keyword')).toEqual(true); + }); + + test('text -> text is true', () => { + expect(typeMatch('text', 'text')).toEqual(true); + }); + + test('ip_range -> ip is true', () => { + expect(typeMatch('ip_range', 'ip')).toEqual(true); + }); + + test('date_range -> date is true', () => { + expect(typeMatch('date_range', 'date')).toEqual(true); + }); + + test('double_range -> double is true', () => { + expect(typeMatch('double_range', 'double')).toEqual(true); + }); + + test('float_range -> float is true', () => { + expect(typeMatch('float_range', 'float')).toEqual(true); + }); + + test('integer_range -> integer is true', () => { + expect(typeMatch('integer_range', 'integer')).toEqual(true); + }); + + test('long_range -> long is true', () => { + expect(typeMatch('long_range', 'long')).toEqual(true); + }); + + test('ip -> date is false', () => { + expect(typeMatch('ip', 'date')).toEqual(false); + }); + + test('long -> float is false', () => { + expect(typeMatch('long', 'float')).toEqual(false); + }); + + test('integer -> long is false', () => { + expect(typeMatch('integer', 'long')).toEqual(false); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/type_match/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/type_match/index.ts new file mode 100644 index 000000000000..0e368127b596 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/src/type_match/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Type } from '@kbn/securitysolution-io-ts-list-types'; + +/** + * Given an input list type and a string based ES type this will match + * if they're exact or if they are compatible with a range + * @param type The type to match against the esType + * @param esType The ES type to match with + */ +export const typeMatch = (type: Type, esType: string): boolean => { + return ( + type === esType || + (type === 'ip_range' && esType === 'ip') || + (type === 'date_range' && esType === 'date') || + (type === 'double_range' && esType === 'double') || + (type === 'float_range' && esType === 'float') || + (type === 'integer_range' && esType === 'integer') || + (type === 'long_range' && esType === 'long') + ); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/tsconfig.json new file mode 100644 index 000000000000..e4f0b64736b0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-autocomplete/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": ["jest", "node"] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "kbn_references": [ + "@kbn/datemath", + "@kbn/es-query", + "@kbn/i18n", + "@kbn/securitysolution-io-ts-list-types", + "@kbn/securitysolution-list-hooks", + "@kbn/securitysolution-list-utils", + "@kbn/doc-links", + "@kbn/securitysolution-utils", + ], + "exclude": [ + "target/**/*", + ], +} diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/README.md similarity index 100% rename from packages/kbn-securitysolution-endpoint-exceptions-common/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.gen.ts new file mode 100644 index 000000000000..c8e4f90c347a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.gen.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Create endpoint list API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { EndpointList } from '../model/endpoint_list_common.gen'; + +export type CreateEndpointListResponse = z.infer<typeof CreateEndpointListResponse>; +export const CreateEndpointListResponse = EndpointList; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.schema.yaml new file mode 100644 index 000000000000..12b131e728c5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list/create_endpoint_list.schema.yaml @@ -0,0 +1,45 @@ +openapi: 3.0.0 +info: + title: Create endpoint list API endpoint + version: '2023-10-31' +paths: + /api/endpoint_list: + post: + x-labels: [serverless, ess] + x-codegen-enabled: true + operationId: CreateEndpointList + summary: Create an endpoint exception list + description: Create an endpoint exception list, which groups endpoint exception list items. If an endpoint exception list already exists, an empty response is returned. + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/endpoint_list_common.schema.yaml#/components/schemas/EndpointList' + 400: + description: Invalid input data + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Insufficient privileges + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.gen.ts similarity index 81% rename from packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.gen.ts index ed3a60dc08f3..c4ce0697b237 100644 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.schema.yaml similarity index 79% rename from packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.schema.yaml index b90bee75fc07..0393fa3d943e 100644 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/create_endpoint_list_item/create_endpoint_list_item.schema.yaml @@ -57,29 +57,29 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Insufficient privileges content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 409: description: Endpoint list item already exists content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 500: description: Internal server error content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.gen.ts similarity index 75% rename from packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.gen.ts index 5cb5f518494e..4f268641f93c 100644 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.schema.yaml new file mode 100644 index 000000000000..fac5a12ecc5d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/delete_endpoint_list_item/delete_endpoint_list_item.schema.yaml @@ -0,0 +1,64 @@ +openapi: 3.0.0 +info: + title: Delete endpoint list item API endpoint + version: '2023-10-31' +paths: + /api/endpoint_list/items: + delete: + x-labels: [serverless, ess] + x-codegen-enabled: true + operationId: DeleteEndpointListItem + summary: Delete an endpoint exception list item + description: Delete an endpoint exception list item using the `id` or `item_id` field. + parameters: + - name: id + in: query + required: false + description: Either `id` or `item_id` must be specified + schema: + $ref: '../../../kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemId' + - name: item_id + in: query + required: false + description: Either `id` or `item_id` must be specified + schema: + $ref: '../../../kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemHumanId' + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/endpoint_list_common.schema.yaml#/components/schemas/EndpointListItem' + 400: + description: Invalid input data + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Insufficient privileges + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: Endpoint list item not found + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.gen.ts similarity index 83% rename from packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.gen.ts index a830572c7b50..5b1e10d9bb04 100644 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.schema.yaml new file mode 100644 index 000000000000..35f565bfa27f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/find_endpoint_list_item/find_endpoint_list_item.schema.yaml @@ -0,0 +1,113 @@ +openapi: 3.0.0 +info: + title: Find endpoint list items API endpoint + version: '2023-10-31' +paths: + /api/endpoint_list/items/_find: + get: + x-labels: [serverless, ess] + x-codegen-enabled: true + operationId: FindEndpointListItems + summary: Get endpoint exception list items + description: Get a list of all endpoint exception list items. + parameters: + - name: filter + in: query + required: false + description: | + Filters the returned results according to the value of the specified field, + using the `<field name>:<field value>` syntax. + schema: + $ref: '#/components/schemas/FindEndpointListItemsFilter' + - name: page + in: query + required: false + description: The page number to return + schema: + type: integer + minimum: 0 + - name: per_page + in: query + required: false + description: The number of exception list items to return per page + schema: + type: integer + minimum: 0 + - name: sort_field + in: query + required: false + description: Determines which field is used to sort the results + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + - name: sort_order + in: query + required: false + description: Determines the sort order, which can be `desc` or `asc` + schema: + type: string + enum: [desc, asc] + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '../model/endpoint_list_common.schema.yaml#/components/schemas/EndpointListItem' + page: + type: integer + minimum: 0 + per_page: + type: integer + minimum: 0 + total: + type: integer + minimum: 0 + pit: + type: string + required: + - data + - page + - per_page + - total + 400: + description: Invalid input data + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Insufficient privileges + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: Endpoint list not found + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + +components: + schemas: + FindEndpointListItemsFilter: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/index.ts new file mode 100644 index 000000000000..912af19448e4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/index.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 * from './create_endpoint_list/create_endpoint_list.gen'; +export * from './create_endpoint_list_item/create_endpoint_list_item.gen'; +export * from './read_endpoint_list_item/read_endpoint_list_item.gen'; +export * from './update_endpoint_list_item/update_endpoint_list_item.gen'; +export * from './delete_endpoint_list_item/delete_endpoint_list_item.gen'; +export * from './find_endpoint_list_item/find_endpoint_list_item.gen'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/model/endpoint_list_common.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/model/endpoint_list_common.gen.ts new file mode 100644 index 000000000000..a68abd53c11c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/model/endpoint_list_common.gen.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Common Exception List Attributes + * version: not applicable + */ + +import { z } from '@kbn/zod'; + +import { + ExceptionList, + ExceptionListItem, +} from '@kbn/securitysolution-exceptions-common/api/model/exception_list_common.gen'; + +export type EndpointList = z.infer<typeof EndpointList>; +export const EndpointList = z.union([ExceptionList, z.object({}).strict()]); + +export type EndpointListItem = z.infer<typeof EndpointListItem>; +export const EndpointListItem = ExceptionListItem; diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/model/endpoint_list_common.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/model/endpoint_list_common.schema.yaml similarity index 100% rename from packages/kbn-securitysolution-endpoint-exceptions-common/api/model/endpoint_list_common.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/model/endpoint_list_common.schema.yaml diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.gen.ts similarity index 75% rename from packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.gen.ts index 4a9c335b395b..e2dc38450bbb 100644 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.schema.yaml new file mode 100644 index 000000000000..45f0c384c7f0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/read_endpoint_list_item/read_endpoint_list_item.schema.yaml @@ -0,0 +1,66 @@ +openapi: 3.0.0 +info: + title: Read endpoint list item API endpoint + version: '2023-10-31' +paths: + /api/endpoint_list/items: + get: + x-labels: [serverless, ess] + x-codegen-enabled: true + operationId: ReadEndpointListItem + summary: Get an endpoint exception list item + description: Get the details of an endpoint exception list item using the `id` or `item_id` field. + parameters: + - name: id + in: query + required: false + description: Either `id` or `item_id` must be specified + schema: + $ref: '../../../kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemId' + - name: item_id + in: query + required: false + description: Either `id` or `item_id` must be specified + schema: + $ref: '../../../kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemHumanId' + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: array + items: + $ref: '../model/endpoint_list_common.schema.yaml#/components/schemas/EndpointListItem' + 400: + description: Invalid input data + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Insufficient privileges + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: Endpoint list item not found + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.gen.ts similarity index 83% rename from packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.gen.ts index 0a235ca2ff89..ec86992d58b7 100644 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.schema.yaml similarity index 80% rename from packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.schema.yaml index 679000674c7b..cd4cbf0c11d5 100644 --- a/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/api/update_endpoint_list_item/update_endpoint_list_item.schema.yaml @@ -62,29 +62,29 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Insufficient privileges content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 404: description: Endpoint list item not found content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 500: description: Internal server error content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/ess/security_solution_endpoint_exceptions_api_2023_10_31.bundled.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/ess/security_solution_endpoint_exceptions_api_2023_10_31.bundled.schema.yaml similarity index 100% rename from packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/ess/security_solution_endpoint_exceptions_api_2023_10_31.bundled.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/ess/security_solution_endpoint_exceptions_api_2023_10_31.bundled.schema.yaml diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/serverless/security_solution_endpoint_exceptions_api_2023_10_31.bundled.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/serverless/security_solution_endpoint_exceptions_api_2023_10_31.bundled.schema.yaml similarity index 100% rename from packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/serverless/security_solution_endpoint_exceptions_api_2023_10_31.bundled.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/serverless/security_solution_endpoint_exceptions_api_2023_10_31.bundled.schema.yaml diff --git a/packages/kbn-securitysolution-endpoint-exceptions-common/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-endpoint-exceptions-common/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/package.json new file mode 100644 index 000000000000..39e6232e4e0f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/package.json @@ -0,0 +1,11 @@ +{ + "description": "OpenAPI Endpoint Exceptions Common", + "license": "Elastic License 2.0", + "name": "@kbn/securitysolution-endpoint-exceptions-common", + "private": true, + "version": "1.0.0", + "scripts": { + "openapi:generate": "node scripts/openapi_generate", + "openapi:bundle": "node scripts/openapi_bundle" + } +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_bundle.js b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_bundle.js new file mode 100644 index 000000000000..c54e162b6462 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_bundle.js @@ -0,0 +1,65 @@ +/* + * 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. + */ + +require('../../../../../../src/setup_node_env'); +// eslint-disable-next-line import/no-nodejs-modules +const { join, resolve } = require('path'); +const { bundle } = require('@kbn/openapi-bundler'); + +const ROOT = resolve(__dirname, '..'); + +(async () => { + await bundle({ + sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), + outputFilePath: join( + ROOT, + 'docs/openapi/serverless/security_solution_endpoint_exceptions_api_{version}.bundled.schema.yaml' + ), + options: { + includeLabels: ['serverless'], + prototypeDocument: { + info: { + title: 'Security Endpoint Exceptions API (Elastic Cloud Serverless)', + description: 'Endpoint Exceptions API allow you to manage Endpoint lists.', + }, + tags: [ + { + name: 'Security Endpoint Exceptions API', + 'x-displayName': 'Security endpoint exceptions', + description: + "Endpoint Exceptions API allows you to manage detection rule endpoint exceptions to prevent a rule from generating an alert from incoming events even when the rule's other criteria are met.", + }, + ], + }, + }, + }); + + await bundle({ + sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), + outputFilePath: join( + ROOT, + 'docs/openapi/ess/security_solution_endpoint_exceptions_api_{version}.bundled.schema.yaml' + ), + options: { + includeLabels: ['ess'], + prototypeDocument: { + info: { + title: 'Security Endpoint Exceptions API (Elastic Cloud and self-hosted)', + description: 'Endpoint Exceptions API allow you to manage Endpoint lists.', + }, + tags: [ + { + name: 'Security Endpoint Exceptions API', + 'x-displayName': 'Security endpoint exceptions', + description: + "Endpoint Exceptions API allows you to manage detection rule endpoint exceptions to prevent a rule from generating an alert from incoming events even when the rule's other criteria are met.", + }, + ], + }, + }, + }); +})(); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_generate.js b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_generate.js new file mode 100644 index 000000000000..9a303c4f74be --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_generate.js @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +require('../../../../../../src/setup_node_env'); +// eslint-disable-next-line import/no-nodejs-modules +const { join, resolve } = require('path'); +const { generate } = require('@kbn/openapi-generator'); +const { REPO_ROOT } = require('@kbn/repo-info'); + +const ROOT = resolve(__dirname, '..'); + +(async () => { + await generate({ + title: 'OpenAPI Endpoint Exceptions API Schemas', + rootDir: ROOT, + sourceGlob: './api/**/*.schema.yaml', + templateName: 'zod_operation_schema', + }); + + await generate({ + title: 'Endpoint Exceptions API client for tests', + rootDir: ROOT, + sourceGlob: './api/**/*.schema.yaml', + templateName: 'api_client_supertest', + skipLinting: true, + bundle: { + outFile: join( + REPO_ROOT, + 'x-pack/test/api_integration/services/security_solution_endpoint_exceptions_api.gen.ts' + ), + }, + }); +})(); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/tsconfig.json new file mode 100644 index 000000000000..38f2843b03ad --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "outDir": "target/types", + "types": ["jest", "node"] + }, + "exclude": ["target/**/*"], + "extends": "../../../../../tsconfig.base.json", + "include": ["**/*.ts"], + "kbn_references": [ + "@kbn/securitysolution-exceptions-common", + "@kbn/openapi-common", + "@kbn/zod", + ] +} diff --git a/packages/kbn-securitysolution-exception-list-components/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/README.md similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/index.ts new file mode 100644 index 000000000000..40277892f1f7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './src/search_bar'; +export * from './src/empty_viewer_state'; +export * from './src/pagination/pagination'; +// export * from './src/exceptions_utility/exceptions_utility'; +export * from './src/exception_items'; +export * from './src/exception_item_card'; +export * from './src/value_with_space_warning'; +export * from './src/types'; +export * from './src/list_header'; +export * from './src/header_menu'; +export * from './src/generate_linked_rules_menu_item'; +export * from './src/wildcard_with_wrong_operator_callout'; +export * from './src/partial_code_signature_callout'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/jest.config.js b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/jest.config.js new file mode 100644 index 000000000000..d9a562b21e87 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: [ + '<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components', + ], + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/**/*.{ts,tsx}', + '!<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/**/*.test', + '!<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/**/types/*', + '!<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/**/*.type', + '!<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/**/*.styles', + '!<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/**/mocks/*', + '!<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/**/*.config', + '!<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/**/translations', + '!<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/**/types/*', + ], + setupFilesAfterEnv: [ + '<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/setup_test.ts', + ], +}; diff --git a/packages/kbn-securitysolution-exception-list-components/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/package.json new file mode 100644 index 000000000000..f9246cb9353d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/securitysolution-exception-list-components", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0", + "sideEffects": false +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/setup_test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/setup_test.ts new file mode 100644 index 000000000000..72e0edd0d07f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/setup_test.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. + */ + +// eslint-disable-next-line import/no-extraneous-dependencies +import '@testing-library/jest-dom'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/assets/images/illustration_product_no_results_magnifying_glass.svg b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/assets/images/illustration_product_no_results_magnifying_glass.svg similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/assets/images/illustration_product_no_results_magnifying_glass.svg rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/assets/images/illustration_product_no_results_magnifying_glass.svg diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/custom.d.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/custom.d.ts new file mode 100644 index 000000000000..18dda6a91003 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/custom.d.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. + */ + +declare module '*.svg' { + const content: string; + // eslint-disable-next-line import/no-default-export + export default content; +} diff --git a/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.test.tsx similarity index 93% rename from packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.test.tsx index 1b711185c526..7df3f732711a 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/empty_viewer_state.test.tsx @@ -1,10 +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", 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". + * 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'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/index.tsx new file mode 100644 index 000000000000..2e489cb5dca9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/empty_viewer_state/index.tsx @@ -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 React, { useMemo } from 'react'; +import type { FC } from 'react'; +import { css } from '@emotion/react'; +import { + EuiSkeletonText, + EuiImage, + EuiEmptyPrompt, + EuiButton, + useEuiTheme, + EuiPanel, +} from '@elastic/eui'; +import type { ExpressionColor } from '@elastic/eui/src/components/expression/expression'; +import type { EuiFacetGroupLayout } from '@elastic/eui/src/components/facet/facet_group'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { ListTypeText, ViewerStatus } from '../types'; +import * as i18n from '../translations'; +import illustration from '../assets/images/illustration_product_no_results_magnifying_glass.svg'; + +interface EmptyViewerStateProps { + title?: string; + body?: string; + buttonText?: string; + listType?: ListTypeText; + isReadOnly: boolean; + viewerStatus: ViewerStatus; + onEmptyButtonStateClick?: () => void | null; +} + +const panelCss = css` + margin: ${euiThemeVars.euiSizeL} 0; + padding: ${euiThemeVars.euiSizeL} 0; +`; +const EmptyViewerStateComponent: FC<EmptyViewerStateProps> = ({ + title, + body, + buttonText, + listType, + isReadOnly, + viewerStatus, + onEmptyButtonStateClick, +}) => { + const { euiTheme } = useEuiTheme(); + + const euiEmptyPromptProps = useMemo(() => { + switch (viewerStatus) { + case ViewerStatus.ERROR: { + return { + color: 'danger' as ExpressionColor, + iconType: 'error', + title: ( + <h2 data-test-subj="errorTitle">{title || i18n.EMPTY_VIEWER_STATE_ERROR_TITLE}</h2> + ), + body: <p data-test-subj="errorBody">{body || i18n.EMPTY_VIEWER_STATE_ERROR_BODY}</p>, + 'data-test-subj': 'errorViewerState', + }; + } + case ViewerStatus.EMPTY: + return { + color: 'subdued' as ExpressionColor, + iconType: 'plusInCircle', + iconColor: euiTheme.colors.darkestShade, + title: ( + <h2 data-test-subj="emptyTitle">{title || i18n.EMPTY_VIEWER_STATE_EMPTY_TITLE}</h2> + ), + body: <p data-test-subj="emptyBody">{body || i18n.EMPTY_VIEWER_STATE_EMPTY_BODY}</p>, + 'data-test-subj': 'emptyViewerState', + actions: [ + <EuiButton + data-test-subj="emptyStateButton" + onClick={onEmptyButtonStateClick} + iconType="plusInCircle" + color="primary" + isDisabled={isReadOnly} + fill + > + {buttonText || i18n.EMPTY_VIEWER_STATE_EMPTY_VIEWER_BUTTON(listType || 'rule')} + </EuiButton>, + ], + }; + case ViewerStatus.EMPTY_SEARCH: + return { + color: 'plain' as ExpressionColor, + layout: 'horizontal' as EuiFacetGroupLayout, + hasBorder: true, + hasShadow: false, + icon: <EuiImage size="fullWidth" alt="" src={illustration} />, + title: ( + <h3 data-test-subj="emptySearchTitle"> + {title || i18n.EMPTY_VIEWER_STATE_EMPTY_SEARCH_TITLE} + </h3> + ), + body: ( + <p data-test-subj="emptySearchBody"> + {body || i18n.EMPTY_VIEWER_STATE_EMPTY_SEARCH_BODY} + </p> + ), + 'data-test-subj': 'emptySearchViewerState', + }; + } + }, [ + viewerStatus, + euiTheme.colors.darkestShade, + title, + body, + onEmptyButtonStateClick, + isReadOnly, + buttonText, + listType, + ]); + + return ( + <EuiSkeletonText + lines={4} + data-test-subj="loadingViewerState" + isLoading={viewerStatus === ViewerStatus.LOADING || viewerStatus === ViewerStatus.SEARCHING} + > + <EuiPanel css={panelCss} color={viewerStatus === 'empty_search' ? 'subdued' : 'transparent'}> + <EuiEmptyPrompt {...euiEmptyPromptProps} /> + </EuiPanel> + </EuiSkeletonText> + ); +}; + +export const EmptyViewerState = React.memo(EmptyViewerStateComponent); + +EmptyViewerState.displayName = 'EmptyViewerState'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/__snapshots__/comments.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/__snapshots__/comments.test.tsx.snap similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/__snapshots__/comments.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/__snapshots__/comments.test.tsx.snap diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.test.tsx similarity index 83% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.test.tsx index c8c8e92cd6c7..caf989efcc9e 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/comments.test.tsx @@ -1,10 +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", 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". + * 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'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/index.tsx new file mode 100644 index 000000000000..76ef48fba0a9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/index.tsx @@ -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 React, { memo } from 'react'; +import type { EuiCommentProps } from '@elastic/eui'; +import { EuiAccordion, EuiCommentList, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import * as i18n from '../translations'; + +const accordionCss = css` + color: ${euiThemeVars.euiColorPrimary}; +`; + +export interface ExceptionItemCardCommentsProps { + comments: EuiCommentProps[]; + dataTestSubj?: string; +} + +export const ExceptionItemCardComments = memo<ExceptionItemCardCommentsProps>( + ({ comments, dataTestSubj }) => { + if (!comments.length) return null; + return ( + <EuiFlexItem data-test-subj={dataTestSubj}> + <EuiAccordion + id="exceptionItemCardComments" + buttonContent={ + <EuiText size="s" css={accordionCss} data-test-subj={`${dataTestSubj || ''}TextButton`}> + {i18n.exceptionItemCardCommentsAccordion(comments.length)} + </EuiText> + } + arrowDisplay="none" + data-test-subj="exceptionItemCardComments" + > + <EuiPanel data-test-subj="accordionContentPanel" hasBorder hasShadow paddingSize="m"> + <EuiCommentList data-test-subj="accordionCommentList" comments={comments} /> + </EuiPanel> + </EuiAccordion> + </EuiFlexItem> + ); + } +); + +ExceptionItemCardComments.displayName = 'ExceptionItemCardComments'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.config.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.config.ts similarity index 77% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.config.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.config.ts index fdb545dc2c03..0c826f40f022 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.config.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.config.ts @@ -1,10 +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", 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". + * 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 { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; 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 new file mode 100644 index 000000000000..043e2e2e4485 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { cx } from '@emotion/css'; +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 borderCss = cx( + 'eui-xScroll', + ` + border: 1px; + border-color: #d3dae6; + border-style: solid; +` +); + +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/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.test.tsx similarity index 96% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.test.tsx index e19327226de2..6cd3ee06d203 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.test.tsx @@ -1,10 +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", 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". + * 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 } from '@testing-library/react'; diff --git a/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 similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/__snapshots__/entry_content.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/__snapshots__/entry_content.test.tsx.snap diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.test.tsx similarity index 93% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.test.tsx index e49d18ec78c4..07de278ace7b 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.test.tsx @@ -1,10 +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", 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". + * 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 { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.tsx similarity index 85% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.tsx index 2fa113e5a612..aeca92d4d003 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.helper.tsx @@ -1,10 +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", 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". + * 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, { ElementType } from 'react'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.test.tsx similarity index 83% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.test.tsx index 7eb141493ef4..bed784f46b87 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/entry_content.test.tsx @@ -1,10 +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", 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". + * 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'; 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 new file mode 100644 index 000000000000..7f75694b16d3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/index.tsx @@ -0,0 +1,81 @@ +/* + * 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, { ElementType, FC, memo } from 'react'; +import { EuiExpression, EuiToken, EuiFlexGroup } from '@elastic/eui'; +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'; + +interface EntryContentProps { + entry: Entry; + index: number; + isNestedEntry?: boolean; + dataTestSubj?: string; + showValueListModal: ElementType; +} + +export const EntryContent: FC<EntryContentProps> = memo( + ({ entry, index, isNestedEntry = false, dataTestSubj, showValueListModal }) => { + const { field, type } = entry; + const value = getValue(entry); + const operator = 'operator' in entry ? entry.operator : ''; + + const entryKey = `${field}${type}${value}${index}`; + return ( + <div data-test-subj={`${dataTestSubj || ''}${entryKey}EntryContent`} key={entryKey}> + <div css={expressionContainerCss}> + {isNestedEntry ? ( + <EuiFlexGroup + responsive + css={nestedGroupSpaceCss} + direction="row" + alignItems="center" + gutterSize="m" + data-test-subj={`${dataTestSubj || ''}NestedEntry`} + > + <EuiToken data-test-subj="nstedEntryIcon" iconType="tokenNested" size="s" /> + + <div css={valueContainerCss}> + <EuiExpression description="" value={field} color="subdued" /> + {getValueExpression( + type as ListOperatorTypeEnum, + operator, + value, + showValueListModal + )} + </div> + </EuiFlexGroup> + ) : ( + <> + <EuiExpression + description={index === 0 ? '' : i18n.CONDITION_AND} + value={field} + color={index === 0 ? 'primary' : 'subdued'} + data-test-subj={`${dataTestSubj || ''}SingleEntry`} + /> + + {getValueExpression( + type as ListOperatorTypeEnum, + operator, + value, + showValueListModal + )} + </> + )} + </div> + </div> + ); + } +); +EntryContent.displayName = 'EntryContent'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/index.tsx new file mode 100644 index 000000000000..804af918e4f1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/index.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { EuiPanel } from '@elastic/eui'; + +import { borderCss } from './conditions.styles'; +import { EntryContent } from './entry_content'; +import { OsCondition } from './os_conditions'; +import type { CriteriaConditionsProps, Entry } from './types'; + +export const ExceptionItemCardConditions = memo<CriteriaConditionsProps>( + ({ os, entries, dataTestSubj, showValueListModal }) => { + return ( + <EuiPanel + color="subdued" + hasBorder={true} + hasShadow={false} + data-test-subj={dataTestSubj} + className={borderCss} + > + {os?.length ? <OsCondition os={os} dataTestSubj={dataTestSubj} /> : null} + {entries.map((entry: Entry, index: number) => { + const nestedEntries = 'entries' in entry ? entry.entries : []; + return ( + <div key={`ExceptionItemCardConditionsContainer${index}`}> + <EntryContent + key={`entry${index}`} + entry={entry} + index={index} + dataTestSubj={dataTestSubj} + showValueListModal={showValueListModal} + /> + {nestedEntries?.length + ? nestedEntries.map((nestedEntry: Entry, nestedIndex: number) => ( + <EntryContent + key={`nestedEntry${index}${nestedIndex}`} + entry={nestedEntry} + index={nestedIndex} + isNestedEntry={true} + dataTestSubj={dataTestSubj} + showValueListModal={showValueListModal} + /> + )) + : null} + </div> + ); + })} + </EuiPanel> + ); + } +); +ExceptionItemCardConditions.displayName = 'ExceptionItemCardConditions'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/__snapshots__/os_conditions.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/__snapshots__/os_conditions.test.tsx.snap similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/__snapshots__/os_conditions.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/__snapshots__/os_conditions.test.tsx.snap diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/index.tsx new file mode 100644 index 000000000000..3a2dded6ce2b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/index.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useMemo } from 'react'; +import { EuiExpression } from '@elastic/eui'; + +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { OS_LABELS } from '../conditions.config'; +import * as i18n from '../../translations'; + +export interface OsConditionsProps { + dataTestSubj?: string; + os: ExceptionListItemSchema['os_types']; +} + +export const OsCondition = memo<OsConditionsProps>(({ os, dataTestSubj }) => { + const osLabel = useMemo(() => { + return os.map((osValue) => OS_LABELS[osValue] ?? osValue).join(', '); + }, [os]); + return osLabel ? ( + <div data-test-subj={`${dataTestSubj || ''}Os`}> + <strong> + <EuiExpression data-test-subj="osLabel" description="" value={i18n.CONDITION_OS} /> + <EuiExpression + data-test-subj="osValue" + description={i18n.CONDITION_OPERATOR_TYPE_MATCH} + value={osLabel} + /> + </strong> + </div> + ) : null; +}); +OsCondition.displayName = 'OsCondition'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/os_conditions.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/os_conditions.test.tsx similarity index 82% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/os_conditions.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/os_conditions.test.tsx index 3aea8d5ed006..a3575c184522 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/os_conditions.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/os_conditions/os_conditions.test.tsx @@ -1,10 +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", 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". + * 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'; @@ -35,6 +33,7 @@ describe('OsCondition', () => { expect(wrapper.container).toMatchSnapshot(); }); it('should return any os sent', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const wrapper = render(<OsCondition os={['MacPro' as any]} dataTestSubj="OsConditionMac" />); expect(wrapper.getByTestId('osLabel')).toHaveTextContent(i18n.CONDITION_OS); expect(wrapper.getByTestId('osValue')).toHaveTextContent( diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/types.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/types.ts new file mode 100644 index 000000000000..a69a0f16c1d7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/types.ts @@ -0,0 +1,33 @@ +/* + * 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 { + EntryExists, + EntryList, + EntryMatch, + EntryMatchAny, + EntryMatchWildcard, + EntryNested, + ExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { ElementType } from 'react'; + +export type Entry = + | EntryExists + | EntryList + | EntryMatch + | EntryMatchAny + | EntryMatchWildcard + | EntryNested; + +export type Entries = ExceptionListItemSchema['entries']; +export interface CriteriaConditionsProps { + entries: Entries; + dataTestSubj: string; + os?: ExceptionListItemSchema['os_types']; + showValueListModal: ElementType; +} diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.test.tsx similarity index 94% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.test.tsx index aeaf5755ea1d..b3f06eddec71 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.test.tsx @@ -1,10 +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", 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". + * 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'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx similarity index 91% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx index 44456a130687..52d2f730b33b 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx @@ -1,10 +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", 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". + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. */ import React, { FC, ElementType } from 'react'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx similarity index 81% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx index 5de4f5d729f0..9127589b5b56 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/header.test.tsx @@ -1,10 +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", 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". + * 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'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/index.tsx new file mode 100644 index 000000000000..88eb6233a935 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/header/index.tsx @@ -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 React, { memo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; +import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { HeaderMenu } from '../../header_menu'; + +export interface ExceptionItemCardHeaderProps { + item: ExceptionListItemSchema; + actions: Array<{ key: string; icon: string; label: string | boolean; onClick: () => void }>; + disableActions?: boolean; + dataTestSubj: string; +} + +export const ExceptionItemCardHeader = memo<ExceptionItemCardHeaderProps>( + ({ item, actions, disableActions = false, dataTestSubj }) => { + return ( + <EuiFlexGroup responsive data-test-subj={dataTestSubj} justifyContent="spaceBetween"> + <EuiFlexItem grow={9}> + <EuiTitle size="xs" textTransform="uppercase" data-test-subj={`${dataTestSubj}Title`}> + <h3>{item.name}</h3> + </EuiTitle> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <HeaderMenu + iconType="boxesHorizontal" + disableActions={disableActions} + actions={actions} + aria-label="Exception item actions menu" + dataTestSubj={dataTestSubj} + anchorPosition="downCenter" + /> + </EuiFlexItem> + </EuiFlexGroup> + ); + } +); + +ExceptionItemCardHeader.displayName = 'ExceptionItemCardHeader'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/index.ts new file mode 100644 index 000000000000..2d2b38d288da --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/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 * from './conditions'; +export * from './header'; +export * from './meta'; +export * from './comments'; +export * from './exception_item_card'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/__snapshots__/details_info.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/__snapshots__/details_info.test.tsx.snap similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/__snapshots__/details_info.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/__snapshots__/details_info.test.tsx.snap diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/details_info.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/details_info.test.tsx new file mode 100644 index 000000000000..3950d674a1ad --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/details_info.test.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { MetaInfoDetails } from '.'; + +describe('MetaInfoDetails', () => { + it('should render lastUpdate as string', () => { + const wrapper = render( + <MetaInfoDetails + dataTestSubj="MetaInfoDetails" + label="created_by" + lastUpdate="last update" + lastUpdateValue="value" + /> + ); + expect(wrapper.container).toMatchSnapshot(); + expect(wrapper.getByTestId('MetaInfoDetailslastUpdate')).toHaveTextContent('last update'); + }); + it('should render lastUpdate as JSX Element', () => { + const wrapper = render( + <MetaInfoDetails + dataTestSubj="MetaInfoDetails" + label="created_by" + // eslint-disable-next-line react/jsx-no-literals + lastUpdate={<p>Last update value</p>} + lastUpdateValue="value" + /> + ); + expect(wrapper.container).toMatchSnapshot(); + expect(wrapper.getByTestId('MetaInfoDetailslastUpdate')).toHaveTextContent('Last update value'); + }); +}); 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 new file mode 100644 index 000000000000..209ce3b59553 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/index.tsx @@ -0,0 +1,67 @@ +/* + * 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, { memo } from 'react'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import * as i18n from '../../translations'; + +interface MetaInfoDetailsProps { + label: string; + lastUpdate: JSX.Element | string; + lastUpdateValue?: string; + dataTestSubj?: string; +} + +const euiBadgeFontFamily = css` + font-family: ${euiThemeVars.euiFontFamily}; +`; +export const MetaInfoDetails = memo<MetaInfoDetailsProps>( + ({ label, lastUpdate, lastUpdateValue, dataTestSubj }) => { + return ( + <EuiFlexGroup + data-test-subj={`${dataTestSubj || ''}metaInfoDetails`} + alignItems="center" + gutterSize="s" + wrap + responsive + > + <EuiFlexItem grow={false}> + <EuiText size="xs" css={euiBadgeFontFamily}> + {label} + </EuiText> + </EuiFlexItem> + <EuiFlexItem grow={false} data-test-subj={`${dataTestSubj || ''}lastUpdate`}> + <EuiBadge color="default" css={euiBadgeFontFamily}> + {lastUpdate} + </EuiBadge> + </EuiFlexItem> + {lastUpdateValue != null && ( + <> + <EuiFlexItem grow={false}> + <EuiText size="xs" css={euiBadgeFontFamily}> + {i18n.EXCEPTION_ITEM_CARD_META_BY} + </EuiText> + </EuiFlexItem> + <EuiFlexItem grow={false} data-test-subj={`${dataTestSubj || ''}lastUpdateValue`}> + <EuiFlexGroup responsive gutterSize="xs" alignItems="center"> + <EuiFlexItem grow={false}> + <EuiBadge color="hollow" css={euiBadgeFontFamily}> + {lastUpdateValue} + </EuiBadge> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlexItem> + </> + )} + </EuiFlexGroup> + ); + } +); + +MetaInfoDetails.displayName = 'MetaInfoDetails'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx new file mode 100644 index 000000000000..8c7734ab51c9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx @@ -0,0 +1,123 @@ +/* + * 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, { memo, useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import * as i18n from '../translations'; +import type { Rule } from '../../types'; +import { MetaInfoDetails } from './details_info'; +import { HeaderMenu } from '../../header_menu'; +import { generateLinkedRulesMenuItems } from '../../generate_linked_rules_menu_item'; + +const itemCss = css` + border-right: 1px solid #d3dae6; + padding: ${euiThemeVars.euiSizeS} ${euiThemeVars.euiSizeM} ${euiThemeVars.euiSizeS} 0; +`; + +export interface ExceptionItemCardMetaInfoProps { + item: ExceptionListItemSchema; + rules: Rule[]; + dataTestSubj: string; + formattedDateComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + securityLinkAnchorComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common +} + +export const ExceptionItemCardMetaInfo = memo<ExceptionItemCardMetaInfoProps>( + ({ item, rules, dataTestSubj, securityLinkAnchorComponent, formattedDateComponent }) => { + const FormattedDateComponent = formattedDateComponent; + + const referencedLinks = useMemo( + () => + generateLinkedRulesMenuItems({ + dataTestSubj, + linkedRules: rules, + securityLinkAnchorComponent, + }), + [dataTestSubj, rules, securityLinkAnchorComponent] + ); + + const isExpired = useMemo( + () => (item.expire_time ? new Date(item.expire_time) <= new Date() : false), + [item] + ); + + return ( + <EuiFlexGroup alignItems="center" responsive gutterSize="s" data-test-subj={dataTestSubj}> + {FormattedDateComponent !== null && ( + <> + <EuiFlexItem css={itemCss} grow={false}> + <MetaInfoDetails + label={i18n.EXCEPTION_ITEM_CARD_CREATED_LABEL} + lastUpdate={ + <FormattedDateComponent + data-test-subj={`{dataTestSubj||''}formattedDateComponentCreatedBy`} + fieldName="created_at" + value={item.created_at} + /> + } + lastUpdateValue={item.created_by} + dataTestSubj={`${dataTestSubj || ''}CreatedBy`} + /> + </EuiFlexItem> + + <EuiFlexItem css={itemCss} grow={false}> + <MetaInfoDetails + label={i18n.EXCEPTION_ITEM_CARD_UPDATED_LABEL} + lastUpdate={ + <FormattedDateComponent + data-test-subj={`{dataTestSubj||''}formattedDateComponentUpdatedBy`} + fieldName="updated_at" + value={item.updated_at} + /> + } + lastUpdateValue={item.updated_by} + dataTestSubj={`${dataTestSubj || ''}UpdatedBy`} + /> + </EuiFlexItem> + {item.expire_time != null && ( + <> + <EuiFlexItem css={itemCss} grow={false}> + <MetaInfoDetails + label={ + isExpired + ? i18n.EXCEPTION_ITEM_CARD_EXPIRED_LABEL + : i18n.EXCEPTION_ITEM_CARD_EXPIRES_LABEL + } + lastUpdate={ + <FormattedDateComponent + data-test-subj={`{dataTestSubj||''}formattedDateComponentExpireTime`} + fieldName="expire_time" + value={item.expire_time} + /> + } + dataTestSubj={`${dataTestSubj || ''}ExpireTime`} + /> + </EuiFlexItem> + </> + )} + </> + )} + <EuiFlexItem> + <HeaderMenu + emptyButton + useCustomActions + iconType="list" + actions={referencedLinks} + disableActions={false} + text={i18n.AFFECTED_RULES(rules.length)} + dataTestSubj={dataTestSubj} + /> + </EuiFlexItem> + </EuiFlexGroup> + ); + } +); +ExceptionItemCardMetaInfo.displayName = 'ExceptionItemCardMetaInfo'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx similarity index 90% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx index 10570b263781..f7a94d9d76ff 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/meta.test.tsx @@ -1,10 +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", 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". + * 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'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/translations.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/translations.ts new file mode 100644 index 000000000000..9b1a64bd6cf4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/translations.ts @@ -0,0 +1,179 @@ +/* + * 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'; + +export const exceptionItemCardEditButton = (listType: string) => + i18n.translate('exceptionList-components.exceptions.exceptionItem.card.editItemButton', { + values: { listType }, + defaultMessage: 'Edit {listType} exception', + }); + +export const exceptionItemCardDeleteButton = (listType: string) => + i18n.translate('exceptionList-components.exceptions.exceptionItem.card.deleteItemButton', { + values: { listType }, + defaultMessage: 'Delete {listType} exception', + }); + +export const EXCEPTION_ITEM_CARD_CREATED_LABEL = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.createdLabel', + { + defaultMessage: 'Created', + } +); + +export const EXCEPTION_ITEM_CARD_UPDATED_LABEL = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.updatedLabel', + { + defaultMessage: 'Updated', + } +); + +export const EXCEPTION_ITEM_CARD_EXPIRES_LABEL = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.expiresLabel', + { + defaultMessage: 'Expires at', + } +); + +export const EXCEPTION_ITEM_CARD_EXPIRED_LABEL = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.expiredLabel', + { + defaultMessage: 'Expired at', + } +); + +export const EXCEPTION_ITEM_CARD_META_BY = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.metaDetailsBy', + { + defaultMessage: 'by', + } +); + +export const exceptionItemCardCommentsAccordion = (comments: number) => + i18n.translate('exceptionList-components.exceptions.exceptionItem.card.showCommentsLabel', { + values: { comments }, + defaultMessage: 'Show {comments, plural, =1 {comment} other {comments}} ({comments})', + }); + +export const CONDITION_OPERATOR_TYPE_MATCH = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchOperator', + { + defaultMessage: 'IS', + } +); + +export const CONDITION_OPERATOR_TYPE_NOT_MATCH = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchOperator.not', + { + defaultMessage: 'IS NOT', + } +); + +export const CONDITION_OPERATOR_TYPE_WILDCARD_MATCHES = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.wildcardMatchesOperator', + { + defaultMessage: 'MATCHES', + } +); + +export const CONDITION_OPERATOR_TYPE_WILDCARD_DOES_NOT_MATCH = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.wildcardDoesNotMatchOperator', + { + defaultMessage: 'DOES NOT MATCH', + } +); + +export const CONDITION_OPERATOR_TYPE_NESTED = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.nestedOperator', + { + defaultMessage: 'has', + } +); + +export const CONDITION_OPERATOR_TYPE_MATCH_ANY = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchAnyOperator', + { + defaultMessage: 'is one of', + } +); + +export const CONDITION_OPERATOR_TYPE_NOT_MATCH_ANY = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.matchAnyOperator.not', + { + defaultMessage: 'is not one of', + } +); + +export const CONDITION_OPERATOR_TYPE_EXISTS = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.existsOperator', + { + defaultMessage: 'exists', + } +); + +export const CONDITION_OPERATOR_TYPE_DOES_NOT_EXIST = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.existsOperator.not', + { + defaultMessage: 'does not exist', + } +); + +export const CONDITION_OPERATOR_TYPE_LIST = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.listOperator', + { + defaultMessage: 'included in', + } +); + +export const CONDITION_OPERATOR_TYPE_NOT_IN_LIST = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.listOperator.not', + { + defaultMessage: 'is not included in', + } +); + +export const CONDITION_AND = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.and', + { + defaultMessage: 'AND', + } +); + +export const CONDITION_OS = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.os', + { + defaultMessage: 'OS', + } +); + +export const OS_WINDOWS = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.windows', + { + defaultMessage: 'Windows', + } +); + +export const OS_LINUX = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.linux', + { + defaultMessage: 'Linux', + } +); + +export const OS_MAC = i18n.translate( + 'exceptionList-components.exceptions.exceptionItem.card.conditions.macos', + { + defaultMessage: 'Mac', + } +); + +export const AFFECTED_RULES = (numRules: number) => + i18n.translate('exceptionList-components.exceptions.card.exceptionItem.affectedRules', { + values: { numRules }, + defaultMessage: 'Affects {numRules} {numRules, plural, =1 {rule} other {rules}}', + }); diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.test.ts similarity index 90% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.test.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.test.ts index 5c04c8772714..1a144182eeed 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.test.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.test.ts @@ -1,10 +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", 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". + * 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 { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.ts similarity index 86% rename from packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.ts index 2978dfb12c72..f3da4ec92595 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/use_exception_item_card.ts @@ -1,10 +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", 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". + * 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 { useCallback, useMemo } from 'react'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx similarity index 96% rename from packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx index e60cff40626f..936a58b670f5 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_items/exception_items.test.tsx @@ -1,10 +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", 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". + * 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'; 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 new file mode 100644 index 000000000000..6fde321cfcb9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_items/index.tsx @@ -0,0 +1,153 @@ +/* + * 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, { ElementType } from 'react'; +import { css } from '@emotion/react'; +import type { FC } from 'react'; +import { EuiCommentProps, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +import type { + CommentsArray, + ExceptionListItemSchema, + ExceptionListTypeEnum, +} from '@kbn/securitysolution-io-ts-list-types'; + +import { euiThemeVars } from '@kbn/ui-theme'; +import { EmptyViewerState, ExceptionItemCard, Pagination, PaginationProps } from '../..'; + +import type { + RuleReferences, + ExceptionListItemIdentifiers, + ViewerStatus, + 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; + isReadOnly: boolean; + emptyViewerTitle?: string; + emptyViewerBody?: string; + emptyViewerButtonText?: string; + exceptions: ExceptionListItemSchema[]; + listType: ExceptionListTypeEnum; + ruleReferences: RuleReferences; + pagination: PaginationProps['pagination']; + editActionLabel?: string; + deleteActionLabel?: string; + dataTestSubj?: string; + securityLinkAnchorComponent: ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + formattedDateComponent: ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + exceptionsUtilityComponent: ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + getFormattedComments: (comments: CommentsArray) => EuiCommentProps[]; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + onCreateExceptionListItem?: () => void; + onDeleteException: (arg: ExceptionListItemIdentifiers) => void; + onEditExceptionItem: (item: ExceptionListItemSchema) => void; + onPaginationChange: (arg: GetExceptionItemProps) => void; + showValueListModal: ElementType; +} + +const ExceptionItemsComponent: FC<ExceptionItemsProps> = ({ + lastUpdated, + viewerStatus, + isReadOnly, + exceptions, + listType, + ruleReferences, + emptyViewerTitle, + emptyViewerBody, + emptyViewerButtonText, + pagination, + dataTestSubj, + editActionLabel, + deleteActionLabel, + securityLinkAnchorComponent, + exceptionsUtilityComponent, + formattedDateComponent, + getFormattedComments, + onPaginationChange, + onDeleteException, + onEditExceptionItem, + onCreateExceptionListItem, + showValueListModal, +}) => { + const ExceptionsUtility = exceptionsUtilityComponent; + if (!exceptions.length || viewerStatus) + return ( + <EmptyViewerState + isReadOnly={isReadOnly} + title={emptyViewerTitle} + viewerStatus={viewerStatus} + buttonText={emptyViewerButtonText} + body={emptyViewerBody} + onEmptyButtonStateClick={onCreateExceptionListItem} + /> + ); + const ShowValueListModal = showValueListModal; + return ( + <> + <ExceptionsUtility pagination={pagination} lastUpdated={lastUpdated} /> + <EuiFlexGroup direction="column" gutterSize="none" className="eui-yScrollWithShadows"> + <EuiFlexItem grow={false}> + <EuiFlexGroup + css={exceptionItemCss} + data-test-subj={`${dataTestSubj || ''}exceptionsContainer`} + direction="column" + gutterSize="s" + > + {exceptions.map((exception) => ( + <EuiFlexItem + data-test-subj={`${dataTestSubj || ''}exceptionItemContainer`} + grow={false} + key={exception.id} + > + <ExceptionItemCard + key={`${exception.id}exceptionItemCardKey`} + dataTestSubj={`${dataTestSubj || ''}exceptionItemCard`} + disableActions={isReadOnly} + exceptionItem={exception} + listType={listType} + ruleReferences={ + Object.keys(ruleReferences).length && ruleReferences[exception.list_id] + ? ruleReferences[exception.list_id].referenced_rules + : [] + } + editActionLabel={editActionLabel} + deleteActionLabel={deleteActionLabel} + onDeleteException={onDeleteException} + onEditException={onEditExceptionItem} + securityLinkAnchorComponent={securityLinkAnchorComponent} + formattedDateComponent={formattedDateComponent} + getFormattedComments={getFormattedComments} + showValueListModal={ShowValueListModal} + /> + </EuiFlexItem> + ))} + </EuiFlexGroup> + </EuiFlexItem> + </EuiFlexGroup> + <Pagination + dataTestSubj={`${dataTestSubj || ''}pagination`} + pagination={pagination} + onPaginationChange={onPaginationChange} + /> + </> + ); +}; + +ExceptionItemsComponent.displayName = 'ExceptionItemsComponent'; + +export const ExceptionItems = React.memo(ExceptionItemsComponent); + +ExceptionItems.displayName = 'ExceptionsItems'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/__snapshots__/generate_linked_rules_menu_item.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/__snapshots__/generate_linked_rules_menu_item.test.tsx.snap similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/__snapshots__/generate_linked_rules_menu_item.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/__snapshots__/generate_linked_rules_menu_item.test.tsx.snap diff --git a/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/generate_linked_rules_menu_item.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/generate_linked_rules_menu_item.test.tsx similarity index 80% rename from packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/generate_linked_rules_menu_item.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/generate_linked_rules_menu_item.test.tsx index 01f708a0f6bd..2bf26f551fb0 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/generate_linked_rules_menu_item.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/generate_linked_rules_menu_item.test.tsx @@ -1,15 +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", 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". + * 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 } from '@testing-library/react'; -import { ReactElement } from 'react'; -import { ElementType } from 'react'; +import { ReactElement, ElementType } from 'react'; import { generateLinkedRulesMenuItems } from '.'; import { rules } from '../mocks/rule_references.mock'; import { @@ -45,7 +42,7 @@ describe('generateLinedRulesMenuItems', () => { leftIcon: 'check', }) as ReactElement[]; - result.map((link) => { + result.forEach((link) => { const wrapper = render(link); expect(wrapper.container).toMatchSnapshot(); expect(wrapper.getByTestId('generateLinedRulesMenuItemsTestActionItem1a2b3c')); 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 new file mode 100644 index 000000000000..9187d0975e92 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/index.tsx @@ -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 React, { ElementType, ReactElement } from 'react'; +import { EuiContextMenuItem, EuiFlexGroup, EuiFlexItem, EuiIcon, IconType } from '@elastic/eui'; +import { Rule } from '../types'; +import { itemContentCss, containerCss } from './menu_link.styles'; + +interface MenuItemLinkedRulesProps { + leftIcon?: IconType; + dataTestSubj?: string; + linkedRules: Rule[]; + securityLinkAnchorComponent: ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common +} + +export const generateLinkedRulesMenuItems = ({ + dataTestSubj, + linkedRules, + securityLinkAnchorComponent, + leftIcon = '', +}: MenuItemLinkedRulesProps): ReactElement[] | null => { + if (!linkedRules.length || securityLinkAnchorComponent === null) return null; + + const SecurityLinkAnchor = securityLinkAnchorComponent; + return linkedRules.map((rule) => { + return ( + <EuiContextMenuItem + css={linkedRules.length > 1 ? containerCss : ''} + data-test-subj={`${dataTestSubj || ''}ActionItem${rule.id}`} + key={rule.id} + > + <EuiFlexGroup gutterSize="s" css={itemContentCss}> + {leftIcon ? ( + <EuiFlexItem data-test-subj={`${dataTestSubj || ''}LeftIcon`} grow={false}> + <EuiIcon type={leftIcon} /> + </EuiFlexItem> + ) : null} + <EuiFlexItem css={itemContentCss}> + <SecurityLinkAnchor external referenceName={rule.name} referenceId={rule.id} /> + </EuiFlexItem> + </EuiFlexGroup> + </EuiContextMenuItem> + ); + }); +}; 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 new file mode 100644 index 000000000000..886369b22e23 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/menu_link.styles.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +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/packages/kbn-securitysolution-exception-list-components/src/header_menu/__snapshots__/header_menu.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/header_menu/__snapshots__/header_menu.test.tsx.snap similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/header_menu/__snapshots__/header_menu.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/header_menu/__snapshots__/header_menu.test.tsx.snap diff --git a/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 similarity index 93% rename from packages/kbn-securitysolution-exception-list-components/src/header_menu/header_menu.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/header_menu/header_menu.test.tsx index 7e14b503721b..2869b16e3aaa 100644 --- a/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 @@ -1,10 +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", 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". + * 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 { createEvent, fireEvent, render } from '@testing-library/react'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/header_menu/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/header_menu/index.tsx new file mode 100644 index 000000000000..bd634b5f6c36 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/header_menu/index.tsx @@ -0,0 +1,137 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, ReactElement, useMemo, useState } from 'react'; +import { + EuiButtonEmpty, + EuiButtonEmptyProps, + EuiButtonIcon, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiPopover, + PanelPaddingSize, + PopoverAnchorPosition, +} from '@elastic/eui'; + +import { css } from '@emotion/react'; + +export interface Action { + key: string; + icon: string; + label: string | boolean; + disabled?: boolean; + onClick: (e: React.MouseEvent<Element, MouseEvent>) => void; +} + +interface HeaderMenuComponentProps { + disableActions: boolean; + actions: Action[] | ReactElement[] | null; + text?: string; + iconType?: EuiButtonEmptyProps['iconType']; + iconSide?: EuiButtonEmptyProps['iconSide']; + dataTestSubj?: string; + emptyButton?: boolean; + useCustomActions?: boolean; + anchorPosition?: PopoverAnchorPosition; + panelPaddingSize?: PanelPaddingSize; +} + +const popoverHeightStyle = css` + max-height: 300px; + height: 100%; + overflow-x: hidden; + overflow-y: auto; +`; +const HeaderMenuComponent: FC<HeaderMenuComponentProps> = ({ + text, + dataTestSubj, + actions, + disableActions, + emptyButton, + useCustomActions, + iconType, + iconSide = 'left', + anchorPosition = 'downCenter', + panelPaddingSize = 's', +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const onAffectedRulesClick = () => setIsPopoverOpen((isOpen) => !isOpen); + const onClosePopover = () => setIsPopoverOpen(false); + + const itemActions = useMemo(() => { + if (useCustomActions || actions === null) return actions; + return (actions as Action[]).map((action) => ( + <EuiContextMenuItem + data-test-subj={`${dataTestSubj || ''}ActionItem${action.key}`} + key={action.key} + icon={action.icon} + disabled={action.disabled} + layoutAlign="center" + onClick={(e) => { + onClosePopover(); + if (typeof action.onClick === 'function') action.onClick(e); + }} + > + {action.label} + </EuiContextMenuItem> + )); + }, [actions, dataTestSubj, useCustomActions]); + + return ( + <EuiFlexGroup responsive> + <EuiPopover + button={ + emptyButton ? ( + <EuiButtonEmpty + isDisabled={disableActions} + onClick={onAffectedRulesClick} + iconType={iconType ? iconType : undefined} + iconSide={iconSide} + data-test-subj={`${dataTestSubj || ''}EmptyButton`} + aria-label="Header menu Button Empty" + > + {text} + </EuiButtonEmpty> + ) : ( + <EuiButtonIcon + isDisabled={disableActions} + onClick={onAffectedRulesClick} + iconType={iconType ? iconType : 'boxesHorizontal'} + data-test-subj={`${dataTestSubj || ''}ButtonIcon`} + aria-label="Header menu Button Icon" + > + {text} + </EuiButtonIcon> + ) + } + onClick={(e) => e.stopPropagation()} + panelPaddingSize={panelPaddingSize} + isOpen={isPopoverOpen} + closePopover={onClosePopover} + anchorPosition={anchorPosition} + data-test-subj={`${dataTestSubj || ''}Items`} + > + {!itemActions ? null : ( + <EuiContextMenuPanel + css={popoverHeightStyle} + className="eui-scrollBar" + data-test-subj={`${dataTestSubj || ''}MenuPanel`} + size="s" + items={itemActions as ReactElement[]} + /> + )} + </EuiPopover> + </EuiFlexGroup> + ); +}; +HeaderMenuComponent.displayName = 'HeaderMenuComponent'; + +export const HeaderMenu = React.memo(HeaderMenuComponent); + +HeaderMenu.displayName = 'HeaderMenu'; diff --git a/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 similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/__snapshots__/edit_modal.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/__snapshots__/edit_modal.test.tsx.snap similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/__snapshots__/edit_modal.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/__snapshots__/edit_modal.test.tsx.snap diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/edit_modal.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/edit_modal.test.tsx similarity index 93% rename from packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/edit_modal.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/edit_modal.test.tsx index 39692e35394e..9b23afaf2089 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/edit_modal.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/edit_modal.test.tsx @@ -1,10 +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", 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". + * 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'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/index.tsx new file mode 100644 index 000000000000..fda87a768fd0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/index.tsx @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiFieldText, + EuiForm, + EuiFormRow, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiTextArea, + EuiProgress, +} from '@elastic/eui'; +import * as i18n from '../../translations'; +import { ListDetails } from '../../types'; +import { useEditModal } from './use_edit_modal'; + +interface EditModalProps { + listDetails: ListDetails; + onSave: (newListDetails: ListDetails) => void; + onCancel: () => void; +} + +const EditModalComponent: FC<EditModalProps> = ({ listDetails, onSave, onCancel }) => { + const { error, modalFormId, newListDetails, showProgress, onBlur, onSubmit, onChange } = + useEditModal({ + listDetails, + onSave, + }); + return ( + <EuiModal data-test-subj="EditModal" onClose={onCancel} initialFocus="[name=popswitch]"> + {showProgress && ( + <EuiProgress data-test-subj="editModalProgess" size="xs" position="absolute" /> + )} + <EuiModalHeader> + <EuiModalHeaderTitle data-test-subj="editModalTitle"> + {i18n.EXCEPTION_LIST_HEADER_EDIT_MODAL_TITLE(listDetails.name)} + </EuiModalHeaderTitle> + </EuiModalHeader> + + <EuiModalBody> + <EuiForm + id={modalFormId} + data-test-subj="editModalForm" + component="form" + onSubmit={onSubmit} + > + <EuiFormRow + error={error} + isInvalid={!!error} + fullWidth + label={i18n.EXCEPTION_LIST_HEADER_NAME_TEXTBOX} + > + <EuiFieldText + fullWidth + isInvalid={!!error} + onBlur={onBlur} + data-test-subj="editModalNameTextField" + name="name" + value={newListDetails.name} + onChange={onChange} + /> + </EuiFormRow> + + <EuiFormRow fullWidth label={i18n.EXCEPTION_LIST_HEADER_DESCRIPTION_TEXTBOX}> + <EuiTextArea + fullWidth + data-test-subj="editModalDescriptionTextField" + name="description" + value={newListDetails.description} + onChange={onChange} + onBlur={onBlur} + /> + </EuiFormRow> + </EuiForm> + </EuiModalBody> + + <EuiModalFooter> + <EuiButtonEmpty data-test-subj="editModalCancelBtn" onClick={onCancel}> + {i18n.EXCEPTION_LIST_HEADER_EDIT_MODAL_CANCEL_BUTTON} + </EuiButtonEmpty> + + <EuiButton + data-test-subj="editModalSaveBtn" + type="submit" + form={modalFormId} + onClick={onSubmit} + fill + > + {i18n.EXCEPTION_LIST_HEADER_EDIT_MODAL_SAVE_BUTTON} + </EuiButton> + </EuiModalFooter> + </EuiModal> + ); +}; +EditModalComponent.displayName = 'EditModalComponent'; + +export const EditModal = React.memo(EditModalComponent); + +EditModal.displayName = 'EditModal'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.test.ts similarity index 81% rename from packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.test.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.test.ts index 65b532a55c83..a540fdd7704d 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.test.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.test.ts @@ -1,10 +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", 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". + * 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 { ChangeEvent, SyntheticEvent } from 'react'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.ts similarity index 80% rename from packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.ts index 6977b8467b94..38c60328ef3d 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/use_edit_modal.ts @@ -1,10 +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", 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". + * 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 { useGeneratedHtmlId } from '@elastic/eui'; 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 new file mode 100644 index 000000000000..a6fce8869999 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/index.tsx @@ -0,0 +1,138 @@ +/* + * 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. + */ + +/* eslint-disable react/jsx-no-literals */ + +import React from 'react'; +import type { FC } from 'react'; +import { EuiIcon, EuiPageHeader, EuiText } from '@elastic/eui'; +import * as i18n from '../translations'; +import { textCss, descriptionContainerCss, backTextCss } from './list_header.styles'; +import { MenuItems } from './menu_items'; +import { TextWithEdit } from '../text_with_edit'; +import { EditModal } from './edit_modal'; +import { ListDetails, Rule } from '../types'; +import { useExceptionListHeader } from './use_list_header'; +import { textWithEditContainerCss } from '../text_with_edit/text_with_edit.styles'; + +interface ExceptionListHeaderComponentProps { + name: string; + description?: string; + listId: string; + isReadonly: boolean; + linkedRules: Rule[]; + dataTestSubj?: string; + backOptions: BackOptions; + canUserEditList?: boolean; + securityLinkAnchorComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + onEditListDetails: (listDetails: ListDetails) => void; + onDeleteList: () => void; + onManageRules: () => void; + onExportList: () => void; + onDuplicateList: () => void; +} + +export interface BackOptions { + pageId: string; + path: string; + dataTestSubj?: string; + onNavigate: (path: string) => void; +} +const ExceptionListHeaderComponent: FC<ExceptionListHeaderComponentProps> = ({ + name, + description, + listId, + linkedRules, + isReadonly, + dataTestSubj, + securityLinkAnchorComponent, + backOptions, + canUserEditList = true, + onEditListDetails, + onDeleteList, + onManageRules, + onExportList, + onDuplicateList, +}) => { + const { isModalVisible, listDetails, onEdit, onSave, onCancel } = useExceptionListHeader({ + name, + description, + onEditListDetails, + }); + return ( + <div> + <EuiPageHeader + bottomBorder + paddingSize="none" + pageTitle={ + <TextWithEdit + dataTestSubj={`${dataTestSubj || ''}Title`} + text={listDetails.name || i18n.EXCEPTION_LIST_HEADER_NAME} + isReadonly={isReadonly || !canUserEditList} + onEdit={onEdit} + /> + } + responsive + data-test-subj={`${dataTestSubj || ''}PageHeader`} + description={ + <div css={descriptionContainerCss}> + <TextWithEdit + dataTestSubj={`${dataTestSubj || ''}Description`} + textCss={textCss} + isReadonly={isReadonly || !canUserEditList} + text={listDetails.description || i18n.EXCEPTION_LIST_HEADER_DESCRIPTION} + onEdit={onEdit} + /> + <div css={textWithEditContainerCss} data-test-subj={`${dataTestSubj || ''}ListID`}> + <EuiText css={textCss}>{i18n.EXCEPTION_LIST_HEADER_LIST_ID}:</EuiText> + <EuiText css={textCss}>{listId}</EuiText> + </div> + </div> + } + rightSideItems={[ + <MenuItems + dataTestSubj={`${dataTestSubj || ''}RightSideMenuItems`} + linkedRules={linkedRules} + isReadonly={isReadonly} + canUserEditList={canUserEditList} + securityLinkAnchorComponent={securityLinkAnchorComponent} + onDeleteList={onDeleteList} + onManageRules={onManageRules} + onExportList={onExportList} + onDuplicateList={onDuplicateList} + />, + ]} + breadcrumbs={[ + { + text: ( + <div data-test-subj={`${dataTestSubj || ''}Breadcrumb`} css={backTextCss}> + <EuiIcon size="s" type="arrowLeft" /> + {i18n.EXCEPTION_LIST_HEADER_BREADCRUMB} + </div> + ), + color: 'primary', + 'aria-current': false, + href: backOptions.path, + onClick: (e) => { + e.preventDefault(); + backOptions.onNavigate(backOptions.path); + }, + }, + ]} + /> + {isModalVisible && ( + <EditModal listDetails={listDetails} onSave={onSave} onCancel={onCancel} /> + )} + </div> + ); +}; + +ExceptionListHeaderComponent.displayName = 'ExceptionListHeaderComponent'; + +export const ExceptionListHeader = React.memo(ExceptionListHeaderComponent); + +ExceptionListHeader.displayName = 'ExceptionListHeader'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.styles.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.styles.ts new file mode 100644 index 000000000000..7a82cd9bc289 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.styles.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; + +export const headerMenuCss = css` + border-right: 1px solid #d3dae6; + padding: ${euiThemeVars.euiSizeXS} ${euiThemeVars.euiSizeL} ${euiThemeVars.euiSizeXS} 0; +`; + +export const noLinkedRulesCss = css` + width: max-content; +`; + +export const textCss = css` + font-size: ${euiThemeVars.euiFontSize}; + color: ${euiThemeVars.euiTextSubduedColor}; + margin-left: ${euiThemeVars.euiSizeXS}; +`; +export const descriptionContainerCss = css` + margin-top: -${euiThemeVars.euiSizeL}; + margin-bottom: -${euiThemeVars.euiSizeL}; +`; + +export const backTextCss = css` + font-size: ${euiThemeVars.euiFontSizeXS}; +`; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.test.tsx similarity index 94% rename from packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.test.tsx index 9ceb648dfa7e..246e88071608 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/list_header.test.tsx @@ -1,10 +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", 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". + * 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'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/__snapshots__/menu_items.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/__snapshots__/menu_items.test.tsx.snap similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/__snapshots__/menu_items.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/__snapshots__/menu_items.test.tsx.snap diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/index.tsx new file mode 100644 index 000000000000..798f737be65d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/index.tsx @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiTextColor } from '@elastic/eui'; +import React, { FC, useMemo } from 'react'; +import { HeaderMenu } from '../../header_menu'; +import { headerMenuCss, noLinkedRulesCss } from '../list_header.styles'; +import * as i18n from '../../translations'; +import { Rule } from '../../types'; +import { generateLinkedRulesMenuItems } from '../../generate_linked_rules_menu_item'; +interface MenuItemsProps { + isReadonly: boolean; + dataTestSubj?: string; + linkedRules: Rule[]; + canUserEditList?: boolean; + securityLinkAnchorComponent: React.ElementType; // This property needs to be removed to avoid the Prop Drilling, once we move all the common components from x-pack/security-solution/common + onDeleteList: () => void; + onManageRules: () => void; + onExportList: () => void; + onDuplicateList: () => void; +} + +const MenuItemsComponent: FC<MenuItemsProps> = ({ + dataTestSubj, + linkedRules, + securityLinkAnchorComponent, + isReadonly, + canUserEditList = true, + onDeleteList, + onManageRules, + onExportList, + onDuplicateList, +}) => { + const referencedLinks = useMemo( + () => + generateLinkedRulesMenuItems({ + leftIcon: 'check', + dataTestSubj, + linkedRules, + securityLinkAnchorComponent, + }), + [dataTestSubj, linkedRules, securityLinkAnchorComponent] + ); + return ( + <EuiFlexGroup + direction="row" + alignItems="baseline" + justifyContent="center" + responsive + data-test-subj={`${dataTestSubj || ''}Container`} + gutterSize="l" + > + <EuiFlexItem css={headerMenuCss}> + {linkedRules.length ? ( + <HeaderMenu + dataTestSubj={`${dataTestSubj || ''}LinkedRulesMenu`} + emptyButton + useCustomActions + text={i18n.EXCEPTION_LIST_HEADER_LINKED_RULES(linkedRules.length)} + actions={referencedLinks} + disableActions={false} + iconType="arrowDown" + iconSide="right" + panelPaddingSize="none" + /> + ) : ( + <EuiTextColor data-test-subj="noLinkedRules" css={noLinkedRulesCss} color="subdued"> + {i18n.EXCEPTION_LIST_HEADER_LINKED_RULES(linkedRules.length)} + </EuiTextColor> + )} + </EuiFlexItem> + + {canUserEditList && ( + <EuiFlexItem> + <EuiButton + data-test-subj={`${dataTestSubj || ''}LinkRulesButton`} + fill + onClick={() => { + if (typeof onManageRules === 'function') onManageRules(); + }} + > + {i18n.EXCEPTION_LIST_HEADER_LINK_RULES_BUTTON} + </EuiButton> + </EuiFlexItem> + )} + <EuiFlexItem> + <HeaderMenu + iconType="boxesHorizontal" + dataTestSubj={`${dataTestSubj || ''}MenuActions`} + actions={[ + { + key: '1', + icon: 'exportAction', + label: i18n.EXCEPTION_LIST_HEADER_EXPORT_ACTION, + onClick: () => { + if (typeof onExportList === 'function') onExportList(); + }, + }, + { + key: '2', + icon: 'copy', + label: i18n.EXCEPTION_LIST_HEADER_DUPLICATE_ACTION, + onClick: () => { + if (typeof onDuplicateList === 'function') onDuplicateList(); + }, + disabled: !canUserEditList, + }, + { + key: '3', + icon: 'trash', + label: i18n.EXCEPTION_LIST_HEADER_DELETE_ACTION, + onClick: () => { + if (typeof onDeleteList === 'function') onDeleteList(); + }, + disabled: !canUserEditList, + }, + ]} + disableActions={isReadonly} + anchorPosition="downCenter" + /> + </EuiFlexItem> + </EuiFlexGroup> + ); +}; + +MenuItemsComponent.displayName = 'MenuItemsComponent'; + +export const MenuItems = React.memo(MenuItemsComponent); + +MenuItems.displayName = 'MenuItems'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/menu_items.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/menu_items.test.tsx similarity index 94% rename from packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/menu_items.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/menu_items.test.tsx index 16072bab295a..8fdf6519cf44 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/menu_items.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/menu_items.test.tsx @@ -1,10 +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", 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". + * 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 { fireEvent, render } from '@testing-library/react'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.test.ts similarity index 86% rename from packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.test.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.test.ts index e16681d2b184..a49ef8f6f560 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.test.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.test.ts @@ -1,10 +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", 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". + * 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 { waitFor, renderHook, act } from '@testing-library/react'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.ts new file mode 100644 index 000000000000..cda4bf346a20 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/use_list_header.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 { useState } from 'react'; +import { ListDetails } from '../types'; + +interface UseExceptionListHeaderProps { + name: string; + description?: string; + onEditListDetails: (listDetails: ListDetails) => void; +} +export const useExceptionListHeader = ({ + name, + description, + onEditListDetails, +}: UseExceptionListHeaderProps) => { + const [isModalVisible, setIsModalVisible] = useState(false); + const [listDetails, setListDetails] = useState<ListDetails>({ name, description }); + const onEdit = () => { + setIsModalVisible(true); + }; + const onSave = (newListDetails: ListDetails) => { + setListDetails(newListDetails); + if (typeof onEditListDetails === 'function') onEditListDetails(newListDetails); + setTimeout(() => { + setIsModalVisible(false); + }, 200); + }; + const onCancel = () => { + setIsModalVisible(false); + }; + + return { + isModalVisible, + listDetails, + onEdit, + onSave, + onCancel, + }; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/comments.mock.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/comments.mock.tsx new file mode 100644 index 000000000000..02767cfb979d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/comments.mock.tsx @@ -0,0 +1,24 @@ +/* + * 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 type { Comment, CommentsArray } from '@kbn/securitysolution-io-ts-list-types'; + +export const getCommentsMock = (): Comment => ({ + comment: 'some old comment', + created_at: '2020-04-20T15:25:31.830Z', + created_by: 'some user', + id: 'uuid_here', +}); + +export const getCommentsArrayMock = (): CommentsArray => [getCommentsMock(), getCommentsMock()]; + +export const mockGetFormattedComments = () => + getCommentsArrayMock().map((comment) => ({ + username: comment.created_by, + children: <p>{comment.comment}</p>, + })); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/entry.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/entry.mock.ts new file mode 100644 index 000000000000..34a4e98cf981 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/entry.mock.ts @@ -0,0 +1,28 @@ +/* + * 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 { Entry } from '../exception_item_card/conditions/types'; + +export const includedListTypeEntry: Entry = { + field: '', + operator: 'included', + type: 'list', + list: { id: 'list_id', type: 'boolean' }, +}; + +export const includedMatchTypeEntry: Entry = { + field: '', + operator: 'included', + type: 'match', + value: 'matches value', +}; + +export const includedExistsTypeEntry: Entry = { + field: '', + operator: 'included', + type: 'exists', +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/exception_list_item_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/exception_list_item_schema.mock.ts new file mode 100644 index 000000000000..a34ddc4dd484 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/exception_list_item_schema.mock.ts @@ -0,0 +1,42 @@ +/* + * 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 { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +export const getExceptionListItemSchemaMock = ( + overrides?: Partial<ExceptionListItemSchema> +): ExceptionListItemSchema => ({ + _version: undefined, + comments: [], + created_at: '2020-04-20T15:25:31.830Z', + created_by: 'some user', + description: 'some description', + entries: [ + { + entries: [ + { field: 'nested.field', operator: 'included', type: 'match', value: 'some value' }, + ], + field: 'some.parentField', + type: 'nested', + }, + { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, + ], + expire_time: undefined, + id: '1', + item_id: 'endpoint_list_item', + list_id: 'endpoint_list_id', + meta: {}, + name: 'some name', + namespace_type: 'single', + os_types: [], + tags: ['user added string for a tag', 'malware'], + tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', + type: 'simple', + updated_at: '2020-04-20T15:25:31.830Z', + updated_by: 'some user', + ...(overrides || {}), +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/header.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/header.mock.ts new file mode 100644 index 000000000000..5ee301495e6f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/header.mock.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const handleEdit = jest.fn(); +export const handleDelete = jest.fn(); +export const actions = [ + { + key: 'edit', + icon: 'pencil', + label: 'Edit detection exception', + onClick: handleEdit, + }, + { + key: 'delete', + icon: 'trash', + label: 'Delete detection exception', + onClick: handleDelete, + }, +]; +export const actionsWithDisabledDelete = [ + { + key: 'edit', + icon: 'pencil', + label: 'Edit detection exception', + onClick: handleEdit, + }, + { + key: 'delete', + icon: 'trash', + disabled: true, + label: 'Delete detection exception', + onClick: handleDelete, + }, +]; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/rule_references.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/rule_references.mock.ts new file mode 100644 index 000000000000..c4eaff274a74 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/rule_references.mock.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Rule, RuleReference } from '../types'; + +export const rules: Rule[] = [ + { + exceptions_list: [ + { + id: '123', + list_id: 'i_exist', + namespace_type: 'single', + type: 'detection', + }, + { + id: '456', + list_id: 'i_exist_2', + namespace_type: 'single', + type: 'detection', + }, + ], + id: '1a2b3c', + name: 'Simple Rule Query', + rule_id: 'rule-2', + }, +]; + +export const ruleReference: RuleReference = { + name: 'endpoint list', + id: 'endpoint_list', + referenced_rules: rules, + listId: 'endpoint_list_id', +}; + +export const ruleReferences = { + endpoint_list_id: ruleReference, +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/security_link_component.mock.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/security_link_component.mock.tsx new file mode 100644 index 000000000000..03c44d7e9166 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/security_link_component.mock.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { ReactElement } from 'react'; +import { generateLinkedRulesMenuItems } from '../generate_linked_rules_menu_item'; +import { rules } from './rule_references.mock'; +export const securityLinkAnchorComponentMock = ({ + referenceName, + referenceId, +}: { + referenceName: string; + referenceId: string; +}) => ( + <div data-test-subj="securityLinkAnchorComponent"> + <a href={referenceId}>{referenceName}</a> + </div> +); + +export const getSecurityLinkAction = (dataTestSubj: string) => + generateLinkedRulesMenuItems({ + dataTestSubj, + linkedRules: [ + ...rules, + { + exceptions_list: [], + id: '2a2b3c', + name: 'Simple Rule Query 2', + rule_id: 'rule-2', + }, + ], + securityLinkAnchorComponent: securityLinkAnchorComponentMock, + }) as ReactElement[]; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/value_list_modal.mock.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/value_list_modal.mock.tsx new file mode 100644 index 000000000000..e0f9f5d700be --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/mocks/value_list_modal.mock.tsx @@ -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 React from 'react'; + +export const mockShowValueListModal = jest.fn(); +export const MockedShowValueListModal = (props: { children: React.ReactNode }) => { + mockShowValueListModal(props); + return <>{props.children}</>; +}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.test.tsx similarity index 84% rename from packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.test.tsx index 5eaab267abb5..1467653d432b 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.test.tsx @@ -1,10 +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", 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". + * 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 { fireEvent, render } from '@testing-library/react'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx new file mode 100644 index 000000000000..e7275c4be2c2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/pagination.tsx @@ -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 React from 'react'; +import type { FC } from 'react'; +import { EuiTablePagination } from '@elastic/eui'; + +import type { PaginationProps } from '../types'; +import { usePagination } from './use_pagination'; + +const PaginationComponent: FC<PaginationProps> = ({ + dataTestSubj, + ariaLabel, + pagination, + onPaginationChange, +}) => { + const { + pageIndex, + pageCount, + pageSize, + pageSizeOptions, + + handleItemsPerPageChange, + handlePageIndexChange, + } = usePagination({ pagination, onPaginationChange }); + + return ( + <EuiTablePagination + data-test-subj={dataTestSubj} + aria-label={ariaLabel} + pageCount={pageCount} + activePage={pageIndex} + itemsPerPage={pageSize} + onChangePage={handlePageIndexChange} + onChangeItemsPerPage={handleItemsPerPageChange} + itemsPerPageOptions={pageSizeOptions} + /> + ); +}; + +PaginationComponent.displayName = 'PaginationComponent'; + +export const Pagination = React.memo(PaginationComponent); + +Pagination.displayName = 'Pagination'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.test.ts similarity index 87% rename from packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.test.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.test.ts index e90408f1e161..2126ace9191b 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.test.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.test.ts @@ -1,10 +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", 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". + * 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 { renderHook, act } from '@testing-library/react'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.ts similarity index 76% rename from packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.ts index da96624f8b43..289d5ebcaa26 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/pagination/use_pagination.ts @@ -1,10 +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", 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". + * 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 { useCallback, useMemo } from 'react'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/partial_code_signature_callout/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/partial_code_signature_callout/index.tsx new file mode 100644 index 000000000000..4339d95b6c4c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/partial_code_signature_callout/index.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import { EuiCallOut } from '@elastic/eui'; + +export const PartialCodeSignatureCallout = () => { + return ( + <EuiCallOut + title={i18n.translate('exceptionList-components.partialCodeSignatureCallout.title', { + defaultMessage: 'Please review your entries', + })} + iconType="warning" + color="warning" + size="s" + data-test-subj="partialCodeSignatureCallout" + > + <FormattedMessage + id="exceptionList-components.partialCodeSignatureCallout.body" + defaultMessage='Please review field values, as your filter criteria may be incomplete. We recommend both the signer name and trust status be included (using the "AND" operator) to avoid potential security gaps.' + tagName="p" + /> + </EuiCallOut> + ); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/search_bar/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/search_bar/index.tsx new file mode 100644 index 000000000000..dd36c16b9bd8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/search_bar/index.tsx @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import type { FC } from 'react'; + +import type { EuiSearchBarProps, IconType, SearchFilterConfig } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiSearchBar } from '@elastic/eui'; +import type { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import type { GetExceptionItemProps } from '../types'; + +const ITEMS_SCHEMA = { + strict: true, + fields: { + created_by: { + type: 'string', + }, + description: { + type: 'string', + }, + id: { + type: 'string', + }, + item_id: { + type: 'string', + }, + list_id: { + type: 'string', + }, + name: { + type: 'string', + }, + os_types: { + type: 'string', + }, + tags: { + type: 'string', + }, + }, +}; +interface SearchBarProps { + addExceptionButtonText?: string; + placeholdertext?: string; + canAddException?: boolean; // TODO what is the default value + + // TODO: REFACTOR: not to send the listType and handle it in the Parent + // Exception list type used to determine what type of item is + // being created when "onAddExceptionClick" is invoked + listType: ExceptionListTypeEnum; + isSearching?: boolean; + dataTestSubj?: string; + filters?: SearchFilterConfig[]; // TODO about filters + isButtonFilled?: boolean; + buttonIconType?: IconType; + onSearch: (arg: GetExceptionItemProps) => void; + onAddExceptionClick: (type: ExceptionListTypeEnum) => void; +} +const SearchBarComponent: FC<SearchBarProps> = ({ + addExceptionButtonText, + placeholdertext, + canAddException, + listType, + isSearching, + dataTestSubj, + filters = [], + isButtonFilled = true, + buttonIconType, + onSearch, + onAddExceptionClick, +}) => { + const handleOnSearch = useCallback<NonNullable<EuiSearchBarProps['onChange']>>( + ({ queryText }): void => { + onSearch({ search: queryText }); + }, + [onSearch] + ); + + const handleAddException = useCallback(() => { + // TODO: ASK YARA why we need to send the listType + onAddExceptionClick(listType); + }, [onAddExceptionClick, listType]); + + return ( + <EuiFlexGroup alignItems="center"> + <EuiFlexItem grow={true}> + <EuiSearchBar + box={{ + placeholder: placeholdertext, + incremental: false, + schema: ITEMS_SCHEMA, + 'data-test-subj': `${dataTestSubj || ''}searchBar`, + }} + filters={filters} + onChange={handleOnSearch} + /> + </EuiFlexItem> + {!canAddException && ( + <EuiFlexItem grow={false}> + <EuiButton + data-test-subj={`${dataTestSubj || ''}Button`} + onClick={handleAddException} + isDisabled={isSearching} + fill={isButtonFilled} + iconType={buttonIconType} + > + {addExceptionButtonText} + </EuiButton> + </EuiFlexItem> + )} + </EuiFlexGroup> + ); +}; + +SearchBarComponent.displayName = 'SearchBarComponent'; + +export const SearchBar = React.memo(SearchBarComponent); + +SearchBar.displayName = 'SearchBar'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.test.tsx similarity index 88% rename from packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.test.tsx index b1eadded0e3f..a7eaa7ba47ad 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/search_bar/search_bar.test.tsx @@ -1,10 +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", 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". + * 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'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/__snapshots__/text_with_edit.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/__snapshots__/text_with_edit.test.tsx.snap similarity index 100% rename from packages/kbn-securitysolution-exception-list-components/src/text_with_edit/__snapshots__/text_with_edit.test.tsx.snap rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/__snapshots__/text_with_edit.test.tsx.snap 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 new file mode 100644 index 000000000000..1428dcfa238e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/index.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { Interpolation, Theme } from '@emotion/react'; +import { textWithEditContainerCss, editIconCss } from './text_with_edit.styles'; +interface TextWithEditProps { + isReadonly: boolean; + dataTestSubj?: string; + text: string; + textCss?: Interpolation<Theme>; + onEdit?: () => void; +} + +const TextWithEditComponent: FC<TextWithEditProps> = ({ + isReadonly, + dataTestSubj, + text, + onEdit, + textCss, +}) => { + return ( + <EuiFlexGroup css={textWithEditContainerCss}> + <EuiFlexItem grow={10}> + <span css={textCss} data-test-subj={`${dataTestSubj || ''}Text`}> + {text} + </span> + </EuiFlexItem> + <EuiFlexItem grow={false} css={editIconCss}> + {isReadonly ? null : ( + <EuiButtonIcon + data-test-subj={`${dataTestSubj || ''}EditIcon`} + aria-label="Edit Text List Header" + iconType="pencil" + onClick={() => (typeof onEdit === 'function' ? onEdit() : null)} + /> + )} + </EuiFlexItem> + </EuiFlexGroup> + ); +}; +TextWithEditComponent.displayName = 'TextWithEditComponent'; + +export const TextWithEdit = React.memo(TextWithEditComponent); + +TextWithEdit.displayName = 'TextWithEdit'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.styles.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.styles.ts new file mode 100644 index 000000000000..d61aaab0f9be --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.styles.ts @@ -0,0 +1,24 @@ +/* + * 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 textWithEditContainerCss = css` + display: flex; + width: fit-content; + align-items: baseline; + padding-bottom: ${euiThemeVars.euiSizeS}; + h1 { + margin-bottom: 0; + } +`; +export const editIconCss = css` + button { + margin-left: -${euiThemeVars.euiSizeM}; + } +`; diff --git a/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.test.tsx similarity index 84% rename from packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.test.tsx index eab0eb7817d8..24b5eec6e335 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/text_with_edit.test.tsx @@ -1,10 +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", 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". + * 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 { fireEvent, render } from '@testing-library/react'; @@ -35,6 +33,7 @@ describe('TextWithEdit', () => { isReadonly={false} dataTestSubj="TextWithEditTest" text="Test" + // eslint-disable-next-line @typescript-eslint/no-explicit-any onEdit={onEdit as any} /> ); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/translations.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/translations.ts new file mode 100644 index 000000000000..5660ac3564c7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/translations.ts @@ -0,0 +1,154 @@ +/* + * 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'; + +export const EMPTY_VIEWER_STATE_EMPTY_TITLE = i18n.translate( + 'exceptionList-components.empty.viewer.state.empty.title', + { + defaultMessage: 'Add exceptions to this list', + } +); + +export const EMPTY_VIEWER_STATE_EMPTY_BODY = i18n.translate( + 'exceptionList-components.empty.viewer.state.empty.body', + { + defaultMessage: 'There is no exception in your list. Create your first exception.', + } +); +export const EMPTY_VIEWER_STATE_EMPTY_SEARCH_TITLE = i18n.translate( + 'exceptionList-components.empty.viewer.state.empty_search.search.title', + { + defaultMessage: 'No results match your search criteria', + } +); + +export const EMPTY_VIEWER_STATE_EMPTY_SEARCH_BODY = i18n.translate( + 'exceptionList-components.empty.viewer.state.empty_search.body', + { + defaultMessage: 'Try modifying your search', + } +); + +export const EMPTY_VIEWER_STATE_EMPTY_VIEWER_BUTTON = (exceptionType: string) => + i18n.translate('exceptionList-components.empty.viewer.state.empty.viewer_button', { + values: { exceptionType }, + defaultMessage: 'Create {exceptionType} exception', + }); + +export const EMPTY_VIEWER_STATE_ERROR_TITLE = i18n.translate( + 'exceptionList-components.empty.viewer.state.error_title', + { + defaultMessage: 'Unable to load exception items', + } +); + +export const EMPTY_VIEWER_STATE_ERROR_BODY = i18n.translate( + 'exceptionList-components.empty.viewer.state.error_body', + { + defaultMessage: + 'There was an error loading the exception items. Contact your administrator for help.', + } +); +export const EXCEPTION_LIST_HEADER_EXPORT_ACTION = i18n.translate( + 'exceptionList-components.exception_list_header_export_action', + { + defaultMessage: 'Export exception list', + } +); +export const EXCEPTION_LIST_HEADER_DELETE_ACTION = i18n.translate( + 'exceptionList-components.exception_list_header_delete_action', + { + defaultMessage: 'Delete exception list', + } +); +export const EXCEPTION_LIST_HEADER_DUPLICATE_ACTION = i18n.translate( + 'exceptionList-components.exception_list_header_duplicate_action', + { + defaultMessage: 'Duplicate exception list', + } +); +export const EXCEPTION_LIST_HEADER_LINK_RULES_BUTTON = i18n.translate( + 'exceptionList-components.exception_list_header_link_rules_button', + { + defaultMessage: 'Link rules', + } +); + +export const EXCEPTION_LIST_HEADER_LINKED_RULES = (noOfRules: number) => + i18n.translate('exceptionList-components.exception_list_header_linked_rules', { + values: { noOfRules }, + defaultMessage: 'Linked to {noOfRules} rules', + }); + +export const EXCEPTION_LIST_HEADER_BREADCRUMB = i18n.translate( + 'exceptionList-components.exception_list_header_breadcrumb', + { + defaultMessage: 'Shared Exception Lists', + } +); + +export const EXCEPTION_LIST_HEADER_LIST_ID = i18n.translate( + 'exceptionList-components.exception_list_header_list_id', + { + defaultMessage: 'List ID', + } +); + +export const EXCEPTION_LIST_HEADER_NAME = i18n.translate( + 'exceptionList-components.exception_list_header_name', + { + defaultMessage: 'Add a name', + } +); + +export const EXCEPTION_LIST_HEADER_DESCRIPTION = i18n.translate( + 'exceptionList-components.exception_list_header_description', + { + defaultMessage: 'Add a description', + } +); + +export const EXCEPTION_LIST_HEADER_EDIT_MODAL_TITLE = (listName: string) => + i18n.translate('exceptionList-components.exception_list_header_edit_modal_name', { + defaultMessage: 'Edit {listName}', + values: { listName }, + }); + +export const EXCEPTION_LIST_HEADER_EDIT_MODAL_SAVE_BUTTON = i18n.translate( + 'exceptionList-components.exception_list_header_edit_modal_save_button', + { + defaultMessage: 'Save', + } +); + +export const EXCEPTION_LIST_HEADER_EDIT_MODAL_CANCEL_BUTTON = i18n.translate( + 'exceptionList-components.exception_list_header_edit_modal_cancel_button', + { + defaultMessage: 'Cancel', + } +); +export const EXCEPTION_LIST_HEADER_NAME_TEXTBOX = i18n.translate( + 'exceptionList-components.exception_list_header_Name_textbox', + { + defaultMessage: 'Name', + } +); + +export const EXCEPTION_LIST_HEADER_DESCRIPTION_TEXTBOX = i18n.translate( + 'exceptionList-components.exception_list_header_description_textbox', + { + defaultMessage: 'Description (optional)', + } +); + +export const LIST_NAME_REQUIRED_ERROR = i18n.translate( + 'exceptionList-components.exception_list_header_description_textboxexceptionList-components.exception_list_header_name_required_eror', + { + defaultMessage: 'List name cannot be empty', + } +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/types/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/types/index.ts new file mode 100644 index 000000000000..d628cd013797 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/types/index.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ListArray } from '@kbn/securitysolution-io-ts-list-types'; + +import type { Pagination } from '@elastic/eui'; +import type { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; + +export interface GetExceptionItemProps { + pagination?: PaginationProps['pagination']; + search?: string; + filters?: string; +} + +export interface PaginationProps { + dataTestSubj?: string; + ariaLabel?: string; + pagination: Pagination & { pageSize: number }; + onPaginationChange: (arg: GetExceptionItemProps) => void; +} + +export enum ViewerStatus { + ERROR = 'error', + EMPTY = 'empty', + EMPTY_SEARCH = 'empty_search', + LOADING = 'loading', + SEARCHING = 'searching', + DELETING = 'deleting', +} + +export interface ExceptionListSummaryProps { + pagination: Pagination; + // Corresponds to last time exception items were fetched + lastUpdated: string | number | null; +} + +export type ViewerFlyoutName = 'addException' | 'editException' | null; + +export interface RuleReferences { + [key: string]: RuleReference; +} + +export interface ExceptionListItemIdentifiers { + id: string; + name: string; + namespaceType: NamespaceType; +} + +export enum ListTypeText { + ENDPOINT = 'endpoint', + DETECTION = 'empty', + RULE_DEFAULT = 'empty_search', +} +export interface Rule { + name: string; + id: string; + rule_id: string; + exceptions_list?: ListArray; +} + +export interface RuleReference { + name: string; + id: string; + referenced_rules: Rule[]; + listId?: string; +} + +export interface ListDetails { + name: string; + description?: string; +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/index.ts new file mode 100644 index 000000000000..968bc9666b09 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/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 { ValueWithSpaceWarning } from './value_with_space_warning'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.test.ts similarity index 84% rename from packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.test.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.test.ts index 51954c0140e6..570399a5ecb2 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.test.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.test.ts @@ -1,10 +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", 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". + * 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 { renderHook } from '@testing-library/react'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.ts new file mode 100644 index 000000000000..252f05f4d102 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/use_value_with_space_warning.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 { paramContainsSpace, autoCompletei18n } from '@kbn/securitysolution-autocomplete'; + +interface UseValueWithSpaceWarningResult { + showSpaceWarningIcon: boolean; + warningText: string; +} +interface UseValueWithSpaceWarningProps { + value: string | string[]; + tooltipIconText?: string; +} + +export const useValueWithSpaceWarning = ({ + value, + tooltipIconText, +}: UseValueWithSpaceWarningProps): UseValueWithSpaceWarningResult => { + const showSpaceWarningIcon = Array.isArray(value) + ? value.find(paramContainsSpace) + : paramContainsSpace(value); + + return { + showSpaceWarningIcon: !!showSpaceWarningIcon, + warningText: tooltipIconText || autoCompletei18n.FIELD_SPACE_WARNING, + }; +}; diff --git a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx similarity index 81% rename from packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx index 8f780181e052..a500f13af6e1 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx @@ -1,10 +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", 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". + * 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'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx similarity index 75% rename from packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx rename to x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx index c736982142b1..c73a39a516ed 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.tsx @@ -1,10 +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", 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". + * 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'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/wildcard_with_wrong_operator_callout/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/wildcard_with_wrong_operator_callout/index.tsx new file mode 100644 index 000000000000..b21259f4ed89 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/wildcard_with_wrong_operator_callout/index.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import { EuiCallOut } from '@elastic/eui'; + +export const WildCardWithWrongOperatorCallout = () => { + return ( + <EuiCallOut + title={i18n.translate('exceptionList-components.wildcardWithWrongOperatorCallout.title', { + defaultMessage: 'Please review your entries', + })} + iconType="warning" + color="warning" + size="s" + data-test-subj="wildcardWithWrongOperatorCallout" + > + <p> + <FormattedMessage + id="exceptionList-components.wildcardWithWrongOperatorCallout.body" + defaultMessage='Using a "*" or a "?" in the value with the "is" operator can make the entry ineffective. {operator} to "{matches}" to ensure wildcards run properly.' + values={{ + operator: ( + <strong> + {i18n.translate( + 'exceptionList-components.wildcardWithWrongOperatorCallout.changeTheOperator', + { defaultMessage: 'Change the operator' } + )} + </strong> + ), + matches: ( + <strong> + {i18n.translate( + 'exceptionList-components.wildcardWithWrongOperatorCallout.matches', + { defaultMessage: 'matches' } + )} + </strong> + ), + }} + /> + </p> + </EuiCallOut> + ); +}; 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 new file mode 100644 index 000000000000..5378a8fbdfe1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react", + "@emotion/react/types/css-prop" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + "**/*.d.ts" + ], + "kbn_references": [ + "@kbn/securitysolution-io-ts-list-types", + "@kbn/securitysolution-autocomplete", + "@kbn/ui-theme", + "@kbn/i18n", + "@kbn/i18n-react", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-securitysolution-exceptions-common/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/README.md similarity index 100% rename from packages/kbn-securitysolution-exceptions-common/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/README.md diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.gen.ts similarity index 78% rename from packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.gen.ts index a9acaee37cef..2c7dc29dd182 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.schema.yaml similarity index 79% rename from packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.schema.yaml index 5925d0bd923c..e7a399c2d7a8 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.schema.yaml @@ -59,29 +59,29 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 409: description: Exception list already exists response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.gen.ts similarity index 85% rename from packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.gen.ts index 9adf64b6b083..dba75c11fde8 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.schema.yaml similarity index 78% rename from packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.schema.yaml index 47fa2895d27c..2913d8c5c07d 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.schema.yaml @@ -69,32 +69,32 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 409: description: Exception list item already exists response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' components: x-codegen-enabled: true @@ -103,7 +103,7 @@ components: type: object properties: comment: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' required: - comment diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.gen.ts similarity index 88% rename from packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.gen.ts index 77437ff51618..e2fa379cdc52 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.schema.yaml similarity index 78% rename from packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.schema.yaml index 6162d00d78ae..b7b2db3fabef 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.schema.yaml @@ -9,7 +9,7 @@ paths: operationId: CreateRuleExceptionListItems x-codegen-enabled: true summary: Create rule exception list items - description: Create exception items that apply to a single detection rule. + description: Create exception items that apply to a single detection rule. parameters: - name: id in: path @@ -45,37 +45,37 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' components: schemas: RuleId: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/UUID' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/UUID' CreateRuleExceptionListItemComment: type: object properties: comment: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' required: - comment diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.gen.ts new file mode 100644 index 000000000000..07650ee2d0c0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.gen.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Create shared exception list API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { + ExceptionListName, + ExceptionListDescription, + ExceptionList, +} from '../model/exception_list_common.gen'; + +export type CreateSharedExceptionListRequestBody = z.infer< + typeof CreateSharedExceptionListRequestBody +>; +export const CreateSharedExceptionListRequestBody = z.object({ + name: ExceptionListName, + description: ExceptionListDescription, +}); +export type CreateSharedExceptionListRequestBodyInput = z.input< + typeof CreateSharedExceptionListRequestBody +>; + +export type CreateSharedExceptionListResponse = z.infer<typeof CreateSharedExceptionListResponse>; +export const CreateSharedExceptionListResponse = ExceptionList; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.schema.yaml new file mode 100644 index 000000000000..040acca3ebd7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.schema.yaml @@ -0,0 +1,68 @@ +openapi: 3.0.0 +info: + title: Create shared exception list API endpoint + version: '2023-10-31' +paths: + /api/exceptions/shared: + post: + x-labels: [serverless, ess] + operationId: CreateSharedExceptionList + x-codegen-enabled: true + summary: Create a shared exception list + description: | + An exception list groups exception items and can be associated with detection rules. A shared exception list can apply to multiple detection rules. + > info + > All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + name: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListName' + description: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListDescription' + required: + - name + - description + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionList' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 409: + description: Exception list already exists response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.gen.ts similarity index 75% rename from packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.gen.ts index a12512aab137..0842dc7c7463 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.schema.yaml new file mode 100644 index 000000000000..2912070635b8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.schema.yaml @@ -0,0 +1,70 @@ +openapi: 3.0.0 +info: + title: Delete exception list API endpoint + version: '2023-10-31' +paths: + /api/exception_lists: + delete: + x-labels: [serverless, ess] + operationId: DeleteExceptionList + x-codegen-enabled: true + summary: Delete an exception list + description: Delete an exception list using the `id` or `list_id` field. + parameters: + - name: id + in: query + required: false + description: Either `id` or `list_id` must be specified + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListId' + - name: list_id + in: query + required: false + description: Either `id` or `list_id` must be specified + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListHumanId' + - name: namespace_type + in: query + required: false + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' + default: single + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionList' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: Exception list not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.gen.ts similarity index 76% rename from packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.gen.ts index a93af0cf3d4c..429568c33f1c 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.schema.yaml new file mode 100644 index 000000000000..05f997307a4c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.schema.yaml @@ -0,0 +1,70 @@ +openapi: 3.0.0 +info: + title: Delete exception list item API endpoint + version: '2023-10-31' +paths: + /api/exception_lists/items: + delete: + x-labels: [serverless, ess] + operationId: DeleteExceptionListItem + x-codegen-enabled: true + summary: Delete an exception list item + description: Delete an exception list item using the `id` or `item_id` field. + parameters: + - name: id + in: query + required: false + description: Either `id` or `item_id` must be specified + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemId' + - name: item_id + in: query + required: false + description: Either `id` or `item_id` must be specified + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemHumanId' + - name: namespace_type + in: query + required: false + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' + default: single + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItem' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: Exception list item not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.gen.ts similarity index 75% rename from packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.gen.ts index 61de17abe06f..d259d37b2348 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.schema.yaml new file mode 100644 index 000000000000..80620c4adf7f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.schema.yaml @@ -0,0 +1,71 @@ +openapi: 3.0.0 +info: + title: Duplicate exception list API endpoint + version: '2023-10-31' +paths: + /api/exception_lists/_duplicate: + post: + x-labels: [serverless, ess] + operationId: DuplicateExceptionList + x-codegen-enabled: true + summary: Duplicate an exception list + description: Duplicate an existing exception list. + parameters: + - name: list_id + in: query + required: true + description: Exception list's human identifier + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListHumanId' + - name: namespace_type + in: query + required: true + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' + - name: include_expired_exceptions + in: query + required: true + description: Determines whether to include expired exceptions in the exported list + schema: + type: string + enum: ['true', 'false'] + default: 'true' + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionList' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 405: + description: Exception list to duplicate not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.gen.ts new file mode 100644 index 000000000000..280884c7d749 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.gen.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Export exception list API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { + ExceptionListId, + ExceptionListHumanId, + ExceptionNamespaceType, +} from '../model/exception_list_common.gen'; + +export type ExportExceptionListRequestQuery = z.infer<typeof ExportExceptionListRequestQuery>; +export const ExportExceptionListRequestQuery = z.object({ + /** + * Exception list's identifier + */ + id: ExceptionListId, + /** + * Exception list's human identifier + */ + list_id: ExceptionListHumanId, + namespace_type: ExceptionNamespaceType, + /** + * Determines whether to include expired exceptions in the exported list + */ + include_expired_exceptions: z.enum(['true', 'false']).default('true'), +}); +export type ExportExceptionListRequestQueryInput = z.input<typeof ExportExceptionListRequestQuery>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.schema.yaml new file mode 100644 index 000000000000..89f97ff8bbe6 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.schema.yaml @@ -0,0 +1,79 @@ +openapi: 3.0.0 +info: + title: Export exception list API endpoint + version: '2023-10-31' +paths: + /api/exception_lists/_export: + post: + x-labels: [serverless, ess] + operationId: ExportExceptionList + x-codegen-enabled: true + summary: Export an exception list + description: Export an exception list and its associated items to an NDJSON file. + parameters: + - name: id + in: query + required: true + description: Exception list's identifier + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListId' + - name: list_id + in: query + required: true + description: Exception list's human identifier + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListHumanId' + - name: namespace_type + in: query + required: true + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' + - name: include_expired_exceptions + in: query + required: true + description: Determines whether to include expired exceptions in the exported list + schema: + type: string + enum: ['true', 'false'] + default: 'true' + responses: + 200: + description: Successful response + content: + application/ndjson: + schema: + type: string + format: binary + description: A `.ndjson` file containing specified exception list and its items + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: Exception list not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.gen.ts similarity index 86% rename from packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.gen.ts index 99ed1b3a31dd..d7606bbccff3 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.schema.yaml similarity index 77% rename from packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.schema.yaml index e40f780af03e..6d390e9ecdf6 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.schema.yaml @@ -65,7 +65,7 @@ paths: required: false description: Determines which field is used to sort the results schema: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - name: sort_order in: query required: false @@ -107,34 +107,34 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 404: description: Exception list not found response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' components: schemas: FindExceptionListItemsFilter: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' diff --git a/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.gen.ts similarity index 85% rename from packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.gen.ts index 83a68b4232e8..82f5de2f5a15 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.schema.yaml similarity index 82% rename from packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.schema.yaml index c46dacbab01d..49017d6d4591 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.schema.yaml @@ -93,26 +93,26 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' components: schemas: diff --git a/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.gen.ts similarity index 86% rename from packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.gen.ts index ea24803e7945..738ce79dd97d 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.schema.yaml similarity index 85% rename from packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.schema.yaml index 8ae3ac1aa2c0..95bc9ee508e5 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.schema.yaml @@ -92,26 +92,26 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' components: schemas: diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/index.ts new file mode 100644 index 000000000000..014fe58cfce0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './model/exception_list_common.gen'; +export * from './model/exception_list_item_entry.gen'; +export * from './create_exception_list_item/create_exception_list_item.gen'; +export * from './create_rule_exceptions/create_rule_exceptions.gen'; +export * from './create_shared_exceptions_list/create_shared_exceptions_list.gen'; +export * from './create_exception_list/create_exception_list.gen'; +export * from './delete_exception_list_item/delete_exception_list_item.gen'; +export * from './delete_exception_list/delete_exception_list.gen'; +export * from './duplicate_exception_list/duplicate_exception_list.gen'; +export * from './export_exception_list/export_exception_list.gen'; +export * from './find_exception_list_items/find_exception_list_items.gen'; +export * from './find_exception_lists/find_exception_lists.gen'; +export * from './import_exceptions/import_exceptions.gen'; +export * from './read_exception_list_item/read_exception_list_item.gen'; +export * from './read_exception_list/read_exception_list.gen'; +export * from './read_exception_list_summary/read_exception_list_summary.gen'; +export * from './update_exception_list_item/update_exception_list_item.gen'; +export * from './update_exception_list/update_exception_list.gen'; diff --git a/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.gen.ts similarity index 94% rename from packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.gen.ts index 1f2d9e7387da..2ee44afa69b9 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml similarity index 83% rename from packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml index ebcecc9c916f..4ca9326ec6c9 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_common.schema.yaml @@ -7,10 +7,10 @@ components: x-codegen-enabled: true schemas: ExceptionListId: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' ExceptionListHumanId: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' description: Human readable string identifier, e.g. `trusted-linux-processes` ExceptionListType: @@ -122,17 +122,17 @@ components: - updated_by ExceptionListItemId: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' ExceptionListItemHumanId: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' ExceptionListItemType: type: string enum: [simple] ExceptionListItemName: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' ExceptionListItemDescription: type: string @@ -144,7 +144,7 @@ components: ExceptionListItemTags: type: array items: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' ExceptionListItemOsType: type: string @@ -162,19 +162,19 @@ components: type: object properties: id: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' comment: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' created_at: type: string format: date-time created_by: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' updated_at: type: string format: date-time updated_by: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' required: - id - comment @@ -278,7 +278,7 @@ components: comments: $ref: '#/components/schemas/ExceptionListItemCommentArray' version: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' tie_breaker_id: type: string created_at: diff --git a/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.gen.ts similarity index 90% rename from packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.gen.ts index 0b7f0233ba42..272e5e7d6c35 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.schema.yaml new file mode 100644 index 000000000000..ab8c427344a0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/model/exception_list_item_entry.schema.yaml @@ -0,0 +1,147 @@ +openapi: 3.0.0 +info: + title: Common Exception List Item Entry Attributes + version: 'not applicable' +paths: {} +components: + x-codegen-enabled: true + schemas: + ExceptionListItemEntryOperator: + type: string + enum: [excluded, included] + + ExceptionListItemEntryMatch: + type: object + properties: + type: + type: string + enum: [match] + field: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + value: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + operator: + $ref: '#/components/schemas/ExceptionListItemEntryOperator' + required: + - type + - field + - value + - operator + + ExceptionListItemEntryMatchAny: + type: object + properties: + type: + type: string + enum: [match_any] + field: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + value: + type: array + items: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + minItems: 1 + operator: + $ref: '#/components/schemas/ExceptionListItemEntryOperator' + required: + - type + - field + - value + - operator + + ExceptionListItemEntryList: + type: object + properties: + type: + type: string + enum: [list] + field: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + list: + type: object + properties: + id: + $ref: '../../../kbn-securitysolution-lists-common/api/model/list_common.schema.yaml#/components/schemas/ListId' + type: + $ref: '../../../kbn-securitysolution-lists-common/api/model/list_common.schema.yaml#/components/schemas/ListType' + required: [id, type] + operator: + $ref: '#/components/schemas/ExceptionListItemEntryOperator' + required: + - type + - field + - list + - operator + + ExceptionListItemEntryExists: + type: object + properties: + type: + type: string + enum: [exists] + field: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + operator: + $ref: '#/components/schemas/ExceptionListItemEntryOperator' + required: + - type + - field + - operator + + ExceptionListItemEntryNestedEntryItem: + oneOf: + - $ref: '#/components/schemas/ExceptionListItemEntryMatch' + - $ref: '#/components/schemas/ExceptionListItemEntryMatchAny' + - $ref: '#/components/schemas/ExceptionListItemEntryExists' + + ExceptionListItemEntryNested: + type: object + properties: + type: + type: string + enum: [nested] + field: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + entries: + type: array + items: + $ref: '#/components/schemas/ExceptionListItemEntryNestedEntryItem' + minItems: 1 + required: + - type + - field + - entries + + ExceptionListItemEntryMatchWildcard: + type: object + properties: + type: + type: string + enum: [wildcard] + field: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + value: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + operator: + $ref: '#/components/schemas/ExceptionListItemEntryOperator' + required: + - type + - field + - value + - operator + + ExceptionListItemEntry: + discriminator: + propertyName: type + anyOf: + - $ref: '#/components/schemas/ExceptionListItemEntryMatch' + - $ref: '#/components/schemas/ExceptionListItemEntryMatchAny' + - $ref: '#/components/schemas/ExceptionListItemEntryList' + - $ref: '#/components/schemas/ExceptionListItemEntryExists' + - $ref: '#/components/schemas/ExceptionListItemEntryNested' + - $ref: '#/components/schemas/ExceptionListItemEntryMatchWildcard' + + ExceptionListItemEntryArray: + type: array + items: + $ref: '#/components/schemas/ExceptionListItemEntry' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts new file mode 100644 index 000000000000..bfa84f18fa7c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts @@ -0,0 +1,425 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Exceptions API client for quickstart + * version: Bundle (no version) + */ + +import type { KbnClient } from '@kbn/test'; +import { ToolingLog } from '@kbn/tooling-log'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { replaceParams } from '@kbn/openapi-common/shared'; +import { catchAxiosErrorFormatAndThrow } from '@kbn/securitysolution-utils'; + +import type { + CreateExceptionListItemRequestBodyInput, + CreateExceptionListItemResponse, +} from './create_exception_list_item/create_exception_list_item.gen'; +import type { + CreateExceptionListRequestBodyInput, + CreateExceptionListResponse, +} from './create_exception_list/create_exception_list.gen'; +import type { + CreateRuleExceptionListItemsRequestParamsInput, + CreateRuleExceptionListItemsRequestBodyInput, + CreateRuleExceptionListItemsResponse, +} from './create_rule_exceptions/create_rule_exceptions.gen'; +import type { + CreateSharedExceptionListRequestBodyInput, + CreateSharedExceptionListResponse, +} from './create_shared_exceptions_list/create_shared_exceptions_list.gen'; +import type { + DeleteExceptionListItemRequestQueryInput, + DeleteExceptionListItemResponse, +} from './delete_exception_list_item/delete_exception_list_item.gen'; +import type { + DeleteExceptionListRequestQueryInput, + DeleteExceptionListResponse, +} from './delete_exception_list/delete_exception_list.gen'; +import type { + DuplicateExceptionListRequestQueryInput, + DuplicateExceptionListResponse, +} from './duplicate_exception_list/duplicate_exception_list.gen'; +import type { ExportExceptionListRequestQueryInput } from './export_exception_list/export_exception_list.gen'; +import type { + FindExceptionListItemsRequestQueryInput, + FindExceptionListItemsResponse, +} from './find_exception_list_items/find_exception_list_items.gen'; +import type { + FindExceptionListsRequestQueryInput, + FindExceptionListsResponse, +} from './find_exception_lists/find_exception_lists.gen'; +import type { + ImportExceptionListRequestQueryInput, + ImportExceptionListResponse, +} from './import_exceptions/import_exceptions.gen'; +import type { + ReadExceptionListItemRequestQueryInput, + ReadExceptionListItemResponse, +} from './read_exception_list_item/read_exception_list_item.gen'; +import type { + ReadExceptionListSummaryRequestQueryInput, + ReadExceptionListSummaryResponse, +} from './read_exception_list_summary/read_exception_list_summary.gen'; +import type { + ReadExceptionListRequestQueryInput, + ReadExceptionListResponse, +} from './read_exception_list/read_exception_list.gen'; +import type { + UpdateExceptionListItemRequestBodyInput, + UpdateExceptionListItemResponse, +} from './update_exception_list_item/update_exception_list_item.gen'; +import type { + UpdateExceptionListRequestBodyInput, + UpdateExceptionListResponse, +} from './update_exception_list/update_exception_list.gen'; + +export interface ClientOptions { + kbnClient: KbnClient; + log: ToolingLog; +} + +export class Client { + readonly kbnClient: KbnClient; + readonly log: ToolingLog; + + constructor(options: ClientOptions) { + this.kbnClient = options.kbnClient; + this.log = options.log; + } + /** + * An exception list groups exception items and can be associated with detection rules. You can assign detection rules with multiple exception lists. +> info +> All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. + + */ + async createExceptionList(props: CreateExceptionListProps) { + this.log.info(`${new Date().toISOString()} Calling API CreateExceptionList`); + return this.kbnClient + .request<CreateExceptionListResponse>({ + path: '/api/exception_lists', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Create an exception item and associate it with the specified exception list. +> info +> Before creating exception items, you must create an exception list. + + */ + async createExceptionListItem(props: CreateExceptionListItemProps) { + this.log.info(`${new Date().toISOString()} Calling API CreateExceptionListItem`); + return this.kbnClient + .request<CreateExceptionListItemResponse>({ + path: '/api/exception_lists/items', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Create exception items that apply to a single detection rule. + */ + async createRuleExceptionListItems(props: CreateRuleExceptionListItemsProps) { + this.log.info(`${new Date().toISOString()} Calling API CreateRuleExceptionListItems`); + return this.kbnClient + .request<CreateRuleExceptionListItemsResponse>({ + path: replaceParams('/api/detection_engine/rules/{id}/exceptions', props.params), + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * An exception list groups exception items and can be associated with detection rules. A shared exception list can apply to multiple detection rules. +> info +> All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. + + */ + async createSharedExceptionList(props: CreateSharedExceptionListProps) { + this.log.info(`${new Date().toISOString()} Calling API CreateSharedExceptionList`); + return this.kbnClient + .request<CreateSharedExceptionListResponse>({ + path: '/api/exceptions/shared', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Delete an exception list using the `id` or `list_id` field. + */ + async deleteExceptionList(props: DeleteExceptionListProps) { + this.log.info(`${new Date().toISOString()} Calling API DeleteExceptionList`); + return this.kbnClient + .request<DeleteExceptionListResponse>({ + path: '/api/exception_lists', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'DELETE', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Delete an exception list item using the `id` or `item_id` field. + */ + async deleteExceptionListItem(props: DeleteExceptionListItemProps) { + this.log.info(`${new Date().toISOString()} Calling API DeleteExceptionListItem`); + return this.kbnClient + .request<DeleteExceptionListItemResponse>({ + path: '/api/exception_lists/items', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'DELETE', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Duplicate an existing exception list. + */ + async duplicateExceptionList(props: DuplicateExceptionListProps) { + this.log.info(`${new Date().toISOString()} Calling API DuplicateExceptionList`); + return this.kbnClient + .request<DuplicateExceptionListResponse>({ + path: '/api/exception_lists/_duplicate', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Export an exception list and its associated items to an NDJSON file. + */ + async exportExceptionList(props: ExportExceptionListProps) { + this.log.info(`${new Date().toISOString()} Calling API ExportExceptionList`); + return this.kbnClient + .request({ + path: '/api/exception_lists/_export', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Get a list of all exception list items in the specified list. + */ + async findExceptionListItems(props: FindExceptionListItemsProps) { + this.log.info(`${new Date().toISOString()} Calling API FindExceptionListItems`); + return this.kbnClient + .request<FindExceptionListItemsResponse>({ + path: '/api/exception_lists/items/_find', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Get a list of all exception lists. + */ + async findExceptionLists(props: FindExceptionListsProps) { + this.log.info(`${new Date().toISOString()} Calling API FindExceptionLists`); + return this.kbnClient + .request<FindExceptionListsResponse>({ + path: '/api/exception_lists/_find', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Import an exception list and its associated items from an NDJSON file. + */ + async importExceptionList(props: ImportExceptionListProps) { + this.log.info(`${new Date().toISOString()} Calling API ImportExceptionList`); + return this.kbnClient + .request<ImportExceptionListResponse>({ + path: '/api/exception_lists/_import', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + body: props.attachment, + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Get the details of an exception list using the `id` or `list_id` field. + */ + async readExceptionList(props: ReadExceptionListProps) { + this.log.info(`${new Date().toISOString()} Calling API ReadExceptionList`); + return this.kbnClient + .request<ReadExceptionListResponse>({ + path: '/api/exception_lists', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Get the details of an exception list item using the `id` or `item_id` field. + */ + async readExceptionListItem(props: ReadExceptionListItemProps) { + this.log.info(`${new Date().toISOString()} Calling API ReadExceptionListItem`); + return this.kbnClient + .request<ReadExceptionListItemResponse>({ + path: '/api/exception_lists/items', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Get a summary of the specified exception list. + */ + async readExceptionListSummary(props: ReadExceptionListSummaryProps) { + this.log.info(`${new Date().toISOString()} Calling API ReadExceptionListSummary`); + return this.kbnClient + .request<ReadExceptionListSummaryResponse>({ + path: '/api/exception_lists/summary', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Update an exception list using the `id` or `list_id` field. + */ + async updateExceptionList(props: UpdateExceptionListProps) { + this.log.info(`${new Date().toISOString()} Calling API UpdateExceptionList`); + return this.kbnClient + .request<UpdateExceptionListResponse>({ + path: '/api/exception_lists', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'PUT', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Update an exception list item using the `id` or `item_id` field. + */ + async updateExceptionListItem(props: UpdateExceptionListItemProps) { + this.log.info(`${new Date().toISOString()} Calling API UpdateExceptionListItem`); + return this.kbnClient + .request<UpdateExceptionListItemResponse>({ + path: '/api/exception_lists/items', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'PUT', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } +} + +export interface CreateExceptionListProps { + body: CreateExceptionListRequestBodyInput; +} +export interface CreateExceptionListItemProps { + body: CreateExceptionListItemRequestBodyInput; +} +export interface CreateRuleExceptionListItemsProps { + params: CreateRuleExceptionListItemsRequestParamsInput; + body: CreateRuleExceptionListItemsRequestBodyInput; +} +export interface CreateSharedExceptionListProps { + body: CreateSharedExceptionListRequestBodyInput; +} +export interface DeleteExceptionListProps { + query: DeleteExceptionListRequestQueryInput; +} +export interface DeleteExceptionListItemProps { + query: DeleteExceptionListItemRequestQueryInput; +} +export interface DuplicateExceptionListProps { + query: DuplicateExceptionListRequestQueryInput; +} +export interface ExportExceptionListProps { + query: ExportExceptionListRequestQueryInput; +} +export interface FindExceptionListItemsProps { + query: FindExceptionListItemsRequestQueryInput; +} +export interface FindExceptionListsProps { + query: FindExceptionListsRequestQueryInput; +} +export interface ImportExceptionListProps { + query: ImportExceptionListRequestQueryInput; + attachment: FormData; +} +export interface ReadExceptionListProps { + query: ReadExceptionListRequestQueryInput; +} +export interface ReadExceptionListItemProps { + query: ReadExceptionListItemRequestQueryInput; +} +export interface ReadExceptionListSummaryProps { + query: ReadExceptionListSummaryRequestQueryInput; +} +export interface UpdateExceptionListProps { + body: UpdateExceptionListRequestBodyInput; +} +export interface UpdateExceptionListItemProps { + body: UpdateExceptionListItemRequestBodyInput; +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.gen.ts new file mode 100644 index 000000000000..87db0f9e7562 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.gen.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Read exception list API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { + ExceptionListId, + ExceptionListHumanId, + ExceptionNamespaceType, + ExceptionList, +} from '../model/exception_list_common.gen'; + +export type ReadExceptionListRequestQuery = z.infer<typeof ReadExceptionListRequestQuery>; +export const ReadExceptionListRequestQuery = z.object({ + /** + * Either `id` or `list_id` must be specified + */ + id: ExceptionListId.optional(), + /** + * Either `id` or `list_id` must be specified + */ + list_id: ExceptionListHumanId.optional(), + namespace_type: ExceptionNamespaceType.optional().default('single'), +}); +export type ReadExceptionListRequestQueryInput = z.input<typeof ReadExceptionListRequestQuery>; + +export type ReadExceptionListResponse = z.infer<typeof ReadExceptionListResponse>; +export const ReadExceptionListResponse = ExceptionList; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.schema.yaml new file mode 100644 index 000000000000..e50147083daf --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.schema.yaml @@ -0,0 +1,70 @@ +openapi: 3.0.0 +info: + title: Read exception list API endpoint + version: '2023-10-31' +paths: + /api/exception_lists: + get: + x-labels: [serverless, ess] + operationId: ReadExceptionList + x-codegen-enabled: true + summary: Get exception list details + description: Get the details of an exception list using the `id` or `list_id` field. + parameters: + - name: id + in: query + required: false + description: Either `id` or `list_id` must be specified + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListId' + - name: list_id + in: query + required: false + description: Either `id` or `list_id` must be specified + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListHumanId' + - name: namespace_type + in: query + required: false + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' + default: single + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionList' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: Exception list item not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.gen.ts similarity index 75% rename from packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.gen.ts index 4de512301cd8..02f6d1055838 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.schema.yaml new file mode 100644 index 000000000000..6d7fac776718 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.schema.yaml @@ -0,0 +1,70 @@ +openapi: 3.0.0 +info: + title: Read exception list item API endpoint + version: '2023-10-31' +paths: + /api/exception_lists/items: + get: + x-labels: [serverless, ess] + operationId: ReadExceptionListItem + x-codegen-enabled: true + summary: Get an exception list item + description: Get the details of an exception list item using the `id` or `item_id` field. + parameters: + - name: id + in: query + required: false + description: Either `id` or `item_id` must be specified + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemId' + - name: item_id + in: query + required: false + description: Either `id` or `item_id` must be specified + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItemHumanId' + - name: namespace_type + in: query + required: false + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' + default: single + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListItem' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: Exception list item not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.gen.ts similarity index 79% rename from packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.gen.ts index 3e3230dddb0a..8807f4b7e781 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.schema.yaml new file mode 100644 index 000000000000..02fc3c9c8f6f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.schema.yaml @@ -0,0 +1,89 @@ +openapi: 3.0.0 +info: + title: Read exception list summary API endpoint + version: '2023-10-31' +paths: + /api/exception_lists/summary: + get: + x-labels: [serverless, ess] + operationId: ReadExceptionListSummary + x-codegen-enabled: true + summary: Get an exception list summary + description: Get a summary of the specified exception list. + parameters: + - name: id + in: query + required: false + description: Exception list's identifier generated upon creation + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListId' + - name: list_id + in: query + required: false + description: Exception list's human readable identifier + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionListHumanId' + - name: namespace_type + in: query + required: false + schema: + $ref: '../model/exception_list_common.schema.yaml#/components/schemas/ExceptionNamespaceType' + default: single + - name: filter + in: query + required: false + description: Search filter clause + schema: + type: string + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: object + properties: + windows: + type: integer + minimum: 0 + linux: + type: integer + minimum: 0 + macos: + type: integer + minimum: 0 + total: + type: integer + minimum: 0 + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: Exception list not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.gen.ts similarity index 79% rename from packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.gen.ts index 2f38661cc958..fb5fde05dcc8 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.schema.yaml similarity index 77% rename from packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.schema.yaml index 5e8f3dfd8b50..0f7218a86c23 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.schema.yaml @@ -59,29 +59,29 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 404: description: Exception list not found response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.gen.ts similarity index 86% rename from packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.gen.ts index 651c1dc1f2d4..791af5f65e35 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.schema.yaml similarity index 77% rename from packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.schema.yaml index 2b8182aeb5c3..9adc75141f56 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.schema.yaml @@ -70,32 +70,32 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 404: description: Exception list item not found response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' components: x-codegen-enabled: true @@ -104,9 +104,9 @@ components: type: object properties: id: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' comment: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' required: - comment diff --git a/packages/kbn-securitysolution-exceptions-common/docs/openapi/ess/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/docs/openapi/ess/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml similarity index 100% rename from packages/kbn-securitysolution-exceptions-common/docs/openapi/ess/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/docs/openapi/ess/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml diff --git a/packages/kbn-securitysolution-exceptions-common/docs/openapi/serverless/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/docs/openapi/serverless/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml similarity index 100% rename from packages/kbn-securitysolution-exceptions-common/docs/openapi/serverless/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/docs/openapi/serverless/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml diff --git a/packages/kbn-securitysolution-exceptions-common/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-exceptions-common/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/package.json new file mode 100644 index 000000000000..e68c27982b29 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/package.json @@ -0,0 +1,11 @@ +{ + "description": "Security Solution Exceptions common package", + "license": "Elastic License 2.0", + "name": "@kbn/securitysolution-exceptions-common", + "private": true, + "version": "1.0.0", + "scripts": { + "openapi:generate": "node scripts/openapi_generate", + "openapi:bundle": "node scripts/openapi_bundle" + } +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle.js b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle.js new file mode 100644 index 000000000000..996c55ce307d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle.js @@ -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. + */ + +require('../../../../../../src/setup_node_env'); +// eslint-disable-next-line import/no-nodejs-modules +const { join, resolve } = require('path'); +const { bundle } = require('@kbn/openapi-bundler'); + +const ROOT = resolve(__dirname, '..'); + +(async () => { + await bundle({ + sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), + outputFilePath: join( + ROOT, + 'docs/openapi/serverless/security_solution_exceptions_api_{version}.bundled.schema.yaml' + ), + options: { + includeLabels: ['serverless'], + prototypeDocument: join(ROOT, 'scripts/openapi_bundle_info/exceptions_serverless.info.yaml'), + }, + }); + + await bundle({ + sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), + outputFilePath: join( + ROOT, + 'docs/openapi/ess/security_solution_exceptions_api_{version}.bundled.schema.yaml' + ), + options: { + includeLabels: ['ess'], + prototypeDocument: join(ROOT, 'scripts/openapi_bundle_info/exceptions_ess.info.yaml'), + }, + }); +})(); diff --git a/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle_info/exceptions_ess.info.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle_info/exceptions_ess.info.yaml similarity index 100% rename from packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle_info/exceptions_ess.info.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle_info/exceptions_ess.info.yaml diff --git a/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle_info/exceptions_serverless.info.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle_info/exceptions_serverless.info.yaml similarity index 100% rename from packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle_info/exceptions_serverless.info.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle_info/exceptions_serverless.info.yaml diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_generate.js b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_generate.js new file mode 100644 index 000000000000..92488001b0b3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_generate.js @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +require('../../../../../../src/setup_node_env'); +// eslint-disable-next-line import/no-nodejs-modules +const { join, resolve } = require('path'); +const { generate } = require('@kbn/openapi-generator'); +const { REPO_ROOT } = require('@kbn/repo-info'); + +const ROOT = resolve(__dirname, '..'); + +(async () => { + await generate({ + title: 'OpenAPI Exceptions API Schemas', + rootDir: ROOT, + sourceGlob: './api/**/*.schema.yaml', + templateName: 'zod_operation_schema', + }); + + await generate({ + title: 'Exceptions API client for tests', + rootDir: ROOT, + sourceGlob: './api/**/*.schema.yaml', + templateName: 'api_client_supertest', + skipLinting: true, + bundle: { + outFile: join( + REPO_ROOT, + 'x-pack/test/api_integration/services/security_solution_exceptions_api.gen.ts' + ), + }, + }); + + await generate({ + title: 'Exceptions API client for quickstart', + rootDir: ROOT, + sourceGlob: './api/**/*.schema.yaml', + templateName: 'api_client_quickstart', + skipLinting: true, + bundle: { + outFile: join( + REPO_ROOT, + 'x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts' + ), + }, + }); +})(); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/tsconfig.json new file mode 100644 index 000000000000..ba5a2c115a33 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "outDir": "target/types", + "types": ["jest", "node"] + }, + "exclude": ["target/**/*"], + "extends": "../../../../../tsconfig.base.json", + "include": ["**/*.ts"], + "kbn_references": [ + "@kbn/openapi-common", + "@kbn/zod-helpers", + "@kbn/securitysolution-lists-common", + "@kbn/test", + "@kbn/tooling-log", + "@kbn/core-http-common", + "@kbn/securitysolution-utils", + "@kbn/zod", + ] +} diff --git a/packages/kbn-securitysolution-hook-utils/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/README.md similarity index 100% rename from packages/kbn-securitysolution-hook-utils/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/index.ts new file mode 100644 index 000000000000..40ff03e88094 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/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 * from './src/types'; +export * from './src/use_async'; +export * from './src/use_is_mounted'; +export * from './src/use_observable'; +export * from './src/with_optional_signal'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/jest.config.js b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/jest.config.js new file mode 100644 index 000000000000..8238f9f7e97d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils'], +}; diff --git a/packages/kbn-securitysolution-hook-utils/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-hook-utils/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/package.json new file mode 100644 index 000000000000..efd415370f23 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/securitysolution-hook-utils", + "version": "1.0.0", + "description": "Security Solution utilities for React hooks", + "license": "Elastic License 2.0", + "private": true, + "sideEffects": false +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/types.ts b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/types.ts new file mode 100644 index 000000000000..1b31cae2a16d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/types.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Represents the state of an asynchronous task, along with an initiator + * function to kick off the work. + */ +export interface Task<Args extends unknown[], Result> { + loading: boolean; + error: unknown | undefined; + result: Result | undefined; + start: (...args: Args) => void; +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_async/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_async/index.test.ts new file mode 100644 index 000000000000..e5420124a0eb --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_async/index.test.ts @@ -0,0 +1,164 @@ +/* + * 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 { waitFor, renderHook, act } from '@testing-library/react'; + +import { useAsync } from '.'; + +interface TestArgs { + n: number; + s: string; +} + +type TestReturn = Promise<unknown>; + +describe('useAsync', () => { + /** + * Timeout for both jest tests and for the waitFor. + * jest tests default to 5 seconds and waitFor defaults to 1 second. + * 20_0000 = 20,000 milliseconds = 20 seconds + */ + const timeout = 20_000; + + let fn: jest.Mock<TestReturn, TestArgs[]>; + let args: TestArgs; + + beforeEach(() => { + args = { n: 1, s: 's' }; + fn = jest.fn().mockResolvedValue(false); + }); + + it('does not invoke fn if start was not called', () => { + renderHook(() => useAsync(fn)); + expect(fn).not.toHaveBeenCalled(); + }); + + it( + 'invokes the function when start is called', + async () => { + const { result } = renderHook(() => useAsync(fn)); + + act(() => { + result.current.start(args); + }); + await waitFor(() => expect(fn).toHaveBeenCalled(), { timeout }); + }, + timeout + ); + + it('invokes the function with start args', async () => { + const { result } = renderHook(() => useAsync(fn)); + const expectedArgs = { ...args }; + + act(() => { + result.current.start(args); + }); + await waitFor(() => expect(fn).toHaveBeenCalledWith(expectedArgs), { timeout }); + }); + + it( + 'populates result with the resolved value of the fn', + async () => { + const { result } = renderHook(() => useAsync(fn)); + fn.mockResolvedValue({ resolved: 'value' }); + + act(() => { + result.current.start(args); + }); + await waitFor( + () => { + expect(result.current.result).toEqual({ resolved: 'value' }); + expect(result.current.error).toBeUndefined(); + }, + { timeout } + ); + }, + timeout + ); + + it( + 'populates error if function rejects', + async () => { + fn.mockRejectedValue(new Error('whoops')); + const { result } = renderHook(() => useAsync(fn)); + + act(() => { + result.current.start(args); + }); + + await waitFor( + () => { + expect(result.current.result).toBeUndefined(); + expect(result.current.error).toEqual(new Error('whoops')); + }, + { timeout } + ); + }, + timeout + ); + + it( + 'populates the loading state while the function is pending', + async () => { + let resolve: () => void; + fn.mockImplementation(() => new Promise<void>((_resolve) => (resolve = _resolve))); + + const { result } = renderHook(() => useAsync(fn)); + + act(() => { + result.current.start(args); + }); + + expect(result.current.loading).toBe(true); + + act(() => resolve()); + await waitFor(() => expect(result.current.loading).toBe(false), { timeout }); + }, + timeout + ); + + it( + 'multiple start calls reset state', + async () => { + let resolve: (result: string) => void; + fn.mockImplementation(() => new Promise((_resolve) => (resolve = _resolve))); + + const { result } = renderHook(() => useAsync(fn)); + + act(() => { + result.current.start(args); + }); + + expect(result.current.loading).toBe(true); + + act(() => resolve('result')); + await waitFor( + () => { + expect(result.current.loading).toBe(false); + expect(result.current.result).toBe('result'); + }, + { timeout } + ); + + act(() => { + result.current.start(args); + }); + + expect(result.current.loading).toBe(true); + expect(result.current.result).toBe(undefined); + act(() => resolve('result')); + await waitFor( + () => { + expect(result.current.loading).toBe(false); + expect(result.current.result).toBe('result'); + }, + { timeout } + ); + }, + timeout + ); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_async/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_async/index.ts new file mode 100644 index 000000000000..027039ac7cca --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_async/index.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useState } from 'react'; + +import { Task } from '../types'; +import { useIsMounted } from '../use_is_mounted'; + +/** + * + * This hook wraps a promise-returning thunk (task) in order to conditionally + * initiate the work, and automatically provide state corresponding to the + * task's status. + * + * In order to function properly and not rerender unnecessarily, ensure that + * your task is a stable function reference. + * + * @param fn a function returning a promise. + * + * @returns An {@link Task} containing the task's current state along with a + * start callback + */ +export const useAsync = <Args extends unknown[], Result>( + fn: (...args: Args) => Promise<Result> +): Task<Args, Result> => { + const isMounted = useIsMounted(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState<unknown | undefined>(); + const [result, setResult] = useState<Result | undefined>(); + + const start = useCallback( + (...args: Args) => { + setLoading(true); + setResult(undefined); + setError(undefined); + fn(...args) + .then((r) => isMounted() && setResult(r)) + .catch((e) => isMounted() && setError(e)) + .finally(() => isMounted() && setLoading(false)); + }, + [fn, isMounted] + ); + + return { + error, + loading, + result, + start, + }; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.test.ts new file mode 100644 index 000000000000..52f3a0739f4a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.test.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react'; + +import { useIsMounted } from '.'; + +describe('useIsMounted', () => { + it('evaluates to true when mounted', () => { + const { result } = renderHook(() => useIsMounted()); + + expect(result.current()).toEqual(true); + }); + + it('evaluates to false when unmounted', () => { + const { result, unmount } = renderHook(() => useIsMounted()); + + unmount(); + expect(result.current()).toEqual(false); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.ts new file mode 100644 index 000000000000..8d325aed1abd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_is_mounted/index.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 { useCallback, useEffect, useRef } from 'react'; + +type GetIsMounted = () => boolean; + +/** + * + * @returns A {@link GetIsMounted} getter function returning whether the component is currently mounted + */ +export const useIsMounted = (): GetIsMounted => { + const isMounted = useRef(false); + const getIsMounted: GetIsMounted = useCallback(() => isMounted.current, []); + const handleCleanup = useCallback(() => { + isMounted.current = false; + }, []); + + useEffect(() => { + isMounted.current = true; + return handleCleanup; + }, [handleCleanup]); + + return getIsMounted; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_observable/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_observable/index.test.ts new file mode 100644 index 000000000000..3c6d96e3d3ac --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_observable/index.test.ts @@ -0,0 +1,159 @@ +/* + * 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 { renderHook, act } from '@testing-library/react'; +import { Subject, throwError } from 'rxjs'; + +import { useObservable } from '.'; + +interface TestArgs { + n: number; + s: string; +} + +type TestReturn = Subject<unknown>; + +describe('useObservable', () => { + let fn: jest.Mock<TestReturn, TestArgs[]>; + let subject: TestReturn; + let args: TestArgs; + + beforeEach(() => { + args = { n: 1, s: 's' }; + subject = new Subject(); + fn = jest.fn().mockReturnValue(subject); + }); + + it('does not invoke fn if start was not called', () => { + renderHook(() => useObservable(fn)); + expect(fn).not.toHaveBeenCalled(); + }); + + it('invokes the function when start is called', () => { + const { result } = renderHook(() => useObservable(fn)); + + act(() => { + result.current.start(args); + }); + + expect(fn).toHaveBeenCalled(); + }); + + it('invokes the function with start args', () => { + const { result } = renderHook(() => useObservable(fn)); + const expectedArgs = { ...args }; + + act(() => { + result.current.start(args); + }); + + expect(fn).toHaveBeenCalledWith(expectedArgs); + }); + + it('populates result with the next value of the fn', () => { + const { result } = renderHook(() => useObservable(fn)); + + act(() => { + result.current.start(args); + }); + act(() => subject.next('value')); + + expect(result.current.result).toEqual('value'); + expect(result.current.error).toBeUndefined(); + }); + + it('populates error if observable throws an error', () => { + const error = new Error('whoops'); + const errorFn = () => throwError(error); + + const { result } = renderHook(() => useObservable(errorFn)); + + act(() => { + result.current.start(); + }); + + expect(result.current.result).toBeUndefined(); + expect(result.current.error).toEqual(error); + }); + + it('populates the loading state while no value has resolved', () => { + const { result } = renderHook(() => useObservable(fn)); + + act(() => { + result.current.start(args); + }); + + expect(result.current.loading).toBe(true); + + act(() => subject.next('a value')); + + expect(result.current.loading).toBe(false); + }); + + it('updates result with each resolved value', () => { + const { result } = renderHook(() => useObservable(fn)); + + act(() => { + result.current.start(args); + }); + + act(() => subject.next('a value')); + expect(result.current.result).toEqual('a value'); + + act(() => subject.next('a subsequent value')); + expect(result.current.result).toEqual('a subsequent value'); + }); + + it('does not update result with values if start has not been called', () => { + const { result } = renderHook(() => useObservable(fn)); + + act(() => subject.next('a value')); + expect(result.current.result).toBeUndefined(); + + act(() => subject.next('a subsequent value')); + expect(result.current.result).toBeUndefined(); + }); + + it('unsubscribes on unmount', () => { + const { result, unmount } = renderHook(() => useObservable(fn)); + + act(() => { + result.current.start(args); + }); + expect(subject.observers).toHaveLength(1); + + unmount(); + expect(subject.observers).toHaveLength(0); + }); + + it('multiple start calls reset state', () => { + const { result } = renderHook(() => useObservable(fn)); + + act(() => { + result.current.start(args); + }); + + expect(result.current.loading).toBe(true); + + act(() => subject.next('one value')); + + expect(result.current.loading).toBe(false); + expect(result.current.result).toBe('one value'); + + act(() => { + result.current.start(args); + }); + + expect(result.current.loading).toBe(true); + expect(result.current.result).toBe(undefined); + + act(() => subject.next('another value')); + + expect(result.current.loading).toBe(false); + expect(result.current.result).toBe('another value'); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_observable/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_observable/index.ts new file mode 100644 index 000000000000..b7cd1e2623b7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/use_observable/index.ts @@ -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 { useCallback, useEffect, useRef, useReducer, Reducer } from 'react'; +import { Observable, Subscription } from 'rxjs'; + +import { useIsMounted } from '../use_is_mounted'; +import { Task } from '../types'; + +interface State<T> { + loading: boolean; + error?: unknown; + result?: T; +} + +export type Action<T> = + | { type: 'setResult'; result: T } + | { type: 'setError'; error: unknown } + | { type: 'load' }; + +function reducer<T>(state: State<T>, action: Action<T>) { + switch (action.type) { + case 'setResult': + return { ...state, result: action.result, loading: false }; + case 'setError': + return { ...state, error: action.error, loading: false }; + case 'load': + return { loading: true, result: undefined, error: undefined }; + } +} + +/** + * + * @param fn function returning an observable + * + * @returns An {@link Async} containing the underlying task's state along with a start callback + */ +export const useObservable = <Args extends unknown[], Result>( + fn: (...args: Args) => Observable<Result> +): Task<Args, Result> => { + const isMounted = useIsMounted(); + const subRef = useRef<Subscription | undefined>(); + const [state, dispatch] = useReducer<Reducer<State<Result>, Action<Result>>>(reducer, { + loading: false, + error: undefined, + result: undefined, + }); + + const start = useCallback( + (...args: Args) => { + if (subRef.current) { + subRef.current.unsubscribe(); + } + dispatch({ type: 'load' }); + + subRef.current = fn(...args).subscribe( + (r) => { + if (isMounted()) { + dispatch({ type: 'setResult', result: r }); + } + }, + (e) => { + if (isMounted()) { + dispatch({ type: 'setError', error: e }); + } + } + ); + }, + [fn, isMounted] + ); + + useEffect( + () => () => { + if (subRef.current) { + subRef.current.unsubscribe(); + } + }, + [] + ); + + return { + result: state.result, + error: state.error, + loading: state.loading, + start, + }; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.test.ts new file mode 100644 index 000000000000..05a1b2fa167a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.test.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 { withOptionalSignal } from '.'; + +type TestFn = ({ number, signal }: { number: number; signal: AbortSignal }) => boolean; + +describe('withOptionalSignal', () => { + it('does not require a signal on the returned function', () => { + const fn = jest.fn().mockReturnValue('hello') as TestFn; + + const wrappedFn = withOptionalSignal(fn); + + expect(wrappedFn({ number: 1 })).toEqual('hello'); + }); + + it('will pass a given signal to the wrapped function', () => { + const fn = jest.fn().mockReturnValue('hello') as TestFn; + const { signal } = new AbortController(); + + const wrappedFn = withOptionalSignal(fn); + + wrappedFn({ number: 1, signal }); + expect(fn).toHaveBeenCalledWith({ number: 1, signal }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.ts new file mode 100644 index 000000000000..eadad58804f8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/src/with_optional_signal/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +interface SignalArgs { + signal: AbortSignal; +} + +export type OptionalSignalArgs<Args> = Omit<Args, 'signal'> & Partial<SignalArgs>; + +/** + * + * @param fn an async function receiving an AbortSignal argument + * + * @returns An async function where the AbortSignal argument is optional + */ +export const withOptionalSignal = + <Args extends SignalArgs, Result>(fn: (args: Args) => Result) => + (args: OptionalSignalArgs<Args>): Result => { + const signal = args.signal != null ? args.signal : new AbortController().signal; + return fn({ ...args, signal } as Args); + }; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/tsconfig.json new file mode 100644 index 000000000000..ccdf26c349f0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-hook-utils/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": ["jest", "node"] + }, + "include": [ + "**/*.ts" + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/README.md similarity index 100% rename from packages/kbn-securitysolution-io-ts-alerting-types/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/index.ts new file mode 100644 index 000000000000..f5e8f8419b25 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/index.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './src/actions'; +export * from './src/default_actions_array'; +export * from './src/default_export_file_name'; +export * from './src/default_from_string'; +export * from './src/default_interval_string'; +export * from './src/default_language_string'; +export * from './src/default_max_signals_number'; +export * from './src/default_page'; +export * from './src/default_per_page'; +export * from './src/default_risk_score_mapping_array'; +export * from './src/default_severity_mapping_array'; +export * from './src/default_threat_array'; +export * from './src/default_to_string'; +export * from './src/default_uuid'; +export * from './src/frequency'; +export * from './src/language'; +export * from './src/machine_learning_job_id'; +export * from './src/max_signals'; +export * from './src/normalized_ml_job_id'; +export * from './src/references_default_array'; +export * from './src/risk_score'; +export * from './src/risk_score_mapping'; +export * from './src/rule_schedule'; +export * from './src/saved_object_attributes'; +export * from './src/severity'; +export * from './src/severity_mapping'; +export * from './src/threat'; +export * from './src/threat_mapping'; +export * from './src/threat_subtechnique'; +export * from './src/threat_tactic'; +export * from './src/threat_technique'; +export * from './src/throttle'; +export * from './src/type'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/jest.config.js b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/jest.config.js new file mode 100644 index 000000000000..16223422cbd8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types'], +}; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-io-ts-alerting-types/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/package.json new file mode 100644 index 000000000000..bbe6c163fb5e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/securitysolution-io-ts-alerting-types", + "version": "1.0.0", + "description": "io ts utilities and types to be shared with plugins from the security solution project", + "license": "Elastic License 2.0", + "private": true, + "sideEffects": false +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts new file mode 100644 index 000000000000..64953a0dc0cd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts @@ -0,0 +1,127 @@ +/* + * 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 { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; + +import * as t from 'io-ts'; +import { saved_object_attributes } from '../saved_object_attributes'; +import { RuleActionFrequency } from '../frequency'; + +export type RuleActionGroup = t.TypeOf<typeof RuleActionGroup>; +export const RuleActionGroup = t.string; + +export type RuleActionId = t.TypeOf<typeof RuleActionId>; +export const RuleActionId = t.string; + +export type RuleActionTypeId = t.TypeOf<typeof RuleActionTypeId>; +export const RuleActionTypeId = t.string; + +export type RuleActionUuid = t.TypeOf<typeof RuleActionUuid>; +export const RuleActionUuid = NonEmptyString; + +/** + * Params is an "object", since it is a type of RuleActionParams which is action templates. + * @see x-pack/plugins/alerting/common/rule.ts + */ +export type RuleActionParams = t.TypeOf<typeof RuleActionParams>; +export const RuleActionParams = saved_object_attributes; + +export const RuleActionAlertsFilter = t.partial({ + query: t.union([ + t.undefined, + t.intersection([ + t.strict({ + kql: t.string, + filters: t.array( + t.intersection([ + t.type({ + meta: t.partial({ + alias: t.union([t.string, t.null]), + disabled: t.boolean, + negate: t.boolean, + controlledBy: t.string, + group: t.string, + index: t.string, + isMultiIndex: t.boolean, + type: t.string, + key: t.string, + params: t.any, + value: t.string, + }), + }), + t.partial({ + $state: t.type({ store: t.any }), + query: t.record(t.string, t.any), + }), + ]) + ), + }), + t.partial({ dsl: t.string }), + ]), + ]), + timeframe: t.union([ + t.undefined, + t.strict({ + timezone: t.string, + days: t.array( + t.union([ + t.literal(1), + t.literal(2), + t.literal(3), + t.literal(4), + t.literal(5), + t.literal(6), + t.literal(7), + ]) + ), + hours: t.strict({ + start: t.string, + end: t.string, + }), + }), + ]), +}); + +export type RuleAction = t.TypeOf<typeof RuleAction>; +export const RuleAction = t.exact( + t.intersection([ + t.type({ + group: RuleActionGroup, + id: RuleActionId, + action_type_id: RuleActionTypeId, + params: RuleActionParams, + }), + t.partial({ + uuid: RuleActionUuid, + alerts_filter: RuleActionAlertsFilter, + frequency: RuleActionFrequency, + }), + ]) +); + +export type RuleActionArray = t.TypeOf<typeof RuleActionArray>; +export const RuleActionArray = t.array(RuleAction); + +export type RuleActionCamel = t.TypeOf<typeof RuleActionCamel>; +export const RuleActionCamel = t.exact( + t.intersection([ + t.type({ + group: RuleActionGroup, + id: RuleActionId, + actionTypeId: RuleActionTypeId, + params: RuleActionParams, + }), + t.partial({ + uuid: RuleActionUuid, + alertsFilter: RuleActionAlertsFilter, + frequency: RuleActionFrequency, + }), + ]) +); + +export type RuleActionArrayCamel = t.TypeOf<typeof RuleActionArrayCamel>; +export const RuleActionArrayCamel = t.array(RuleActionCamel); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.mock.ts new file mode 100644 index 000000000000..2747846c74d8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.mock.ts @@ -0,0 +1,24 @@ +/* + * 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 ENTRY_VALUE = 'some host name'; +export const FIELD = 'host.name'; +export const MATCH = 'match'; +export const MATCH_ANY = 'match_any'; +export const OPERATOR = 'included'; +export const NESTED = 'nested'; +export const NESTED_FIELD = 'parent.field'; +export const LIST_ID = 'some-list-id'; +export const LIST = 'list'; +export const TYPE = 'ip'; +export const EXISTS = 'exists'; +export const WILDCARD = 'wildcard'; +export const USER = 'some user'; +export const DATE_NOW = '2020-04-20T15:25:31.830Z'; + +// Exception List specific +export const ID = 'uuid_here'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.ts new file mode 100644 index 000000000000..ad5a2ba26226 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/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. + */ + +/** + * TODO: Create a kbn-alerting-constants and add this to it. + * @deprecated Use a DEFAULT_MAX_SIGNALS from a kbn-alerting-constants package. + */ +export const DEFAULT_MAX_SIGNALS = 100; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_actions_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_actions_array/index.ts new file mode 100644 index 000000000000..28ff3053564c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_actions_array/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { RuleActionArray } from '../actions'; + +export const DefaultActionsArray = new t.Type< + RuleActionArray, + RuleActionArray | undefined, + unknown +>( + 'DefaultActionsArray', + RuleActionArray.is, + (input, context): Either<t.Errors, RuleActionArray> => + input == null ? t.success([]) : RuleActionArray.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts new file mode 100644 index 000000000000..009fdb23d3fb --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts @@ -0,0 +1,42 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultExportFileName } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_export_file_name', () => { + test('it should validate a regular string', () => { + const payload = 'some string'; + const decoded = DefaultExportFileName.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultExportFileName.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultExportFileName"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of "export.ndjson"', () => { + const payload = null; + const decoded = DefaultExportFileName.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('export.ndjson'); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.ts new file mode 100644 index 000000000000..53f0972baca2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultExportFileName as: + * - If null or undefined, then a default of "export.ndjson" will be used + */ +export const DefaultExportFileName = new t.Type<string, string | undefined, unknown>( + 'DefaultExportFileName', + t.string.is, + (input, context): Either<t.Errors, string> => + input == null ? t.success('export.ndjson') : t.string.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts new file mode 100644 index 000000000000..f6c7f91e315a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts @@ -0,0 +1,42 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultFromString } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_from_string', () => { + test('it should validate a from string', () => { + const payload = 'now-20m'; + const decoded = DefaultFromString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultFromString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultFromString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of "now-6m"', () => { + const payload = null; + const decoded = DefaultFromString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('now-6m'); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.ts new file mode 100644 index 000000000000..d23bca135d03 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { From } from '../from'; + +/** + * Types the DefaultFromString as: + * - If null or undefined, then a default of the string "now-6m" will be used + */ +export const DefaultFromString = new t.Type<string, string | undefined, unknown>( + 'DefaultFromString', + t.string.is, + (input, context): Either<t.Errors, string> => { + if (input == null) { + return t.success('now-6m'); + } + return From.validate(input, context); + }, + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts new file mode 100644 index 000000000000..602d6c9ff79e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts @@ -0,0 +1,42 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultIntervalString } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_interval_string', () => { + test('it should validate a interval string', () => { + const payload = '20m'; + const decoded = DefaultIntervalString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultIntervalString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultIntervalString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of "5m"', () => { + const payload = null; + const decoded = DefaultIntervalString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('5m'); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.ts new file mode 100644 index 000000000000..d743fa773f2e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultIntervalString as: + * - If null or undefined, then a default of the string "5m" will be used + */ +export const DefaultIntervalString = new t.Type<string, string | undefined, unknown>( + 'DefaultIntervalString', + t.string.is, + (input, context): Either<t.Errors, string> => + input == null ? t.success('5m') : t.string.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts new file mode 100644 index 000000000000..34e6e65518c7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { Language } from '../language'; +import { DefaultLanguageString } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_language_string', () => { + test('it should validate a string', () => { + const payload: Language = 'lucene'; + const decoded = DefaultLanguageString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultLanguageString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultLanguageString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of "kuery"', () => { + const payload = null; + const decoded = DefaultLanguageString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('kuery'); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.ts new file mode 100644 index 000000000000..ca557dafecef --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { language } from '../language'; + +/** + * Types the DefaultLanguageString as: + * - If null or undefined, then a default of the string "kuery" will be used + */ +export const DefaultLanguageString = new t.Type<string, string | undefined, unknown>( + 'DefaultLanguageString', + t.string.is, + (input, context): Either<t.Errors, string> => + input == null ? t.success('kuery') : language.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts new file mode 100644 index 000000000000..cd4b0aa42953 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts @@ -0,0 +1,65 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultMaxSignalsNumber } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { DEFAULT_MAX_SIGNALS } from '../constants'; + +describe('default_from_string', () => { + test('it should validate a max signal number', () => { + const payload = 5; + const decoded = DefaultMaxSignalsNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a string', () => { + const payload = '5'; + const decoded = DefaultMaxSignalsNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultMaxSignals"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a zero', () => { + const payload = 0; + const decoded = DefaultMaxSignalsNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "DefaultMaxSignals"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a negative number', () => { + const payload = -1; + const decoded = DefaultMaxSignalsNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "DefaultMaxSignals"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of DEFAULT_MAX_SIGNALS', () => { + const payload = null; + const decoded = DefaultMaxSignalsNumber.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(DEFAULT_MAX_SIGNALS); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.ts new file mode 100644 index 000000000000..535f9397c7af --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { max_signals } from '../max_signals'; +import { DEFAULT_MAX_SIGNALS } from '../constants'; + +/** + * Types the default max signal: + * - Natural Number (positive integer and not a float), + * - greater than 1 + * - If undefined then it will use DEFAULT_MAX_SIGNALS (100) as the default + */ +export const DefaultMaxSignalsNumber = new t.Type<number, number | undefined, unknown>( + 'DefaultMaxSignals', + t.number.is, + (input, context): Either<t.Errors, number> => { + return input == null ? t.success(DEFAULT_MAX_SIGNALS) : max_signals.validate(input, context); + }, + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts new file mode 100644 index 000000000000..d9e63e0f9ec1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultPage } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_page', () => { + test('it should validate a regular number greater than zero', () => { + const payload = 5; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a string of a number', () => { + const payload = '5'; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(5); + }); + + test('it should not validate a junk string', () => { + const payload = 'invalid-string'; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "NaN" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate an empty string', () => { + const payload = ''; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "NaN" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a zero', () => { + const payload = 0; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a negative number', () => { + const payload = -1; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of 20', () => { + const payload = null; + const decoded = DefaultPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(1); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.ts new file mode 100644 index 000000000000..6ae23dd8c676 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.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 * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; + +/** + * Types the DefaultPerPage as: + * - If a string this will convert the string to a number + * - If null or undefined, then a default of 1 will be used + * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero + */ +export const DefaultPage = new t.Type<number, number | undefined, unknown>( + 'DefaultPerPage', + t.number.is, + (input, context): Either<t.Errors, number> => { + if (input == null) { + return t.success(1); + } else if (typeof input === 'string') { + return PositiveIntegerGreaterThanZero.validate(parseInt(input, 10), context); + } else { + return PositiveIntegerGreaterThanZero.validate(input, context); + } + }, + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts new file mode 100644 index 000000000000..07a6358ba2e8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultPerPage } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_per_page', () => { + test('it should validate a regular number greater than zero', () => { + const payload = 5; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a string of a number', () => { + const payload = '5'; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(5); + }); + + test('it should not validate a junk string', () => { + const payload = 'invalid-string'; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "NaN" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate an empty string', () => { + const payload = ''; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "NaN" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a zero', () => { + const payload = 0; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate a negative number', () => { + const payload = -1; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "DefaultPerPage"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of 20', () => { + const payload = null; + const decoded = DefaultPerPage.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(20); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.ts new file mode 100644 index 000000000000..320945311d3b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.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 * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; + +/** + * Types the DefaultPerPage as: + * - If a string this will convert the string to a number + * - If null or undefined, then a default of 20 will be used + * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero + */ +export const DefaultPerPage = new t.Type<number, number | undefined, unknown>( + 'DefaultPerPage', + t.number.is, + (input, context): Either<t.Errors, number> => { + if (input == null) { + return t.success(20); + } else if (typeof input === 'string') { + return PositiveIntegerGreaterThanZero.validate(parseInt(input, 10), context); + } else { + return PositiveIntegerGreaterThanZero.validate(input, context); + } + }, + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_risk_score_mapping_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_risk_score_mapping_array/index.ts new file mode 100644 index 000000000000..a457c827e25f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_risk_score_mapping_array/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { RiskScoreMapping } from '../risk_score_mapping'; + +/** + * Types the DefaultStringArray as: + * - If null or undefined, then a default RiskScoreMapping array will be set + */ +export const DefaultRiskScoreMappingArray = new t.Type< + RiskScoreMapping, + RiskScoreMapping | undefined, + unknown +>( + 'DefaultRiskScoreMappingArray', + RiskScoreMapping.is, + (input, context): Either<t.Errors, RiskScoreMapping> => + input == null ? t.success([]) : RiskScoreMapping.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_severity_mapping_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_severity_mapping_array/index.ts new file mode 100644 index 000000000000..8d7b581146ea --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_severity_mapping_array/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { SeverityMapping } from '../severity_mapping'; + +/** + * Types the DefaultStringArray as: + * - If null or undefined, then a default SeverityMapping array will be set + */ +export const DefaultSeverityMappingArray = new t.Type< + SeverityMapping, + SeverityMapping | undefined, + unknown +>( + 'DefaultSeverityMappingArray', + SeverityMapping.is, + (input, context): Either<t.Errors, SeverityMapping> => + input == null ? t.success([]) : SeverityMapping.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts new file mode 100644 index 000000000000..ed42de41effc --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts @@ -0,0 +1,65 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { Threats } from '../threat'; +import { DefaultThreatArray } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_threat_null', () => { + test('it should validate an empty array', () => { + const payload: Threats = []; + const decoded = DefaultThreatArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of threats', () => { + const payload: Threats = [ + { + framework: 'MITRE ATTACK', + technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA000999' }, + }, + ]; + const decoded = DefaultThreatArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array with a number', () => { + const payload = [ + { + framework: 'MITRE ATTACK', + technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA000999' }, + }, + 5, + ]; + const decoded = DefaultThreatArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultThreatArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default empty array if not provided a value', () => { + const payload = null; + const decoded = DefaultThreatArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.ts new file mode 100644 index 000000000000..8ed8abc58750 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { threats, Threats } from '../threat'; + +/** + * Types the DefaultThreatArray as: + * - If null or undefined, then an empty array will be set + */ +export const DefaultThreatArray = new t.Type<Threats, Threats | undefined, unknown>( + 'DefaultThreatArray', + threats.is, + (input, context): Either<t.Errors, Threats> => + input == null ? t.success([]) : threats.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts new file mode 100644 index 000000000000..f87b1c4f24a9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts @@ -0,0 +1,42 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultToString } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_to_string', () => { + test('it should validate a to string', () => { + const payload = 'now-5m'; + const decoded = DefaultToString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultToString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultToString"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of "now"', () => { + const payload = null; + const decoded = DefaultToString.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('now'); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.ts new file mode 100644 index 000000000000..d2e58f423b59 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultToString as: + * - If null or undefined, then a default of the string "now" will be used + */ +export const DefaultToString = new t.Type<string, string | undefined, unknown>( + 'DefaultToString', + t.string.is, + (input, context): Either<t.Errors, string> => + input == null ? t.success('now') : t.string.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts new file mode 100644 index 000000000000..ee605a5c4383 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts @@ -0,0 +1,42 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultUuid } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_uuid', () => { + test('it should validate a regular string', () => { + const payload = '1'; + const decoded = DefaultUuid.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate a number', () => { + const payload = 5; + const decoded = DefaultUuid.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "DefaultUuid"']); + expect(message.schema).toEqual({}); + }); + + test('it should return a default of a uuid', () => { + const payload = null; + const decoded = DefaultUuid.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i + ); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts new file mode 100644 index 000000000000..b3d6fda5b134 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { v4 as uuidv4 } from 'uuid'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; + +/** + * Types the DefaultUuid as: + * - If null or undefined, then a default string uuidv4() will be + * created otherwise it will be checked just against an empty string + */ +export const DefaultUuid = new t.Type<string, string | undefined, unknown>( + 'DefaultUuid', + t.string.is, + (input, context): Either<t.Errors, string> => + input == null ? t.success(uuidv4()) : NonEmptyString.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts new file mode 100644 index 000000000000..318408f14582 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { RuleActionThrottle } from '../throttle'; + +/** + * Action summary indicates whether we will send a summary notification about all the generate alerts or notification per individual alert + */ +export type RuleActionSummary = t.TypeOf<typeof RuleActionSummary>; +export const RuleActionSummary = t.boolean; + +/** + * The condition for throttling the notification: `onActionGroupChange`, `onActiveAlert`, or `onThrottleInterval` + */ +export type RuleActionNotifyWhen = t.TypeOf<typeof RuleActionNotifyWhen>; +export const RuleActionNotifyWhen = t.union([ + t.literal('onActionGroupChange'), + t.literal('onActiveAlert'), + t.literal('onThrottleInterval'), +]); + +/** + * The action frequency defines when the action runs (for example, only on rule execution or at specific time intervals). + */ +export type RuleActionFrequency = t.TypeOf<typeof RuleActionFrequency>; +export const RuleActionFrequency = t.type({ + summary: RuleActionSummary, + notifyWhen: RuleActionNotifyWhen, + throttle: t.union([RuleActionThrottle, t.null]), +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts new file mode 100644 index 000000000000..d0330fae6140 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Either } from 'fp-ts/lib/Either'; +import * as t from 'io-ts'; +import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; + +const stringValidator = (input: unknown): input is string => typeof input === 'string'; + +export type From = t.TypeOf<typeof From>; +export const From = new t.Type<string, string, unknown>( + 'From', + t.string.is, + (input, context): Either<t.Errors, string> => { + if (stringValidator(input) && parseScheduleDates(input) == null) { + return t.failure(input, context, 'Failed to parse "from" on rule param'); + } + return t.string.validate(input, context); + }, + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/language/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/language/index.ts new file mode 100644 index 000000000000..63ad102a4134 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/language/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const language = t.keyof({ eql: null, kuery: null, lucene: null, esql: null }); +export type Language = t.TypeOf<typeof language>; + +export const languageOrUndefined = t.union([language, t.undefined]); +export type LanguageOrUndefined = t.TypeOf<typeof languageOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/machine_learning_job_id/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/machine_learning_job_id/index.ts new file mode 100644 index 000000000000..8e6453ac7e7b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/machine_learning_job_id/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +import { machine_learning_job_id_normalized } from '../normalized_ml_job_id'; + +export const machine_learning_job_id = t.union([t.string, machine_learning_job_id_normalized]); +export type MachineLearningJobId = t.TypeOf<typeof machine_learning_job_id>; + +export const machineLearningJobIdOrUndefined = t.union([machine_learning_job_id, t.undefined]); +export type MachineLearningJobIdOrUndefined = t.TypeOf<typeof machineLearningJobIdOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts new file mode 100644 index 000000000000..64ded4827b5e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; + +export const max_signals = PositiveIntegerGreaterThanZero; +export type MaxSignals = t.TypeOf<typeof max_signals>; + +export const maxSignalsOrUndefined = t.union([max_signals, t.undefined]); +export type MaxSignalsOrUndefined = t.TypeOf<typeof maxSignalsOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.ts new file mode 100644 index 000000000000..f81c3da1db5d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +import { NonEmptyArray } from '@kbn/securitysolution-io-ts-types'; + +export const machine_learning_job_id_normalized = NonEmptyArray(t.string); +export type MachineLearningJobIdNormalized = t.TypeOf<typeof machine_learning_job_id_normalized>; + +export const machineLearningJobIdNormalizedOrUndefined = t.union([ + machine_learning_job_id_normalized, + t.undefined, +]); +export type MachineLearningJobIdNormalizedOrUndefined = t.TypeOf< + typeof machineLearningJobIdNormalizedOrUndefined +>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts new file mode 100644 index 000000000000..7c5e9bb29877 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { ReferencesDefaultArray } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_string_array', () => { + test('it should validate an empty array', () => { + const payload: string[] = []; + const decoded = ReferencesDefaultArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of strings', () => { + const payload = ['value 1', 'value 2']; + const decoded = ReferencesDefaultArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array with a number', () => { + const payload = ['value 1', 5]; + const decoded = ReferencesDefaultArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "referencesWithDefaultArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = ReferencesDefaultArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.ts new file mode 100644 index 000000000000..e5ccac365043 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the ReferencesDefaultArray as: + * - If null or undefined, then a default array will be set + */ +export const ReferencesDefaultArray = new t.Type<string[], string[] | undefined, unknown>( + 'referencesWithDefaultArray', + t.array(t.string).is, + (input, context): Either<t.Errors, string[]> => + input == null ? t.success([]) : t.array(t.string).validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts new file mode 100644 index 000000000000..b55ef415fbbb --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts @@ -0,0 +1,60 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { RiskScore } from '.'; + +describe('risk_score', () => { + test('it should validate a positive number', () => { + const payload = 1; + const decoded = RiskScore.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a zero', () => { + const payload = 0; + const decoded = RiskScore.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate a negative number', () => { + const payload = -1; + const decoded = RiskScore.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "-1" supplied to "RiskScore"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a string', () => { + const payload = 'some string'; + const decoded = RiskScore.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "RiskScore"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a risk score greater than 100', () => { + const payload = 101; + const decoded = RiskScore.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "101" supplied to "RiskScore"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts new file mode 100644 index 000000000000..143ea2e72754 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the risk score as: + * - Natural Number (positive integer and not a float), + * - Between the values [0 and 100] inclusive. + */ +export type RiskScore = t.TypeOf<typeof RiskScore>; +export const RiskScore = new t.Type<number, number, unknown>( + 'RiskScore', + t.number.is, + (input, context): Either<t.Errors, number> => { + return typeof input === 'number' && Number.isSafeInteger(input) && input >= 0 && input <= 100 + ? t.success(input) + : t.failure(input, context); + }, + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.ts new file mode 100644 index 000000000000..0856806d4898 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.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 * as t from 'io-ts'; +import { operator } from '@kbn/securitysolution-io-ts-types'; +import { RiskScore } from '../risk_score'; + +export type RiskScoreMappingItem = t.TypeOf<typeof RiskScoreMappingItem>; +export const RiskScoreMappingItem = t.exact( + t.type({ + field: t.string, + value: t.string, + operator, + risk_score: t.union([RiskScore, t.undefined]), + }) +); + +export type RiskScoreMapping = t.TypeOf<typeof RiskScoreMapping>; +export const RiskScoreMapping = t.array(RiskScoreMappingItem); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/rule_schedule/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/rule_schedule/index.ts new file mode 100644 index 000000000000..16bc6dc15310 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/rule_schedule/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { From } from '../from'; + +export type RuleInterval = t.TypeOf<typeof RuleInterval>; +export const RuleInterval = t.string; // we need a more specific schema + +export type RuleIntervalFrom = t.TypeOf<typeof RuleIntervalFrom>; +export const RuleIntervalFrom = From; + +/** + * TODO: Create a regular expression type or custom date math part type here + */ +export type RuleIntervalTo = t.TypeOf<typeof RuleIntervalTo>; +export const RuleIntervalTo = t.string; // we need a more specific schema diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/saved_object_attributes/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/saved_object_attributes/index.ts new file mode 100644 index 000000000000..33db222f098e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/saved_object_attributes/index.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +/** + * TODO: This type are originally from "src/core/types/saved_objects.ts", once that is package friendly remove + * this copied type. + * + * Don't use this type, it's simply a helper type for {@link SavedObjectAttribute} + * + * @public + */ +export type SavedObjectAttributeSingle = + | string + | number + | boolean + | null + | undefined + | SavedObjectAttributes; + +/** + * TODO: This type are originally from "src/core/types/saved_objects.ts", once that is package friendly remove + * this copied type. + * + * Type definition for a Saved Object attribute value + * + * @public + */ +export type SavedObjectAttribute = SavedObjectAttributeSingle | SavedObjectAttributeSingle[]; + +/** + * TODO: This type are originally from "src/core/types/saved_objects.ts", once that is package friendly remove + * this copied type. + * + * The data for a Saved Object is stored as an object in the `attributes` + * property. + * + * @public + */ +export interface SavedObjectAttributes { + [key: string]: SavedObjectAttribute; +} + +export const saved_object_attribute_single: t.Type<SavedObjectAttributeSingle> = t.recursion( + 'saved_object_attribute_single', + () => t.union([t.string, t.number, t.boolean, t.null, t.undefined, saved_object_attributes]) +); +export const saved_object_attribute: t.Type<SavedObjectAttribute> = t.recursion( + 'saved_object_attribute', + () => t.union([saved_object_attribute_single, t.array(saved_object_attribute_single)]) +); +export const saved_object_attributes: t.Type<SavedObjectAttributes> = t.recursion( + 'saved_object_attributes', + () => t.record(t.string, saved_object_attribute) +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/severity/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/severity/index.ts new file mode 100644 index 000000000000..1b27423eb0db --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/severity/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. + */ + +import * as t from 'io-ts'; + +export type Severity = t.TypeOf<typeof Severity>; +export const Severity = t.keyof({ low: null, medium: null, high: null, critical: null }); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts new file mode 100644 index 000000000000..4256314b32a2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { operator } from '@kbn/securitysolution-io-ts-types'; +import { Severity } from '../severity'; + +export type SeverityMappingItem = t.TypeOf<typeof SeverityMappingItem>; +export const SeverityMappingItem = t.exact( + t.type({ + field: t.string, + operator, + value: t.string, + severity: Severity, + }) +); + +export type SeverityMapping = t.TypeOf<typeof SeverityMapping>; +export const SeverityMapping = t.array(SeverityMappingItem); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts new file mode 100644 index 000000000000..4b5b7aa16c2d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { threat_tactic } from '../threat_tactic'; +import { threat_techniques } from '../threat_technique'; + +export const threat_framework = t.string; + +export const threat = t.intersection([ + t.exact( + t.type({ + framework: threat_framework, + tactic: threat_tactic, + }) + ), + t.exact( + t.partial({ + technique: threat_techniques, + }) + ), +]); + +export type Threat = t.TypeOf<typeof threat>; + +export const threats = t.array(threat); +export type Threats = t.TypeOf<typeof threats>; + +export const threatsOrUndefined = t.union([threats, t.undefined]); +export type ThreatsOrUndefined = t.TypeOf<typeof threatsOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts new file mode 100644 index 000000000000..781b5b89ebe6 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts @@ -0,0 +1,235 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { + concurrent_searches, + items_per_search, + ThreatMapping, + threatMappingEntries, + ThreatMappingEntries, + threat_mapping, +} from '.'; +import { foldLeftRight, getPaths, exactCheck } from '@kbn/securitysolution-io-ts-utils'; + +describe('threat_mapping', () => { + describe('threatMappingEntries', () => { + test('it should validate an entry', () => { + const payload: ThreatMappingEntries = [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + }, + ]; + const decoded = threatMappingEntries.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation with an extra entry item', () => { + const payload: Array<ThreatMappingEntries[0] & { extra: string }> = [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + extra: 'blah', + }, + ]; + const decoded = threatMappingEntries.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation with a non string', () => { + const payload = [ + { + field: 5, + type: 'mapping', + value: 'field.one', + }, + ] as unknown as ThreatMappingEntries[]; + const decoded = threatMappingEntries.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation with a wrong type', () => { + const payload = [ + { + field: 'field.one', + type: 'invalid', + value: 'field.one', + }, + ] as unknown as ThreatMappingEntries[]; + const decoded = threatMappingEntries.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "invalid" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('threat_mapping', () => { + test('it should validate a threat mapping', () => { + const payload: ThreatMapping = [ + { + entries: [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + }, + ], + }, + ]; + const decoded = threat_mapping.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + }); + + test('it should fail validate with an extra key', () => { + const payload: Array<ThreatMapping[0] & { extra: string }> = [ + { + entries: [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + }, + ], + extra: 'invalid', + }, + ]; + + const decoded = threat_mapping.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validate with an extra inner entry', () => { + const payload: Array<ThreatMapping[0] & { entries: Array<{ extra: string }> }> = [ + { + entries: [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + extra: 'blah', + }, + ], + }, + ]; + + const decoded = threat_mapping.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validate with an extra inner entry with the wrong data type', () => { + const payload = [ + { + entries: [ + { + field: 5, + type: 'mapping', + value: 'field.one', + }, + ], + }, + ] as unknown as ThreatMapping; + + const decoded = threat_mapping.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "entries,field"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validate with empty array', () => { + const payload: string[] = []; + + const decoded = threat_mapping.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "NonEmptyArray<ThreatMap>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when concurrent_searches is < 0', () => { + const payload = -1; + const decoded = concurrent_searches.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when concurrent_searches is 0', () => { + const payload = 0; + const decoded = concurrent_searches.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when items_per_search is 0', () => { + const payload = 0; + const decoded = items_per_search.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when items_per_search is < 0', () => { + const payload = -1; + const decoded = items_per_search.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "PositiveIntegerGreaterThanZero"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts new file mode 100644 index 000000000000..3bf4f84aeec8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts @@ -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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { + NonEmptyArray, + NonEmptyString, + PositiveIntegerGreaterThanZero, +} from '@kbn/securitysolution-io-ts-types'; +import { language } from '../language'; + +export const threat_query = t.string; +export type ThreatQuery = t.TypeOf<typeof threat_query>; +export const threatQueryOrUndefined = t.union([threat_query, t.undefined]); +export type ThreatQueryOrUndefined = t.TypeOf<typeof threatQueryOrUndefined>; + +export const threat_indicator_path = t.string; +export type ThreatIndicatorPath = t.TypeOf<typeof threat_indicator_path>; +export const threatIndicatorPathOrUndefined = t.union([threat_indicator_path, t.undefined]); +export type ThreatIndicatorPathOrUndefined = t.TypeOf<typeof threatIndicatorPathOrUndefined>; + +export const threat_filters = t.array(t.unknown); // Filters are not easily type-able yet +export type ThreatFilters = t.TypeOf<typeof threat_filters>; +export const threatFiltersOrUndefined = t.union([threat_filters, t.undefined]); +export type ThreatFiltersOrUndefined = t.TypeOf<typeof threatFiltersOrUndefined>; + +export const threatMapEntry = t.exact( + t.type({ + field: NonEmptyString, + type: t.keyof({ mapping: null }), + value: NonEmptyString, + }) +); + +export type ThreatMapEntry = t.TypeOf<typeof threatMapEntry>; + +export const threatMappingEntries = t.array(threatMapEntry); +export type ThreatMappingEntries = t.TypeOf<typeof threatMappingEntries>; + +export const threatMap = t.exact( + t.type({ + entries: threatMappingEntries, + }) +); +export type ThreatMap = t.TypeOf<typeof threatMap>; + +export const threat_mapping = NonEmptyArray(threatMap, 'NonEmptyArray<ThreatMap>'); +export type ThreatMapping = t.TypeOf<typeof threat_mapping>; + +export const threatMappingOrUndefined = t.union([threat_mapping, t.undefined]); +export type ThreatMappingOrUndefined = t.TypeOf<typeof threatMappingOrUndefined>; + +export const threat_index = t.array(t.string); +export type ThreatIndex = t.TypeOf<typeof threat_index>; +export const threatIndexOrUndefined = t.union([threat_index, t.undefined]); +export type ThreatIndexOrUndefined = t.TypeOf<typeof threatIndexOrUndefined>; + +export const threat_language = t.union([language, t.undefined]); +export type ThreatLanguage = t.TypeOf<typeof threat_language>; +export const threatLanguageOrUndefined = t.union([threat_language, t.undefined]); +export type ThreatLanguageOrUndefined = t.TypeOf<typeof threatLanguageOrUndefined>; + +export const concurrent_searches = PositiveIntegerGreaterThanZero; +export type ConcurrentSearches = t.TypeOf<typeof concurrent_searches>; +export const concurrentSearchesOrUndefined = t.union([concurrent_searches, t.undefined]); +export type ConcurrentSearchesOrUndefined = t.TypeOf<typeof concurrentSearchesOrUndefined>; + +export const items_per_search = PositiveIntegerGreaterThanZero; +export type ItemsPerSearch = t.TypeOf<typeof concurrent_searches>; +export const itemsPerSearchOrUndefined = t.union([items_per_search, t.undefined]); +export type ItemsPerSearchOrUndefined = t.TypeOf<typeof itemsPerSearchOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts new file mode 100644 index 000000000000..b5f2d95a139b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts @@ -0,0 +1,24 @@ +/* + * 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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const threat_subtechnique_id = t.string; +export const threat_subtechnique_name = t.string; +export const threat_subtechnique_reference = t.string; + +export const threat_subtechnique = t.type({ + id: threat_subtechnique_id, + name: threat_subtechnique_name, + reference: threat_subtechnique_reference, +}); + +export const threat_subtechniques = t.array(threat_subtechnique); + +export type ThreatSubtechnique = t.TypeOf<typeof threat_subtechnique>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_tactic/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_tactic/index.ts new file mode 100644 index 000000000000..a4c63cb63289 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_tactic/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const threat_tactic_id = t.string; +export const threat_tactic_name = t.string; +export const threat_tactic_reference = t.string; + +export const threat_tactic = t.type({ + id: threat_tactic_id, + name: threat_tactic_name, + reference: threat_tactic_reference, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts new file mode 100644 index 000000000000..992c5b15ba65 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { threat_subtechniques } from '../threat_subtechnique'; + +export const threat_technique_id = t.string; +export const threat_technique_name = t.string; +export const threat_technique_reference = t.string; + +export const threat_technique = t.intersection([ + t.exact( + t.type({ + id: threat_technique_id, + name: threat_technique_name, + reference: threat_technique_reference, + }) + ), + t.exact( + t.partial({ + subtechnique: threat_subtechniques, + }) + ), +]); +export const threat_techniques = t.array(threat_technique); + +export type ThreatTechnique = t.TypeOf<typeof threat_technique>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts new file mode 100644 index 000000000000..b4be7a4a97ff --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { TimeDuration } from '@kbn/securitysolution-io-ts-types'; + +export type RuleActionThrottle = t.TypeOf<typeof RuleActionThrottle>; +export const RuleActionThrottle = t.union([ + t.literal('no_actions'), + t.literal('rule'), + TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }), +]); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.ts new file mode 100644 index 000000000000..92b71faf14fe --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.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 * as t from 'io-ts'; + +export const type = t.keyof({ + eql: null, + machine_learning: null, + query: null, + saved_query: null, + threshold: null, + threat_match: null, + new_terms: null, + esql: null, +}); +export type Type = t.TypeOf<typeof type>; + +export const typeOrUndefined = t.union([type, t.undefined]); +export type TypeOrUndefined = t.TypeOf<typeof typeOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json new file mode 100644 index 000000000000..9b61aea31d97 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "kbn_references": [ + "@kbn/securitysolution-io-ts-types", + "@kbn/securitysolution-io-ts-utils" + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-securitysolution-io-ts-list-types/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/README.md similarity index 100% rename from packages/kbn-securitysolution-io-ts-list-types/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/index.ts new file mode 100644 index 000000000000..90db55188109 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/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 * from './src/common'; +export * from './src/request'; +export * from './src/response'; +export * from './src/typescript_types'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/jest.config.js b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/jest.config.js new file mode 100644 index 000000000000..1e8525371d27 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types'], +}; diff --git a/packages/kbn-securitysolution-io-ts-list-types/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-io-ts-list-types/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/package.json new file mode 100644 index 000000000000..da6c20901584 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/securitysolution-io-ts-list-types", + "version": "1.0.0", + "description": "io ts utilities and types to be shared with plugins from the security solution project", + "license": "Elastic License 2.0", + "private": true, + "sideEffects": false +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.mock.ts new file mode 100644 index 000000000000..69ad828a480e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Comment, CommentsArray } from '.'; +import { DATE_NOW, ID, USER } from '../../constants/index.mock'; + +export const getCommentsMock = (): Comment => ({ + comment: 'some old comment', + created_at: DATE_NOW, + created_by: USER, + id: ID, +}); + +export const getCommentsArrayMock = (): CommentsArray => [getCommentsMock(), getCommentsMock()]; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.test.ts new file mode 100644 index 000000000000..32a230fcf586 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.test.ts @@ -0,0 +1,236 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getCommentsArrayMock, getCommentsMock } from './index.mock'; +import { + Comment, + comment, + CommentsArray, + commentsArray, + CommentsArrayOrUndefined, + commentsArrayOrUndefined, +} from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { DATE_NOW } from '../../constants/index.mock'; + +describe('Comment', () => { + describe('comment', () => { + test('it fails validation when "id" is undefined', () => { + const payload = { ...getCommentsMock(), id: undefined }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it passes validation with a typical comment', () => { + const payload = getCommentsMock(); + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation with "updated_at" and "updated_by" fields included', () => { + const payload = getCommentsMock(); + payload.updated_at = DATE_NOW; + payload.updated_by = 'someone'; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when undefined', () => { + const payload = undefined; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "comment" is an empty string', () => { + const payload: Omit<Comment, 'comment'> & { comment: string } = { + ...getCommentsMock(), + comment: '', + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "comment"']); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "comment" is not a string', () => { + const payload: Omit<Comment, 'comment'> & { comment: string[] } = { + ...getCommentsMock(), + comment: ['some value'], + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "comment"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "created_at" is not a string', () => { + const payload: Omit<Comment, 'created_at'> & { created_at: number } = { + ...getCommentsMock(), + created_at: 1, + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "created_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "created_by" is not a string', () => { + const payload: Omit<Comment, 'created_by'> & { created_by: number } = { + ...getCommentsMock(), + created_by: 1, + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "created_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "updated_at" is not a string', () => { + const payload: Omit<Comment, 'updated_at'> & { updated_at: number } = { + ...getCommentsMock(), + updated_at: 1, + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "updated_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "updated_by" is not a string', () => { + const payload: Omit<Comment, 'updated_by'> & { updated_by: number } = { + ...getCommentsMock(), + updated_by: 1, + }; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "updated_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: Comment & { + extraKey?: string; + } = getCommentsMock(); + payload.extraKey = 'some value'; + const decoded = comment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getCommentsMock()); + }); + }); + + describe('commentsArray', () => { + test('it passes validation an array of Comment', () => { + const payload = getCommentsArrayMock(); + const decoded = commentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation when a Comment includes "updated_at" and "updated_by"', () => { + const commentsPayload = getCommentsMock(); + commentsPayload.updated_at = DATE_NOW; + commentsPayload.updated_by = 'someone'; + const payload = [{ ...commentsPayload }, ...getCommentsArrayMock()]; + const decoded = commentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when undefined', () => { + const payload = undefined; + const decoded = commentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when array includes non Comment types', () => { + const payload = [1] as unknown as CommentsArray; + const decoded = commentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('commentsArrayOrUndefined', () => { + test('it passes validation an array of Comment', () => { + const payload = getCommentsArrayMock(); + const decoded = commentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation when undefined', () => { + const payload = undefined; + const decoded = commentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when array includes non Comment types', () => { + const payload = [1] as unknown as CommentsArrayOrUndefined; + const decoded = commentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.ts new file mode 100644 index 000000000000..48bce8fabafe --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/comment/index.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; +import { created_at } from '../created_at'; +import { created_by } from '../created_by'; +import { id } from '../id'; +import { updated_at } from '../updated_at'; +import { updated_by } from '../updated_by'; + +export const comment = t.intersection([ + t.exact( + t.type({ + comment: NonEmptyString, + created_at, + created_by, + id, + }) + ), + t.exact( + t.partial({ + updated_at, + updated_by, + }) + ), +]); + +export const commentsArray = t.array(comment); +export type CommentsArray = t.TypeOf<typeof commentsArray>; +export type Comment = t.TypeOf<typeof comment>; +export const commentsArrayOrUndefined = t.union([commentsArray, t.undefined]); +export type CommentsArrayOrUndefined = t.TypeOf<typeof commentsArrayOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.mock.ts new file mode 100644 index 000000000000..67be8d1f15da --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.mock.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 { CreateComment, CreateCommentsArray } from '.'; + +export const getCreateCommentsMock = (): CreateComment => ({ + comment: 'some comments', +}); + +export const getCreateCommentsArrayMock = (): CreateCommentsArray => [getCreateCommentsMock()]; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.test.ts new file mode 100644 index 000000000000..15fab1c35a19 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.test.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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getCreateCommentsArrayMock, getCreateCommentsMock } from './index.mock'; +import { + CreateComment, + createComment, + CreateCommentsArray, + createCommentsArray, + CreateCommentsArrayOrUndefined, + createCommentsArrayOrUndefined, +} from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('CreateComment', () => { + describe('createComment', () => { + test('it passes validation with a default comment', () => { + const payload = getCreateCommentsMock(); + const decoded = createComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when undefined', () => { + const payload = undefined; + const decoded = createComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "{| comment: NonEmptyString |}"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when "comment" is not a string', () => { + const payload: Omit<CreateComment, 'comment'> & { comment: string[] } = { + ...getCreateCommentsMock(), + comment: ['some value'], + }; + const decoded = createComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "comment"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: CreateComment & { + extraKey?: string; + } = getCreateCommentsMock(); + payload.extraKey = 'some value'; + const decoded = createComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getCreateCommentsMock()); + }); + }); + + describe('createCommentsArray', () => { + test('it passes validation an array of comments', () => { + const payload = getCreateCommentsArrayMock(); + const decoded = createCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when undefined', () => { + const payload = undefined; + const decoded = createCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "Array<{| comment: NonEmptyString |}>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when array includes non comments types', () => { + const payload = [1] as unknown as CreateCommentsArray; + const decoded = createCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('createCommentsArrayOrUndefined', () => { + test('it passes validation an array of comments', () => { + const payload = getCreateCommentsArrayMock(); + const decoded = createCommentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation when undefined', () => { + const payload = undefined; + const decoded = createCommentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when array includes non comments types', () => { + const payload = [1] as unknown as CreateCommentsArrayOrUndefined; + const decoded = createCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"', + ]); + expect(message.schema).toEqual({}); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.ts new file mode 100644 index 000000000000..e0942203bac6 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/create_comment/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; + +export const createComment = t.exact( + t.type({ + comment: NonEmptyString, + }) +); + +export type CreateComment = t.TypeOf<typeof createComment>; +export const createCommentsArray = t.array(createComment); +export type CreateCommentsArray = t.TypeOf<typeof createCommentsArray>; +export type CreateComments = t.TypeOf<typeof createComment>; +export const createCommentsArrayOrUndefined = t.union([createCommentsArray, t.undefined]); +export type CreateCommentsArrayOrUndefined = t.TypeOf<typeof createCommentsArrayOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/created_at/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/created_at/index.ts new file mode 100644 index 000000000000..e962946e447e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/created_at/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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const created_at = t.string; // TODO: Make this into an ISO Date string check diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/created_by/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/created_by/index.ts new file mode 100644 index 000000000000..ccf8d816f8e8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/created_by/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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const created_by = t.string; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/cursor/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/cursor/index.ts new file mode 100644 index 000000000000..626b510ec895 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/cursor/index.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. + */ + +import * as t from 'io-ts'; + +export const cursor = t.string; +export type Cursor = t.TypeOf<typeof cursor>; +export const cursorOrUndefined = t.union([cursor, t.undefined]); +export type CursorOrUndefined = t.TypeOf<typeof cursorOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.test.ts new file mode 100644 index 000000000000..af9f342c15e5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { CommentsArray } from '../comment'; +import { DefaultCommentsArray } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getCommentsArrayMock } from '../comment/index.mock'; + +describe('default_comments_array', () => { + test('it should pass validation when supplied an empty array', () => { + const payload: CommentsArray = []; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied an array of comments', () => { + const payload: CommentsArray = getCommentsArrayMock(); + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an array of numbers', () => { + const payload = [1]; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an array of strings', () => { + const payload = ['some string']; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.ts new file mode 100644 index 000000000000..af1576237b9c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_comments_array/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { comment, CommentsArray } from '../comment'; + +/** + * Types the DefaultCommentsArray as: + * - If null or undefined, then a default array of type entry will be set + */ +export const DefaultCommentsArray = new t.Type<CommentsArray, CommentsArray, unknown>( + 'DefaultCommentsArray', + t.array(comment).is, + (input): Either<t.Errors, CommentsArray> => + input == null ? t.success([]) : t.array(comment).decode(input), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.test.ts new file mode 100644 index 000000000000..9f4aba07c9bf --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { CommentsArray } from '../comment'; +import { DefaultCommentsArray } from '../default_comments_array'; +import { getCommentsArrayMock } from '../comment/index.mock'; + +describe('default_comments_array', () => { + test('it should pass validation when supplied an empty array', () => { + const payload: CommentsArray = []; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied an array of comments', () => { + const payload: CommentsArray = getCommentsArrayMock(); + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an array of numbers', () => { + const payload = [1]; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an array of strings', () => { + const payload = ['some string']; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = DefaultCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.ts new file mode 100644 index 000000000000..5b825578191f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_create_comments_array/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { createComment, CreateCommentsArray } from '../create_comment'; + +/** + * Types the DefaultCreateComments as: + * - If null or undefined, then a default array of type entry will be set + */ +export const DefaultCreateCommentsArray = new t.Type< + CreateCommentsArray, + CreateCommentsArray, + unknown +>( + 'DefaultCreateComments', + t.array(createComment).is, + (input): Either<t.Errors, CreateCommentsArray> => + input == null ? t.success([]) : t.array(createComment).decode(input), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.test.ts new file mode 100644 index 000000000000..ea59988dfe0c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.test.ts @@ -0,0 +1,86 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { ImportCommentsArray } from '../import_comment'; +import { DefaultImportCommentsArray } from '.'; +import { getCommentsArrayMock } from '../comment/index.mock'; +import { getCreateCommentsArrayMock } from '../create_comment/index.mock'; + +describe('default_import_comments_array', () => { + test('it should pass validation when supplied an empty array', () => { + const payload: ImportCommentsArray = []; + const decoded = DefaultImportCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied an array of comments', () => { + const payload: ImportCommentsArray = getCommentsArrayMock(); + const decoded = DefaultImportCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied an array of new comments', () => { + const payload: ImportCommentsArray = getCreateCommentsArrayMock(); + const decoded = DefaultImportCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied an array of new and existing comments', () => { + const payload: ImportCommentsArray = [ + ...getCommentsArrayMock(), + ...getCreateCommentsArrayMock(), + ]; + const decoded = DefaultImportCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an array of numbers', () => { + const payload = [1]; + const decoded = DefaultImportCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "DefaultImportComments"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an array of strings', () => { + const payload = ['some string']; + const decoded = DefaultImportCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "DefaultImportComments"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = DefaultImportCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.ts new file mode 100644 index 000000000000..135288c2dd3f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_import_comments_array/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { importComment, ImportCommentsArray } from '../import_comment'; + +/** + * Types the DefaultImportCommentsArray as: + * - If null or undefined, then a default array of type ImportCommentsArray will be set + */ +export const DefaultImportCommentsArray = new t.Type< + ImportCommentsArray, + ImportCommentsArray, + unknown +>( + 'DefaultImportComments', + t.array(importComment).is, + (input, context): Either<t.Errors, ImportCommentsArray> => + input == null ? t.success([]) : t.array(importComment).validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.test.ts new file mode 100644 index 000000000000..fc271855fc12 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.test.ts @@ -0,0 +1,60 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultNamespace } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_namespace', () => { + test('it should validate "single"', () => { + const payload = 'single'; + const decoded = DefaultNamespace.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate "agnostic"', () => { + const payload = 'agnostic'; + const decoded = DefaultNamespace.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it defaults to "single" if "undefined"', () => { + const payload = undefined; + const decoded = DefaultNamespace.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('single'); + }); + + test('it defaults to "single" if "null"', () => { + const payload = null; + const decoded = DefaultNamespace.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual('single'); + }); + + test('it should FAIL validation if not "single" or "agnostic"', () => { + const payload = 'something else'; + const decoded = DefaultNamespace.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + `Invalid value "something else" supplied to "DefaultNamespace"`, + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.ts new file mode 100644 index 000000000000..667ddbb82253 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +export const namespaceType = t.keyof({ agnostic: null, single: null }); +export type NamespaceType = t.TypeOf<typeof namespaceType>; + +/** + * Types the DefaultNamespace as: + * - If null or undefined, then a default string/enumeration of "single" will be used. + */ +export const DefaultNamespace = new t.Type<NamespaceType, NamespaceType | undefined, unknown>( + 'DefaultNamespace', + namespaceType.is, + (input, context): Either<t.Errors, NamespaceType> => + input == null ? t.success('single') : namespaceType.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.test.ts new file mode 100644 index 000000000000..cb7012503d38 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.test.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultNamespaceArray, DefaultNamespaceArrayType } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('default_namespace_array', () => { + test('it should validate "null" single item as an array with a "single" value', () => { + const payload: DefaultNamespaceArrayType = null; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['single']); + }); + + test('it should FAIL validation of numeric value', () => { + const payload = 5; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to "DefaultNamespaceArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate "undefined" item as an array with a "single" value', () => { + const payload: DefaultNamespaceArrayType = undefined; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['single']); + }); + + test('it should validate "single" as an array of a "single" value', () => { + const payload: DefaultNamespaceArrayType = 'single'; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([payload]); + }); + + test('it should validate "agnostic" as an array of a "agnostic" value', () => { + const payload: DefaultNamespaceArrayType = 'agnostic'; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([payload]); + }); + + test('it should validate "single,agnostic" as an array of 2 values of ["single", "agnostic"] values', () => { + const payload: DefaultNamespaceArrayType = 'agnostic,single'; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['agnostic', 'single']); + }); + + test('it should validate 3 elements of "single,agnostic,single" as an array of 3 values of ["single", "agnostic", "single"] values', () => { + const payload: DefaultNamespaceArrayType = 'single,agnostic,single'; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['single', 'agnostic', 'single']); + }); + + test('it should validate 3 elements of "single,agnostic, single" as an array of 3 values of ["single", "agnostic", "single"] values when there are spaces', () => { + const payload: DefaultNamespaceArrayType = ' single, agnostic, single '; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(['single', 'agnostic', 'single']); + }); + + test('it should FAIL validation when given 3 elements of "single,agnostic,junk" since the 3rd value is junk', () => { + const payload: DefaultNamespaceArrayType = 'single,agnostic,junk'; + const decoded = DefaultNamespaceArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "junk" supplied to "DefaultNamespaceArray"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.ts new file mode 100644 index 000000000000..494c6025f198 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_namespace_array/index.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { namespaceType } from '../default_namespace'; + +export const namespaceTypeArray = t.array(namespaceType); +export type NamespaceTypeArray = t.TypeOf<typeof namespaceTypeArray>; + +/** + * Types the DefaultNamespaceArray as: + * - If null or undefined, then a default string array of "single" will be used. + * - If it contains a string, then it is split along the commas and puts them into an array and validates it + */ +export const DefaultNamespaceArray = new t.Type< + NamespaceTypeArray, + string | undefined | null, + unknown +>( + 'DefaultNamespaceArray', + namespaceTypeArray.is, + (input, context): Either<t.Errors, NamespaceTypeArray> => { + if (input == null) { + return t.success(['single']); + } else if (typeof input === 'string') { + const commaSeparatedValues = input + .trim() + .split(',') + .map((value) => value.trim()); + return namespaceTypeArray.validate(commaSeparatedValues, context); + } + return t.failure(input, context); + }, + String +); + +export type DefaultNamespaceArrayType = t.OutputOf<typeof DefaultNamespaceArray>; +export type DefaultNamespaceArrayTypeDecoded = t.TypeOf<typeof DefaultNamespaceArray>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.test.ts new file mode 100644 index 000000000000..3cced7284425 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { UpdateCommentsArray } from '../update_comment'; +import { DefaultUpdateCommentsArray } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getUpdateCommentsArrayMock } from '../update_comment/index.mock'; + +describe('default_update_comments_array', () => { + test('it should pass validation when supplied an empty array', () => { + const payload: UpdateCommentsArray = []; + const decoded = DefaultUpdateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied an array of comments', () => { + const payload: UpdateCommentsArray = getUpdateCommentsArrayMock(); + const decoded = DefaultUpdateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an array of numbers', () => { + const payload = [1]; + const decoded = DefaultUpdateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "DefaultUpdateComments"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an array of strings', () => { + const payload = ['some string']; + const decoded = DefaultUpdateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "DefaultUpdateComments"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = DefaultUpdateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.ts new file mode 100644 index 000000000000..28edd4af353e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/default_update_comments_array/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { updateCommentsArray, UpdateCommentsArray } from '../update_comment'; + +/** + * Types the DefaultUpdateComments as: + * - If null or undefined, then a default array of type UpdateCommentsArray will be set + */ +export const DefaultUpdateCommentsArray = new t.Type< + UpdateCommentsArray, + UpdateCommentsArray, + unknown +>( + 'DefaultUpdateComments', + updateCommentsArray.is, + (input, context): Either<t.Errors, UpdateCommentsArray> => + input == null ? t.success([]) : updateCommentsArray.validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/description/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/description/index.ts new file mode 100644 index 000000000000..adba3ae0ef4f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/description/index.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. + */ + +import * as t from 'io-ts'; + +export const description = t.string; +export type Description = t.TypeOf<typeof description>; +export const descriptionOrUndefined = t.union([description, t.undefined]); +export type DescriptionOrUndefined = t.TypeOf<typeof descriptionOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/deserializer/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/deserializer/index.ts new file mode 100644 index 000000000000..4956736b5cf4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/deserializer/index.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. + */ + +import * as t from 'io-ts'; + +export const deserializer = t.string; +export type Deserializer = t.TypeOf<typeof deserializer>; +export const deserializerOrUndefined = t.union([deserializer, t.undefined]); +export type DeserializerOrUndefined = t.TypeOf<typeof deserializerOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.mock.ts new file mode 100644 index 000000000000..1d46d6412e12 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.mock.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 { EndpointEntriesArray } from '.'; +import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; +import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEndpointEntryNestedMock } from '../entry_nested/index.mock'; +import { getEndpointEntryMatchWildcardMock } from '../entry_match_wildcard/index.mock'; + +export const getEndpointEntriesArrayMock = (): EndpointEntriesArray => [ + getEndpointEntryMatchMock(), + getEndpointEntryMatchAnyMock(), + getEndpointEntryNestedMock(), + getEndpointEntryMatchWildcardMock(), +]; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.test.ts new file mode 100644 index 000000000000..4a317d5c9152 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.test.ts @@ -0,0 +1,120 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; +import { + endpointEntriesArray, + nonEmptyEndpointEntriesArray, + NonEmptyEndpointEntriesArray, +} from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEndpointEntryNestedMock } from '../entry_nested/index.mock'; +import { getEndpointEntriesArrayMock } from './index.mock'; +import { getEntryListMock } from '../../entries_list/index.mock'; +import { getEntryExistsMock } from '../../entries_exist/index.mock'; +import { getEndpointEntryMatchWildcardMock } from '../entry_match_wildcard/index.mock'; + +describe('Endpoint', () => { + describe('entriesArray', () => { + test('it should validate an array with match entry', () => { + const payload = [getEndpointEntryMatchMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with match_any entry', () => { + const payload = [getEndpointEntryMatchAnyMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate an empty array', () => { + const payload: NonEmptyEndpointEntriesArray = []; + const decoded = nonEmptyEndpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "NonEmptyEndpointEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('type guard for nonEmptyEndpointNestedEntries should allow array of endpoint entries', () => { + const payload: NonEmptyEndpointEntriesArray = [getEndpointEntryMatchAnyMock()]; + const guarded = nonEmptyEndpointEntriesArray.is(payload); + expect(guarded).toBeTruthy(); + }); + + test('type guard for nonEmptyEndpointNestedEntries should disallow empty arrays', () => { + const payload: NonEmptyEndpointEntriesArray = []; + const guarded = nonEmptyEndpointEntriesArray.is(payload); + expect(guarded).toBeFalsy(); + }); + + test('it should NOT validate an array with exists entry', () => { + const payload = [getEntryExistsMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "exists" supplied to "type"', + 'Invalid value "undefined" supplied to "value"', + 'Invalid value "undefined" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate an array with list entry', () => { + const payload = [getEntryListMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "list" supplied to "type"', + 'Invalid value "undefined" supplied to "value"', + 'Invalid value "undefined" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate an array with nested entry', () => { + const payload = [getEndpointEntryNestedMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with wildcard entry', () => { + const payload = [getEndpointEntryMatchWildcardMock()]; + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with all types of entries', () => { + const payload = getEndpointEntriesArrayMock(); + const decoded = endpointEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.ts new file mode 100644 index 000000000000..c9a74ae52573 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entries/index.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 * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { endpointEntryMatch } from '../entry_match'; +import { endpointEntryMatchAny } from '../entry_match_any'; +import { endpointEntryNested } from '../entry_nested'; +import { endpointEntryMatchWildcard } from '../entry_match_wildcard'; + +export const endpointEntriesArray = t.array( + t.union([ + endpointEntryMatch, + endpointEntryMatchAny, + endpointEntryMatchWildcard, + endpointEntryNested, + ]) +); +export type EndpointEntriesArray = t.TypeOf<typeof endpointEntriesArray>; + +/** + * Types the nonEmptyEndpointEntriesArray as: + * - An array of entries of length 1 or greater + * + */ +export const nonEmptyEndpointEntriesArray = new t.Type< + EndpointEntriesArray, + EndpointEntriesArray, + unknown +>( + 'NonEmptyEndpointEntriesArray', + (u: unknown): u is EndpointEntriesArray => endpointEntriesArray.is(u) && u.length > 0, + (input, context): Either<t.Errors, EndpointEntriesArray> => { + if (Array.isArray(input) && input.length === 0) { + return t.failure(input, context); + } else { + return endpointEntriesArray.validate(input, context); + } + }, + t.identity +); + +export type NonEmptyEndpointEntriesArray = t.OutputOf<typeof nonEmptyEndpointEntriesArray>; +export type NonEmptyEndpointEntriesArrayDecoded = t.TypeOf<typeof nonEmptyEndpointEntriesArray>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.mock.ts new file mode 100644 index 000000000000..ddcda0e1e2d9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EndpointEntryMatch } from '.'; +import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../../../constants/index.mock'; + +export const getEndpointEntryMatchMock = (): EndpointEntryMatch => ({ + field: FIELD, + operator: OPERATOR, + type: MATCH, + value: ENTRY_VALUE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.test.ts new file mode 100644 index 000000000000..561a36ab194c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.test.ts @@ -0,0 +1,101 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEndpointEntryMatchMock } from './index.mock'; +import { EndpointEntryMatch, endpointEntryMatch } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getEntryMatchMock } from '../../entry_match/index.mock'; + +describe('endpointEntryMatch', () => { + test('it should validate an entry', () => { + const payload = getEndpointEntryMatchMock(); + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate when "operator" is "excluded"', () => { + // Use the generic entry mock so we can test operator: excluded + const payload = getEntryMatchMock(); + payload.operator = 'excluded'; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "excluded" supplied to "operator"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit<EndpointEntryMatch, 'field'> & { field: string } = { + ...getEndpointEntryMatchMock(), + field: '', + }; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is not string', () => { + const payload: Omit<EndpointEntryMatch, 'value'> & { value: string[] } = { + ...getEndpointEntryMatchMock(), + value: ['some value'], + }; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is empty string', () => { + const payload: Omit<EndpointEntryMatch, 'value'> & { value: string } = { + ...getEndpointEntryMatchMock(), + value: '', + }; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "match"', () => { + const payload: Omit<EndpointEntryMatch, 'type'> & { type: string } = { + ...getEndpointEntryMatchMock(), + type: 'match_any', + }; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "match_any" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EndpointEntryMatch & { + extraKey?: string; + } = getEndpointEntryMatchMock(); + payload.extraKey = 'some value'; + const decoded = endpointEntryMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchMock()); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.ts new file mode 100644 index 000000000000..7cc51636cf58 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match/index.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 * as t from 'io-ts'; +import { NonEmptyString, operatorIncluded } from '@kbn/securitysolution-io-ts-types'; + +export const endpointEntryMatch = t.exact( + t.type({ + field: NonEmptyString, + operator: operatorIncluded, + type: t.keyof({ match: null }), + value: NonEmptyString, + }) +); +export type EndpointEntryMatch = t.TypeOf<typeof endpointEntryMatch>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.mock.ts new file mode 100644 index 000000000000..6c04a6b596d6 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../../../constants/index.mock'; +import { EndpointEntryMatchAny } from '.'; + +export const getEndpointEntryMatchAnyMock = (): EndpointEntryMatchAny => ({ + field: FIELD, + operator: OPERATOR, + type: MATCH_ANY, + value: [ENTRY_VALUE], +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.test.ts new file mode 100644 index 000000000000..feb04606adf9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEndpointEntryMatchAnyMock } from './index.mock'; +import { EndpointEntryMatchAny, endpointEntryMatchAny } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getEntryMatchAnyMock } from '../../entry_match_any/index.mock'; + +describe('endpointEntryMatchAny', () => { + test('it should validate an entry', () => { + const payload = getEndpointEntryMatchAnyMock(); + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate when operator is "excluded"', () => { + // Use the generic entry mock so we can test operator: excluded + const payload = getEntryMatchAnyMock(); + payload.operator = 'excluded'; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "excluded" supplied to "operator"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when field is empty string', () => { + const payload: Omit<EndpointEntryMatchAny, 'field'> & { field: string } = { + ...getEndpointEntryMatchAnyMock(), + field: '', + }; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when value is empty array', () => { + const payload: Omit<EndpointEntryMatchAny, 'value'> & { value: string[] } = { + ...getEndpointEntryMatchAnyMock(), + value: [], + }; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "[]" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when value is not string array', () => { + const payload: Omit<EndpointEntryMatchAny, 'value'> & { value: string } = { + ...getEndpointEntryMatchAnyMock(), + value: 'some string', + }; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "match_any"', () => { + const payload: Omit<EndpointEntryMatchAny, 'type'> & { type: string } = { + ...getEndpointEntryMatchAnyMock(), + type: 'match', + }; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EndpointEntryMatchAny & { + extraKey?: string; + } = getEndpointEntryMatchAnyMock(); + payload.extraKey = 'some extra key'; + const decoded = endpointEntryMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchAnyMock()); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.ts new file mode 100644 index 000000000000..7d23e7338e71 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_any/index.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 * as t from 'io-ts'; +import { + NonEmptyString, + nonEmptyOrNullableStringArray, + operatorIncluded, +} from '@kbn/securitysolution-io-ts-types'; + +export const endpointEntryMatchAny = t.exact( + t.type({ + field: NonEmptyString, + operator: operatorIncluded, + type: t.keyof({ match_any: null }), + value: nonEmptyOrNullableStringArray, + }) +); +export type EndpointEntryMatchAny = t.TypeOf<typeof endpointEntryMatchAny>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.mock.ts new file mode 100644 index 000000000000..ca8f01d3e30e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ENTRY_VALUE, FIELD, OPERATOR, WILDCARD } from '../../../constants/index.mock'; +import { EndpointEntryMatchWildcard } from '.'; + +export const getEndpointEntryMatchWildcardMock = (): EndpointEntryMatchWildcard => ({ + field: FIELD, + operator: OPERATOR, + type: WILDCARD, + value: ENTRY_VALUE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.test.ts new file mode 100644 index 000000000000..95ea73b2aac1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.test.ts @@ -0,0 +1,96 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEndpointEntryMatchWildcardMock } from './index.mock'; +import { EndpointEntryMatchWildcard, endpointEntryMatchWildcard } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getEntryMatchWildcardMock } from '../../entry_match_wildcard/index.mock'; + +describe('endpointEntryMatchWildcard', () => { + test('it should validate an entry', () => { + const payload = getEndpointEntryMatchWildcardMock(); + const decoded = endpointEntryMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "excluded"', () => { + const payload = getEntryMatchWildcardMock(); + payload.operator = 'excluded'; + const decoded = endpointEntryMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit<EndpointEntryMatchWildcard, 'field'> & { field: string } = { + ...getEndpointEntryMatchWildcardMock(), + field: '', + }; + const decoded = endpointEntryMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is not string', () => { + const payload: Omit<EndpointEntryMatchWildcard, 'value'> & { value: string[] } = { + ...getEndpointEntryMatchWildcardMock(), + value: ['some value'], + }; + const decoded = endpointEntryMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is empty string', () => { + const payload: Omit<EndpointEntryMatchWildcard, 'value'> & { value: string } = { + ...getEndpointEntryMatchWildcardMock(), + value: '', + }; + const decoded = endpointEntryMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "wildcard"', () => { + const payload: Omit<EndpointEntryMatchWildcard, 'type'> & { type: string } = { + ...getEndpointEntryMatchWildcardMock(), + type: 'match', + }; + const decoded = endpointEntryMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EndpointEntryMatchWildcard & { + extraKey?: string; + } = getEndpointEntryMatchWildcardMock(); + payload.extraKey = 'some value'; + const decoded = endpointEntryMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchWildcardMock()); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.ts new file mode 100644 index 000000000000..cfc96337b627 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_match_wildcard/index.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 * as t from 'io-ts'; +import { + NonEmptyString, + operatorExcluded, + operatorIncluded, +} from '@kbn/securitysolution-io-ts-types'; + +export const endpointEntryMatchWildcard = t.exact( + t.type({ + field: NonEmptyString, + operator: t.union([operatorIncluded, operatorExcluded]), + type: t.keyof({ wildcard: null }), + value: NonEmptyString, + }) +); +export type EndpointEntryMatchWildcard = t.TypeOf<typeof endpointEntryMatchWildcard>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.mock.ts new file mode 100644 index 000000000000..93b99e71b0bc --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EndpointEntryNested } from '.'; +import { FIELD, NESTED } from '../../../constants/index.mock'; +import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; +import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; + +export const getEndpointEntryNestedMock = (): EndpointEntryNested => ({ + entries: [getEndpointEntryMatchMock(), getEndpointEntryMatchAnyMock()], + field: FIELD, + type: NESTED, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.test.ts new file mode 100644 index 000000000000..d68a0686a439 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.test.ts @@ -0,0 +1,136 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { EndpointEntryNested, endpointEntryNested } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getEndpointEntryNestedMock } from './index.mock'; +import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { + nonEmptyEndpointNestedEntriesArray, + NonEmptyEndpointNestedEntriesArray, +} from '../non_empty_nested_entries_array'; +import { getEndpointEntryMatchMock } from '../entry_match/index.mock'; +import { getEntryExistsMock } from '../../entries_exist/index.mock'; + +describe('endpointEntryNested', () => { + test('it should validate a nested entry', () => { + const payload = getEndpointEntryNestedMock(); + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "type" is not "nested"', () => { + const payload: Omit<EndpointEntryNested, 'type'> & { type: 'match' } = { + ...getEndpointEntryNestedMock(), + type: 'match', + }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit<EndpointEntryNested, 'field'> & { + field: string; + } = { ...getEndpointEntryNestedMock(), field: '' }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "field" is not a string', () => { + const payload: Omit<EndpointEntryNested, 'field'> & { + field: number; + } = { ...getEndpointEntryNestedMock(), field: 1 }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "entries" is not an array', () => { + const payload: Omit<EndpointEntryNested, 'entries'> & { + entries: string; + } = { ...getEndpointEntryNestedMock(), entries: 'im a string' }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "im a string" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate when "entries" contains an entry item that is type "match"', () => { + const payload = { ...getEndpointEntryNestedMock(), entries: [getEndpointEntryMatchAnyMock()] }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual({ + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host name'], + }, + ], + field: 'host.name', + type: 'nested', + }); + }); + + test('it should NOT validate when "entries" contains an entry item that is type "exists"', () => { + const payload = { ...getEndpointEntryNestedMock(), entries: [getEntryExistsMock()] }; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "exists" supplied to "entries,type"', + 'Invalid value "undefined" supplied to "entries,value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EndpointEntryNested & { + extraKey?: string; + } = getEndpointEntryNestedMock(); + payload.extraKey = 'some extra key'; + const decoded = endpointEntryNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEndpointEntryNestedMock()); + }); + + test('type guard for nonEmptyEndpointNestedEntries should allow array of endpoint entries', () => { + const payload: NonEmptyEndpointNestedEntriesArray = [ + getEndpointEntryMatchMock(), + getEndpointEntryMatchAnyMock(), + ]; + const guarded = nonEmptyEndpointNestedEntriesArray.is(payload); + expect(guarded).toBeTruthy(); + }); + + test('type guard for nonEmptyEndpointNestedEntries should disallow empty arrays', () => { + const payload: NonEmptyEndpointNestedEntriesArray = []; + const guarded = nonEmptyEndpointNestedEntriesArray.is(payload); + expect(guarded).toBeFalsy(); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.ts new file mode 100644 index 000000000000..f02570af1013 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/entry_nested/index.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 * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; +import { nonEmptyEndpointNestedEntriesArray } from '../non_empty_nested_entries_array'; + +export const endpointEntryNested = t.exact( + t.type({ + entries: nonEmptyEndpointNestedEntriesArray, + field: NonEmptyString, + type: t.keyof({ nested: null }), + }) +); +export type EndpointEntryNested = t.TypeOf<typeof endpointEntryNested>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/index.ts new file mode 100644 index 000000000000..5138682f463b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/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 './entries'; +export * from './non_empty_nested_entries_array'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/non_empty_nested_entries_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/non_empty_nested_entries_array/index.ts new file mode 100644 index 000000000000..5085b4fdacac --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/endpoint/non_empty_nested_entries_array/index.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { endpointEntryMatch } from '../entry_match'; +import { endpointEntryMatchAny } from '../entry_match_any'; + +export const endpointNestedEntriesArray = t.array( + t.union([endpointEntryMatch, endpointEntryMatchAny]) +); +export type EndpointNestedEntriesArray = t.TypeOf<typeof endpointNestedEntriesArray>; + +/** + * Types the nonEmptyNestedEntriesArray as: + * - An array of entries of length 1 or greater + * + */ +export const nonEmptyEndpointNestedEntriesArray = new t.Type< + EndpointNestedEntriesArray, + EndpointNestedEntriesArray, + unknown +>( + 'NonEmptyEndpointNestedEntriesArray', + (u: unknown): u is EndpointNestedEntriesArray => endpointNestedEntriesArray.is(u) && u.length > 0, + (input, context): Either<t.Errors, EndpointNestedEntriesArray> => { + if (Array.isArray(input) && input.length === 0) { + return t.failure(input, context); + } else { + return endpointNestedEntriesArray.validate(input, context); + } + }, + t.identity +); + +export type NonEmptyEndpointNestedEntriesArray = t.OutputOf< + typeof nonEmptyEndpointNestedEntriesArray +>; +export type NonEmptyEndpointNestedEntriesArrayDecoded = t.TypeOf< + typeof nonEmptyEndpointNestedEntriesArray +>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.mock.ts new file mode 100644 index 000000000000..1aeea284751b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.mock.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 { EntriesArray } from '.'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; +import { getEntryListMock } from '../entries_list/index.mock'; +import { getEntryMatchMock } from '../entry_match/index.mock'; +import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEntryNestedMock } from '../entry_nested/index.mock'; + +export const getListAndNonListEntriesArrayMock = (): EntriesArray => [ + getEntryMatchMock(), + getEntryMatchAnyMock(), + getEntryListMock(), + getEntryExistsMock(), + getEntryNestedMock(), +]; + +export const getListEntriesArrayMock = (): EntriesArray => [getEntryListMock(), getEntryListMock()]; + +export const getEntriesArrayMock = (): EntriesArray => [ + getEntryMatchMock(), + getEntryMatchAnyMock(), + getEntryExistsMock(), + getEntryNestedMock(), +]; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.test.ts new file mode 100644 index 000000000000..25c52b68a6f9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.test.ts @@ -0,0 +1,147 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryMatchMock } from '../entry_match/index.mock'; +import { entriesArray, entriesArrayOrUndefined, entry } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; +import { getEntryListMock } from '../entries_list/index.mock'; +import { getEntryNestedMock } from '../entry_nested/index.mock'; +import { getEntriesArrayMock } from './index.mock'; + +describe('Entries', () => { + describe('entry', () => { + test('it should validate a match entry', () => { + const payload = getEntryMatchMock(); + const decoded = entry.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a match_any entry', () => { + const payload = getEntryMatchAnyMock(); + const decoded = entry.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a exists entry', () => { + const payload = getEntryExistsMock(); + const decoded = entry.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a list entry', () => { + const payload = getEntryListMock(); + const decoded = entry.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation of nested entry', () => { + const payload = getEntryNestedMock(); + const decoded = entry.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "operator"', + 'Invalid value "nested" supplied to "type"', + 'Invalid value "undefined" supplied to "value"', + 'Invalid value "undefined" supplied to "list"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('entriesArray', () => { + test('it should validate an array with match entry', () => { + const payload = [getEntryMatchMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with match_any entry', () => { + const payload = [getEntryMatchAnyMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with exists entry', () => { + const payload = [getEntryExistsMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with list entry', () => { + const payload = [getEntryListMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with nested entry', () => { + const payload = [getEntryNestedMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with all types of entries', () => { + const payload = [...getEntriesArrayMock()]; + const decoded = entriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + }); + + describe('entriesArrayOrUndefined', () => { + test('it should validate undefined', () => { + const payload = undefined; + const decoded = entriesArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array with nested entry', () => { + const payload = [getEntryNestedMock()]; + const decoded = entriesArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.ts new file mode 100644 index 000000000000..73340d2c0af5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { entriesExists } from '../entries_exist'; +import { entriesList } from '../entries_list'; +import { entriesMatch } from '../entry_match'; +import { entriesMatchAny } from '../entry_match_any'; +import { entriesMatchWildcard } from '../entry_match_wildcard'; +import { entriesNested } from '../entry_nested'; + +// NOTE: Type nested is not included here to denote it's non-recursive nature. +// So a nested entry is really just a collection of `Entry` types. +export const entry = t.union([ + entriesMatch, + entriesMatchAny, + entriesList, + entriesExists, + entriesMatchWildcard, +]); +export type Entry = t.TypeOf<typeof entry>; + +export const entriesArray = t.array( + t.union([ + entriesMatch, + entriesMatchAny, + entriesList, + entriesExists, + entriesNested, + entriesMatchWildcard, + ]) +); +export type EntriesArray = t.TypeOf<typeof entriesArray>; + +export const entriesArrayOrUndefined = t.union([entriesArray, t.undefined]); +export type EntriesArrayOrUndefined = t.TypeOf<typeof entriesArrayOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.mock.ts new file mode 100644 index 000000000000..e66d14c3c7af --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.mock.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntryExists } from '.'; +import { EXISTS, FIELD, OPERATOR } from '../../constants/index.mock'; + +export const getEntryExistsMock = (): EntryExists => ({ + field: FIELD, + operator: OPERATOR, + type: EXISTS, +}); + +export const getEntryExistsExcludedMock = (): EntryExists => ({ + ...getEntryExistsMock(), + operator: 'excluded', +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.test.ts new file mode 100644 index 000000000000..dcc6d47a437a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.test.ts @@ -0,0 +1,78 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryExistsMock } from './index.mock'; +import { entriesExists, EntryExists } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('entriesExists', () => { + test('it should validate an entry', () => { + const payload = getEntryExistsMock(); + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "included"', () => { + const payload = getEntryExistsMock(); + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "excluded"', () => { + const payload = getEntryExistsMock(); + payload.operator = 'excluded'; + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit<EntryExists, 'field'> & { field: string } = { + ...getEntryExistsMock(), + field: '', + }; + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EntryExists & { + extraKey?: string; + } = getEntryExistsMock(); + payload.extraKey = 'some extra key'; + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryExistsMock()); + }); + + test('it should FAIL validation when "type" is not "exists"', () => { + const payload: Omit<EntryExists, 'type'> & { type: string } = { + ...getEntryExistsMock(), + type: 'match', + }; + const decoded = entriesExists.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.ts new file mode 100644 index 000000000000..44c83c3f5b64 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_exist/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; +import { listOperator as operator } from '../list_operator'; + +export const entriesExists = t.exact( + t.type({ + field: NonEmptyString, + operator, + type: t.keyof({ exists: null }), + }) +); +export type EntryExists = t.TypeOf<typeof entriesExists>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.mock.ts new file mode 100644 index 000000000000..77c73d0bcfa3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntryList } from '.'; +import { FIELD, LIST, LIST_ID, OPERATOR, TYPE } from '../../constants/index.mock'; + +export const getEntryListMock = (): EntryList => ({ + field: FIELD, + list: { id: LIST_ID, type: TYPE }, + operator: OPERATOR, + type: LIST, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.test.ts new file mode 100644 index 000000000000..a9b56207d959 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.test.ts @@ -0,0 +1,95 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryListMock } from './index.mock'; +import { entriesList, EntryList } from '.'; + +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('entriesList', () => { + test('it should validate an entry', () => { + const payload = getEntryListMock(); + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when operator is "included"', () => { + const payload = getEntryListMock(); + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "excluded"', () => { + const payload = getEntryListMock(); + payload.operator = 'excluded'; + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "list" is not expected value', () => { + const payload: Omit<EntryList, 'list'> & { list: string } = { + ...getEntryListMock(), + list: 'someListId', + }; + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "someListId" supplied to "list"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "list.id" is empty string', () => { + const payload: Omit<EntryList, 'list'> & { list: { id: string; type: 'ip' } } = { + ...getEntryListMock(), + list: { id: '', type: 'ip' }, + }; + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "list,id"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "lists"', () => { + const payload: Omit<EntryList, 'type'> & { type: 'match_any' } = { + ...getEntryListMock(), + type: 'match_any', + }; + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "match_any" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EntryList & { + extraKey?: string; + } = getEntryListMock(); + payload.extraKey = 'some extra key'; + const decoded = entriesList.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryListMock()); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.ts new file mode 100644 index 000000000000..214f0c92db22 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; + +import { type } from '../type'; +import { listOperator as operator } from '../list_operator'; + +export const entriesList = t.exact( + t.type({ + field: NonEmptyString, + list: t.exact(t.type({ id: NonEmptyString, type })), + operator, + type: t.keyof({ list: null }), + }) +); +export type EntryList = t.TypeOf<typeof entriesList>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.mock.ts new file mode 100644 index 000000000000..9af6cf08090b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.mock.ts @@ -0,0 +1,21 @@ +/* + * 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 { EntryMatch } from '.'; +import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../../constants/index.mock'; + +export const getEntryMatchMock = (): EntryMatch => ({ + field: FIELD, + operator: OPERATOR, + type: MATCH, + value: ENTRY_VALUE, +}); + +export const getEntryMatchExcludeMock = (): EntryMatch => ({ + ...getEntryMatchMock(), + operator: 'excluded', +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.test.ts new file mode 100644 index 000000000000..6f2fc7fd9e8d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.test.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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryMatchMock } from './index.mock'; +import { entriesMatch, EntryMatch } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('entriesMatch', () => { + test('it should validate an entry', () => { + const payload = getEntryMatchMock(); + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when operator is "included"', () => { + const payload = getEntryMatchMock(); + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "excluded"', () => { + const payload = getEntryMatchMock(); + payload.operator = 'excluded'; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit<EntryMatch, 'field'> & { field: string } = { + ...getEntryMatchMock(), + field: '', + }; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is not string', () => { + const payload: Omit<EntryMatch, 'value'> & { value: string[] } = { + ...getEntryMatchMock(), + value: ['some value'], + }; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is empty string', () => { + const payload: Omit<EntryMatch, 'value'> & { value: string } = { + ...getEntryMatchMock(), + value: '', + }; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "match"', () => { + const payload: Omit<EntryMatch, 'type'> & { type: string } = { + ...getEntryMatchMock(), + type: 'match_any', + }; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "match_any" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EntryMatch & { + extraKey?: string; + } = getEntryMatchMock(); + payload.extraKey = 'some value'; + const decoded = entriesMatch.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchMock()); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.ts new file mode 100644 index 000000000000..8e56991b977a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; +import { listOperator as operator } from '../list_operator'; + +export const entriesMatch = t.exact( + t.type({ + field: NonEmptyString, + operator, + type: t.keyof({ match: null }), + value: NonEmptyString, + }) +); +export type EntryMatch = t.TypeOf<typeof entriesMatch>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.mock.ts new file mode 100644 index 000000000000..a564d4e8fd95 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.mock.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntryMatchAny } from '.'; +import { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../../constants/index.mock'; + +export const getEntryMatchAnyMock = (): EntryMatchAny => ({ + field: FIELD, + operator: OPERATOR, + type: MATCH_ANY, + value: [ENTRY_VALUE], +}); + +export const getEntryMatchAnyExcludeMock = (): EntryMatchAny => ({ + ...getEntryMatchAnyMock(), + operator: 'excluded', + value: [ENTRY_VALUE, 'some other host name'], +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.test.ts new file mode 100644 index 000000000000..84844c432aa2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryMatchAnyMock } from './index.mock'; +import { entriesMatchAny, EntryMatchAny } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('entriesMatchAny', () => { + test('it should validate an entry', () => { + const payload = getEntryMatchAnyMock(); + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when operator is "included"', () => { + const payload = getEntryMatchAnyMock(); + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when operator is "excluded"', () => { + const payload = getEntryMatchAnyMock(); + payload.operator = 'excluded'; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when field is empty string', () => { + const payload: Omit<EntryMatchAny, 'field'> & { field: string } = { + ...getEntryMatchAnyMock(), + field: '', + }; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when value is empty array', () => { + const payload: Omit<EntryMatchAny, 'value'> & { value: string[] } = { + ...getEntryMatchAnyMock(), + value: [], + }; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "[]" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when value is not string array', () => { + const payload: Omit<EntryMatchAny, 'value'> & { value: string } = { + ...getEntryMatchAnyMock(), + value: 'some string', + }; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some string" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "match_any"', () => { + const payload: Omit<EntryMatchAny, 'type'> & { type: string } = { + ...getEntryMatchAnyMock(), + type: 'match', + }; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EntryMatchAny & { + extraKey?: string; + } = getEntryMatchAnyMock(); + payload.extraKey = 'some extra key'; + const decoded = entriesMatchAny.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchAnyMock()); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.ts new file mode 100644 index 000000000000..989c729f8b1b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_any/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { NonEmptyString, nonEmptyOrNullableStringArray } from '@kbn/securitysolution-io-ts-types'; +import { listOperator as operator } from '../list_operator'; + +export const entriesMatchAny = t.exact( + t.type({ + field: NonEmptyString, + operator, + type: t.keyof({ match_any: null }), + value: nonEmptyOrNullableStringArray, + }) +); +export type EntryMatchAny = t.TypeOf<typeof entriesMatchAny>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.mock.ts new file mode 100644 index 000000000000..c279fe3ed45d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.mock.ts @@ -0,0 +1,21 @@ +/* + * 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 { EntryMatchWildcard } from '.'; +import { ENTRY_VALUE, FIELD, OPERATOR, WILDCARD } from '../../constants/index.mock'; + +export const getEntryMatchWildcardMock = (): EntryMatchWildcard => ({ + field: FIELD, + operator: OPERATOR, + type: WILDCARD, + value: ENTRY_VALUE, +}); + +export const getEntryMatchWildcardExcludeMock = (): EntryMatchWildcard => ({ + ...getEntryMatchWildcardMock(), + operator: 'excluded', +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.test.ts new file mode 100644 index 000000000000..4bd2d146412a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryMatchWildcardMock } from './index.mock'; +import { entriesMatchWildcard, EntryMatchWildcard } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('entriesMatchWildcard', () => { + test('it should validate an entry', () => { + const payload = getEntryMatchWildcardMock(); + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when operator is "included"', () => { + const payload = getEntryMatchWildcardMock(); + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when "operator" is "excluded"', () => { + const payload = getEntryMatchWildcardMock(); + payload.operator = 'excluded'; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit<EntryMatchWildcard, 'field'> & { field: string } = { + ...getEntryMatchWildcardMock(), + field: '', + }; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is not string', () => { + const payload: Omit<EntryMatchWildcard, 'value'> & { value: string[] } = { + ...getEntryMatchWildcardMock(), + value: ['some value'], + }; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["some value"]" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "value" is empty string', () => { + const payload: Omit<EntryMatchWildcard, 'value'> & { value: string } = { + ...getEntryMatchWildcardMock(), + value: '', + }; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "type" is not "wildcard"', () => { + const payload: Omit<EntryMatchWildcard, 'type'> & { type: string } = { + ...getEntryMatchWildcardMock(), + type: 'match', + }; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: EntryMatchWildcard & { + extraKey?: string; + } = getEntryMatchWildcardMock(); + payload.extraKey = 'some value'; + const decoded = entriesMatchWildcard.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryMatchWildcardMock()); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.ts new file mode 100644 index 000000000000..af168e33fa70 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_match_wildcard/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; +import { listOperator as operator } from '../list_operator'; + +export const entriesMatchWildcard = t.exact( + t.type({ + field: NonEmptyString, + operator, + type: t.keyof({ wildcard: null }), + value: NonEmptyString, + }) +); +export type EntryMatchWildcard = t.TypeOf<typeof entriesMatchWildcard>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.mock.ts new file mode 100644 index 000000000000..890c3d379d27 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.mock.ts @@ -0,0 +1,28 @@ +/* + * 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 { EntryNested } from '.'; +import { NESTED, NESTED_FIELD } from '../../constants/index.mock'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; +import { getEntryMatchExcludeMock, getEntryMatchMock } from '../entry_match/index.mock'; +import { getEntryMatchAnyExcludeMock, getEntryMatchAnyMock } from '../entry_match_any/index.mock'; + +export const getEntryNestedMock = (): EntryNested => ({ + entries: [getEntryMatchMock(), getEntryMatchAnyMock()], + field: NESTED_FIELD, + type: NESTED, +}); + +export const getEntryNestedExcludeMock = (): EntryNested => ({ + ...getEntryNestedMock(), + entries: [getEntryMatchExcludeMock(), getEntryMatchAnyExcludeMock()], +}); + +export const getEntryNestedMixedEntries = (): EntryNested => ({ + ...getEntryNestedMock(), + entries: [getEntryMatchMock(), getEntryMatchAnyExcludeMock(), getEntryExistsMock()], +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.test.ts new file mode 100644 index 000000000000..be37a0273bce --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.test.ts @@ -0,0 +1,123 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEntryNestedMock } from './index.mock'; +import { entriesNested, EntryNested } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; + +describe('entriesNested', () => { + test('it should validate a nested entry', () => { + const payload = getEntryNestedMock(); + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when "type" is not "nested"', () => { + const payload: Omit<EntryNested, 'type'> & { type: 'match' } = { + ...getEntryNestedMock(), + type: 'match', + }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "field" is empty string', () => { + const payload: Omit<EntryNested, 'field'> & { + field: string; + } = { ...getEntryNestedMock(), field: '' }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "field" is not a string', () => { + const payload: Omit<EntryNested, 'field'> & { + field: number; + } = { ...getEntryNestedMock(), field: 1 }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "field"']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when "entries" is not a an array', () => { + const payload: Omit<EntryNested, 'entries'> & { + entries: string; + } = { ...getEntryNestedMock(), entries: 'im a string' }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "im a string" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate when "entries" contains an entry item that is type "match"', () => { + const payload = { ...getEntryNestedMock(), entries: [getEntryMatchAnyMock()] }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual({ + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host name'], + }, + ], + field: 'parent.field', + type: 'nested', + }); + }); + + test('it should validate when "entries" contains an entry item that is type "exists"', () => { + const payload = { ...getEntryNestedMock(), entries: [getEntryExistsMock()] }; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual({ + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'exists', + }, + ], + field: 'parent.field', + type: 'nested', + }); + }); + + test('it should strip out extra keys', () => { + const payload: EntryNested & { + extraKey?: string; + } = getEntryNestedMock(); + payload.extraKey = 'some extra key'; + const decoded = entriesNested.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEntryNestedMock()); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.ts new file mode 100644 index 000000000000..dac5d6a3d5a8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/entry_nested/index.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 * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; +import { nonEmptyNestedEntriesArray } from '../non_empty_nested_entries_array'; + +export const entriesNested = t.exact( + t.type({ + entries: nonEmptyNestedEntriesArray, + field: NonEmptyString, + type: t.keyof({ nested: null }), + }) +); +export type EntryNested = t.TypeOf<typeof entriesNested>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.mock.ts new file mode 100644 index 000000000000..ded6a6bfbb8a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.mock.ts @@ -0,0 +1,28 @@ +/* + * 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 { ExportExceptionDetails } from '.'; + +export interface ExportExceptionDetailsMock { + listCount?: number; + missingListsCount?: number; + missingLists?: Array<Record<'list_id', string>>; + itemCount?: number; + missingItemCount?: number; + missingItems?: Array<Record<'item_id', string>>; +} + +export const getExceptionExportDetailsMock = ( + details?: ExportExceptionDetailsMock +): ExportExceptionDetails => ({ + exported_exception_list_count: details?.listCount ?? 0, + exported_exception_list_item_count: details?.itemCount ?? 0, + missing_exception_list_item_count: details?.missingItemCount ?? 0, + missing_exception_list_items: details?.missingItems ?? [], + missing_exception_lists: details?.missingLists ?? [], + missing_exception_lists_count: details?.missingListsCount ?? 0, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.test.ts new file mode 100644 index 000000000000..fe8150fd6f3b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.test.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getExceptionExportDetailsMock } from './index.mock'; +import { exportExceptionDetailsSchema, ExportExceptionDetails } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('exportExceptionDetails', () => { + test('it should validate export meta', () => { + const payload = getExceptionExportDetailsMock(); + const decoded = exportExceptionDetailsSchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should strip out extra keys', () => { + const payload: ExportExceptionDetails & { + extraKey?: string; + } = getExceptionExportDetailsMock(); + payload.extraKey = 'some extra key'; + const decoded = exportExceptionDetailsSchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getExceptionExportDetailsMock()); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.ts new file mode 100644 index 000000000000..358e987d8b17 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_export_details/index.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 * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; + +export const exportExceptionDetails = { + exported_exception_list_count: t.number, + exported_exception_list_item_count: t.number, + missing_exception_list_item_count: t.number, + missing_exception_list_items: t.array( + t.exact( + t.type({ + item_id: NonEmptyString, + }) + ) + ), + missing_exception_lists: t.array( + t.exact( + t.type({ + list_id: NonEmptyString, + }) + ) + ), + missing_exception_lists_count: t.number, +}; + +export const exportExceptionDetailsSchema = t.exact(t.type(exportExceptionDetails)); + +export type ExportExceptionDetails = t.TypeOf<typeof exportExceptionDetailsSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.test.ts new file mode 100644 index 000000000000..ac57828355d3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { exceptionListType, ExceptionListTypeEnum } from '.'; + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('exceptionListType', () => { + test('it should validate for "detection"', () => { + const payload = 'detection'; + const decoded = exceptionListType.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate for "rule_default"', () => { + const payload = 'rule_default'; + const decoded = exceptionListType.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate for "endpoint"', () => { + const payload = 'endpoint'; + const decoded = exceptionListType.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should contain same amount of keys as enum', () => { + // Might seem like a weird test, but its meant to + // ensure that if exceptionListType is updated, you + // also update the ExceptionListTypeEnum, a workaround + // for io-ts not yet supporting enums + // https://github.com/gcanti/io-ts/issues/67 + const keys = Object.keys(exceptionListType.keys).sort().join(',').toLowerCase(); + const enumKeys = Object.keys(ExceptionListTypeEnum).sort().join(',').toLowerCase(); + + expect(keys).toEqual(enumKeys); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.ts new file mode 100644 index 000000000000..50273a9c55a9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list/index.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 * as t from 'io-ts'; + +export const exceptionListType = t.keyof({ + detection: null, + rule_default: null, + endpoint: null, + endpoint_trusted_apps: null, + endpoint_events: null, + endpoint_host_isolation_exceptions: null, + endpoint_blocklists: null, +}); +export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]); +export type ExceptionListType = t.TypeOf<typeof exceptionListType>; +export type ExceptionListTypeOrUndefined = t.TypeOf<typeof exceptionListTypeOrUndefined>; +export enum ExceptionListTypeEnum { + DETECTION = 'detection', // shared exception list type + RULE_DEFAULT = 'rule_default', // rule default, cannot be shared + ENDPOINT = 'endpoint', + ENDPOINT_TRUSTED_APPS = 'endpoint', + ENDPOINT_EVENTS = 'endpoint_events', + ENDPOINT_HOST_ISOLATION_EXCEPTIONS = 'endpoint_host_isolation_exceptions', + ENDPOINT_BLOCKLISTS = 'endpoint_blocklists', +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list_item_type/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list_item_type/index.ts new file mode 100644 index 000000000000..36857aec92df --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/exception_list_item_type/index.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. + */ + +import * as t from 'io-ts'; + +export const exceptionListItemType = t.keyof({ simple: null }); +export const exceptionListItemTypeOrUndefined = t.union([exceptionListItemType, t.undefined]); +export type ExceptionListItemType = t.TypeOf<typeof exceptionListItemType>; +export type ExceptionListItemTypeOrUndefined = t.TypeOf<typeof exceptionListItemTypeOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/expire_time/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/expire_time/index.ts new file mode 100644 index 000000000000..9a3f4db110ed --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/expire_time/index.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. + */ + +import * as t from 'io-ts'; +import { IsoDateString } from '@kbn/securitysolution-io-ts-types'; + +export const expireTime = IsoDateString; +export const expireTimeOrUndefined = t.union([expireTime, t.undefined]); +export type ExpireTimeOrUndefined = t.TypeOf<typeof expireTimeOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/file/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/file/index.ts new file mode 100644 index 000000000000..885d714de441 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/file/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const file = t.object; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/filter/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/filter/index.ts new file mode 100644 index 000000000000..43ac9592ed31 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/filter/index.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. + */ + +import * as t from 'io-ts'; + +export const filter = t.string; +export type Filter = t.TypeOf<typeof filter>; +export const filterOrUndefined = t.union([filter, t.undefined]); +export type FilterOrUndefined = t.TypeOf<typeof filterOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/id/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/id/index.ts new file mode 100644 index 000000000000..967282c158a2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/id/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; + +export const id = NonEmptyString; +export type Id = t.TypeOf<typeof id>; +export const idOrUndefined = t.union([id, t.undefined]); +export type IdOrUndefined = t.TypeOf<typeof idOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/immutable/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/immutable/index.ts new file mode 100644 index 000000000000..afeb2de36ea1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/immutable/index.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. + */ + +import * as t from 'io-ts'; + +export const immutable = t.boolean; +export type Immutable = t.TypeOf<typeof immutable>; +export const immutableOrUndefined = t.union([immutable, t.undefined]); +export type ImmutableOrUndefined = t.TypeOf<typeof immutableOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.test.ts new file mode 100644 index 000000000000..a447d1f53507 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.test.ts @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getCommentsArrayMock, getCommentsMock } from '../comment/index.mock'; +import { getCreateCommentsArrayMock } from '../create_comment/index.mock'; +import { + importComment, + ImportCommentsArray, + importCommentsArray, + ImportCommentsArrayOrUndefined, + importCommentsArrayOrUndefined, +} from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('ImportComment', () => { + describe('importComment', () => { + test('it passes validation with a typical comment', () => { + const payload = getCommentsMock(); + const decoded = importComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation with a new comment', () => { + const payload = { comment: 'new comment' }; + const decoded = importComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when undefined', () => { + const payload = undefined; + const decoded = importComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "(({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: NonEmptyString |})"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('importCommentsArray', () => { + test('it passes validation an array of Comment', () => { + const payload = getCommentsArrayMock(); + const decoded = importCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation an array of CreateComment', () => { + const payload = getCreateCommentsArrayMock(); + const decoded = importCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation an array of Comment and CreateComment', () => { + const payload = [...getCommentsArrayMock(), ...getCreateCommentsArrayMock()]; + const decoded = importCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when undefined', () => { + const payload = undefined; + const decoded = importCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "Array<(({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: NonEmptyString |})>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it fails validation when array includes non ImportComment types', () => { + const payload = [1] as unknown as ImportCommentsArray; + const decoded = importCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<(({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: NonEmptyString |})>"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('importCommentsArrayOrUndefined', () => { + test('it passes validation an array of ImportComment', () => { + const payload = [...getCommentsArrayMock(), ...getCreateCommentsArrayMock()]; + const decoded = importCommentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it passes validation when undefined', () => { + const payload = undefined; + const decoded = importCommentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it fails validation when array includes non ImportComment types', () => { + const payload = [1] as unknown as ImportCommentsArrayOrUndefined; + const decoded = importCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<(({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>) | {| comment: NonEmptyString |})>"', + ]); + expect(message.schema).toEqual({}); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.ts new file mode 100644 index 000000000000..c4c906117ecd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/import_comment/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { createComment } from '../create_comment'; +import { comment } from '../comment'; + +export const importComment = t.union([comment, createComment]); + +export type ImportComment = t.TypeOf<typeof importComment>; +export const importCommentsArray = t.array(importComment); +export type ImportCommentsArray = t.TypeOf<typeof importCommentsArray>; +export const importCommentsArrayOrUndefined = t.union([importCommentsArray, t.undefined]); +export type ImportCommentsArrayOrUndefined = t.TypeOf<typeof importCommentsArrayOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/include_expired_exceptions/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/include_expired_exceptions/index.ts new file mode 100644 index 000000000000..0c3d5e4959bc --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/include_expired_exceptions/index.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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const include_expired_exceptions = t.keyof({ true: null, false: null }); +export const includeExpiredExceptionsOrUndefined = t.union([ + include_expired_exceptions, + t.undefined, +]); +export type IncludeExpiredExceptionsOrUndefined = t.TypeOf< + typeof includeExpiredExceptionsOrUndefined +>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/index.ts new file mode 100644 index 000000000000..0ff54c5e5a72 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/index.ts @@ -0,0 +1,65 @@ +/* + * 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 './comment'; +export * from './create_comment'; +export * from './created_at'; +export * from './created_by'; +export * from './cursor'; +export * from './default_namespace'; +export * from './default_namespace_array'; +export * from './default_create_comments_array'; +export * from './default_import_comments_array'; +export * from './description'; +export * from './deserializer'; +export * from './endpoint'; +export * from './entries'; +export * from './entries_exist'; +export * from './entries_list'; +export * from './entry_match'; +export * from './entry_match_any'; +export * from './entry_match_wildcard'; +export * from './entry_nested'; +export * from './exception_export_details'; +export * from './exception_list'; +export * from './exception_list_item_type'; +export * from './expire_time'; +export * from './filter'; +export * from './id'; +export * from './immutable'; +export * from './import_comment'; +export * from './item_id'; +export * from './list_id'; +export * from './list_operator'; +export * from './list_type'; +export * from './lists'; +export * from './lists_default_array'; +export * from './max_size'; +export * from './meta'; +export * from './name'; +export * from './namespace_type'; +export * from './non_empty_entries_array'; +export * from './non_empty_nested_entries_array'; +export * from './os_type'; +export * from './page'; +export * from './per_page'; +export * from './pit'; +export * from './search'; +export * from './search_after'; +export * from './serializer'; +export * from './sort_field'; +export * from './sort_order'; +export * from './tags'; +export * from './tie_breaker_id'; +export * from './timestamp'; +export * from './total'; +export * from './type'; +export * from './underscore_version'; +export * from './update_comment'; +export * from './updated_at'; +export * from './updated_by'; +export * from './refresh'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/item/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/item/index.ts new file mode 100644 index 000000000000..5a71e1242d26 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/item/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const item = t.string; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/item_id/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/item_id/index.ts new file mode 100644 index 000000000000..44ccc97d4666 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/item_id/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; + +export const item_id = NonEmptyString; +export type ItemId = t.TypeOf<typeof item_id>; +export const itemIdOrUndefined = t.union([item_id, t.undefined]); +export type ItemIdOrUndefined = t.TypeOf<typeof itemIdOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_id/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_id/index.ts new file mode 100644 index 000000000000..c813d7170e8a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_id/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; + +export const list_id = NonEmptyString; +export type ListId = t.TypeOf<typeof list_id>; +export const list_idOrUndefined = t.union([list_id, t.undefined]); +export type ListIdOrUndefined = t.TypeOf<typeof list_idOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.test.ts new file mode 100644 index 000000000000..d3d67a8fd60a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { ListOperatorEnum as OperatorEnum, listOperator as operator } from '.'; + +describe('operator', () => { + test('it should validate for "included"', () => { + const payload = 'included'; + const decoded = operator.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate for "excluded"', () => { + const payload = 'excluded'; + const decoded = operator.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should contain same amount of keys as enum', () => { + // Might seem like a weird test, but its meant to + // ensure that if operator is updated, you + // also update the operatorEnum, a workaround + // for io-ts not yet supporting enums + // https://github.com/gcanti/io-ts/issues/67 + const keys = Object.keys(operator.keys).sort().join(',').toLowerCase(); + const enumKeys = Object.keys(OperatorEnum).sort().join(',').toLowerCase(); + + expect(keys).toEqual(enumKeys); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.ts new file mode 100644 index 000000000000..096b21ec20f9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_operator/index.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const listOperator = t.keyof({ excluded: null, included: null }); +export type ListOperator = t.TypeOf<typeof listOperator>; +export enum ListOperatorEnum { + INCLUDED = 'included', + EXCLUDED = 'excluded', +} + +export const listOperatorType = t.keyof({ + nested: null, + match: null, + match_any: null, + wildcard: null, + exists: null, + list: null, +}); +export type ListOperatorType = t.TypeOf<typeof listOperatorType>; +export enum ListOperatorTypeEnum { + NESTED = 'nested', + MATCH = 'match', + MATCH_ANY = 'match_any', + WILDCARD = 'wildcard', + EXISTS = 'exists', + LIST = 'list', +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_type/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_type/index.ts new file mode 100644 index 000000000000..7a160486c399 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/list_type/index.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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const list_type = t.keyof({ item: null, list: null }); +export type ListType = t.TypeOf<typeof list_type>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.mock.ts new file mode 100644 index 000000000000..6ad3c6a371d1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.mock.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { List, ListArray } from '.'; +import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; + +export const getListMock = (): List => ({ + id: 'some_uuid', + list_id: 'list_id_single', + namespace_type: 'single', + type: 'detection', +}); + +export const getEndpointListMock = (): List => ({ + id: ENDPOINT_LIST_ID, + list_id: ENDPOINT_LIST_ID, + namespace_type: 'agnostic', + type: 'endpoint', +}); + +export const getListArrayMock = (): ListArray => [getListMock(), getEndpointListMock()]; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.test.ts new file mode 100644 index 000000000000..aecfe9501ac1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.test.ts @@ -0,0 +1,125 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getEndpointListMock, getListArrayMock, getListMock } from './index.mock'; +import { List, list, ListArray, listArray, ListArrayOrUndefined, listArrayOrUndefined } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('Lists', () => { + describe('list', () => { + test('it should validate a list', () => { + const payload = getListMock(); + const decoded = list.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate a list with "namespace_type" of "agnostic"', () => { + const payload = getEndpointListMock(); + const decoded = list.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate a list without an "id"', () => { + const payload = getListMock(); + // @ts-expect-error + delete payload.id; + const decoded = list.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT validate a list without "namespace_type"', () => { + const payload = getListMock(); + // @ts-expect-error + delete payload.namespace_type; + const decoded = list.decode(payload); + const message = pipe(decoded, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "namespace_type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra keys', () => { + const payload: List & { + extraKey?: string; + } = getListMock(); + payload.extraKey = 'some value'; + const decoded = list.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getListMock()); + }); + }); + + describe('listArray', () => { + test('it should validate an array of lists', () => { + const payload = getListArrayMock(); + const decoded = listArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate when unexpected type found in array', () => { + const payload = [1] as unknown as ListArray; + const decoded = listArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "rule_default" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions" | "endpoint_blocklists", namespace_type: "agnostic" | "single" |}>"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('listArrayOrUndefined', () => { + test('it should validate an array of lists', () => { + const payload = getListArrayMock(); + const decoded = listArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate when undefined', () => { + const payload = undefined; + const decoded = listArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an item that is not of type "list" in array', () => { + const payload = [1] as unknown as ListArrayOrUndefined; + const decoded = listArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "rule_default" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions" | "endpoint_blocklists", namespace_type: "agnostic" | "single" |}> | undefined)"', + 'Invalid value "[1]" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "rule_default" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions" | "endpoint_blocklists", namespace_type: "agnostic" | "single" |}> | undefined)"', + ]); + expect(message.schema).toEqual({}); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.ts new file mode 100644 index 000000000000..bbc99142ffc7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; +import { exceptionListType } from '../exception_list'; +import { namespaceType } from '../default_namespace'; + +export const list = t.exact( + t.type({ + id: NonEmptyString, + list_id: NonEmptyString, + type: exceptionListType, + namespace_type: namespaceType, + }) +); + +export type List = t.TypeOf<typeof list>; +export const listArray = t.array(list); +export type ListArray = t.TypeOf<typeof listArray>; +export const listArrayOrUndefined = t.union([listArray, t.undefined]); +export type ListArrayOrUndefined = t.TypeOf<typeof listArrayOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.test.ts new file mode 100644 index 000000000000..b702f617d17c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { DefaultListArray } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getListArrayMock } from '../lists/index.mock'; + +describe('lists_default_array', () => { + test('it should return a default array when null', () => { + const payload = null; + const decoded = DefaultListArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); + + test('it should return a default array when undefined', () => { + const payload = undefined; + const decoded = DefaultListArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); + + test('it should validate an empty array', () => { + const payload: string[] = []; + const decoded = DefaultListArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of lists', () => { + const payload = getListArrayMock(); + const decoded = DefaultListArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array of non accepted types', () => { + // Terrible casting for purpose of tests + const payload = [1] as unknown; + const decoded = DefaultListArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "DefaultListArray"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.ts new file mode 100644 index 000000000000..33ab281a6f17 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/lists_default_array/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { list, ListArray } from '../lists'; + +/** + * Types the DefaultListArray as: + * - If null or undefined, then a default array of type list will be set + */ +export const DefaultListArray = new t.Type<ListArray, ListArray | undefined, unknown>( + 'DefaultListArray', + t.array(list).is, + (input, context): Either<t.Errors, ListArray> => + input == null ? t.success([]) : t.array(list).validate(input, context), + t.identity +); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.test.ts new file mode 100644 index 000000000000..2c733c93ee77 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.test.ts @@ -0,0 +1,56 @@ +/* + * 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 { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { maxSizeOrUndefined } from '.'; + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; + +describe('maxSizeOrUndefined', () => { + test('it will validate a correct max value', () => { + const payload = 123; + const decoded = maxSizeOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will fail to validate a 0', () => { + const payload = 0; + const decoded = maxSizeOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "(PositiveIntegerGreaterThanZero | undefined)"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it will fail to validate a -1', () => { + const payload = -1; + const decoded = maxSizeOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "-1" supplied to "(PositiveIntegerGreaterThanZero | undefined)"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it will fail to validate a string', () => { + const payload = '123'; + const decoded = maxSizeOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "123" supplied to "(PositiveIntegerGreaterThanZero | undefined)"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.ts new file mode 100644 index 000000000000..c652ae81d6c4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; +import * as t from 'io-ts'; + +export const max_size = PositiveIntegerGreaterThanZero; +export type MaxSize = t.TypeOf<typeof max_size>; + +export const maxSizeOrUndefined = t.union([max_size, t.undefined]); +export type MaxSizeOrUndefined = t.TypeOf<typeof maxSizeOrUndefined>; 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 new file mode 100644 index 000000000000..547b45969be7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/meta/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const meta = t.object; +export type Meta = t.TypeOf<typeof meta>; +export const metaOrUndefined = t.union([meta, t.undefined]); +export type MetaOrUndefined = t.TypeOf<typeof metaOrUndefined>; + +export const nullableMetaOrUndefined = t.union([metaOrUndefined, t.null]); +export type NullableMetaOrUndefined = t.TypeOf<typeof nullableMetaOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/name/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/name/index.ts new file mode 100644 index 000000000000..020f3642306a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/name/index.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. + */ + +import * as t from 'io-ts'; + +export const name = t.string; +export type Name = t.TypeOf<typeof name>; +export const nameOrUndefined = t.union([name, t.undefined]); +export type NameOrUndefined = t.TypeOf<typeof nameOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/namespace_type/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/namespace_type/index.ts new file mode 100644 index 000000000000..ae16f8b4521c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/namespace_type/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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { DefaultNamespace } from '../default_namespace'; + +export const namespace_type = DefaultNamespace; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.test.ts new file mode 100644 index 000000000000..321a57dd4fee --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.test.ts @@ -0,0 +1,130 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { EntriesArray } from '../entries'; +import { nonEmptyEntriesArray } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getEntryMatchMock } from '../entry_match/index.mock'; +import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; +import { + getEntriesArrayMock, + getListAndNonListEntriesArrayMock, + getListEntriesArrayMock, +} from '../entries/index.mock'; +import { getEntryNestedMock } from '../entry_nested/index.mock'; + +describe('non_empty_entries_array', () => { + test('it should FAIL validation when given an empty array', () => { + const payload: EntriesArray = []; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "NonEmptyEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given "undefined"', () => { + const payload = undefined; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "NonEmptyEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given "null"', () => { + const payload = null; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "null" supplied to "NonEmptyEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate an array of "match" entries', () => { + const payload: EntriesArray = [getEntryMatchMock(), getEntryMatchMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "match_any" entries', () => { + const payload: EntriesArray = [getEntryMatchAnyMock(), getEntryMatchAnyMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "exists" entries', () => { + const payload: EntriesArray = [getEntryExistsMock(), getEntryExistsMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "list" entries', () => { + const payload: EntriesArray = [...getListEntriesArrayMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "nested" entries', () => { + const payload: EntriesArray = [getEntryNestedMock(), getEntryNestedMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of entries', () => { + const payload: EntriesArray = [...getEntriesArrayMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when given an array of entries of value list and non-value list entries', () => { + const payload: EntriesArray = [...getListAndNonListEntriesArrayMock()]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Cannot have entry of type list and other']); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given an array of non entries', () => { + const payload = [1]; + const decoded = nonEmptyEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "NonEmptyEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.ts new file mode 100644 index 000000000000..8d6812077d4e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { entriesArray, EntriesArray } from '../entries'; +import { entriesList } from '../entries_list'; + +/** + * Types the nonEmptyEntriesArray as: + * - An array of entries of length 1 or greater + * + */ +export const nonEmptyEntriesArray = new t.Type<EntriesArray, EntriesArray, unknown>( + 'NonEmptyEntriesArray', + entriesArray.is, + (input, context): Either<t.Errors, EntriesArray> => { + if (Array.isArray(input) && input.length === 0) { + return t.failure(input, context); + } else { + if ( + Array.isArray(input) && + input.some((entry) => entriesList.is(entry)) && + input.some((entry) => !entriesList.is(entry)) + ) { + // fail when an exception item contains both a value list entry and a non-value list entry + return t.failure(input, context, 'Cannot have entry of type list and other'); + } + return entriesArray.validate(input, context); + } + }, + t.identity +); + +export type NonEmptyEntriesArray = t.OutputOf<typeof nonEmptyEntriesArray>; +export type NonEmptyEntriesArrayDecoded = t.TypeOf<typeof nonEmptyEntriesArray>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.test.ts new file mode 100644 index 000000000000..1d1d659f028c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { EntriesArray } from '../entries'; +import { nonEmptyNestedEntriesArray } from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getEntryMatchMock } from '../entry_match/index.mock'; +import { getEntryMatchAnyMock } from '../entry_match_any/index.mock'; +import { getEntryExistsMock } from '../entries_exist/index.mock'; +import { getEntryNestedMock } from '../entry_nested/index.mock'; + +describe('non_empty_nested_entries_array', () => { + test('it should FAIL validation when given an empty array', () => { + const payload: EntriesArray = []; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "NonEmptyNestedEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given "undefined"', () => { + const payload = undefined; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "NonEmptyNestedEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should FAIL validation when given "null"', () => { + const payload = null; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "null" supplied to "NonEmptyNestedEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate an array of "match" entries', () => { + const payload: EntriesArray = [getEntryMatchMock(), getEntryMatchMock()]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "match_any" entries', () => { + const payload: EntriesArray = [getEntryMatchAnyMock(), getEntryMatchAnyMock()]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of "exists" entries', () => { + const payload: EntriesArray = [getEntryExistsMock(), getEntryExistsMock()]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when given an array of "nested" entries', () => { + const payload: EntriesArray = [getEntryNestedMock(), getEntryNestedMock()]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "operator"', + 'Invalid value "nested" supplied to "type"', + 'Invalid value "undefined" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should validate an array of entries', () => { + const payload: EntriesArray = [ + getEntryExistsMock(), + getEntryMatchAnyMock(), + getEntryMatchMock(), + ]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should FAIL validation when given an array of non entries', () => { + const payload = [1]; + const decoded = nonEmptyNestedEntriesArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "NonEmptyNestedEntriesArray"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.ts new file mode 100644 index 000000000000..eb2d1074b9f4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_nested_entries_array/index.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { entriesMatch } from '../entry_match'; +import { entriesMatchAny } from '../entry_match_any'; +import { entriesExists } from '../entries_exist'; + +export const nestedEntryItem = t.union([entriesMatch, entriesMatchAny, entriesExists]); +export const nestedEntriesArray = t.array(nestedEntryItem); +export type NestedEntriesArray = t.TypeOf<typeof nestedEntriesArray>; + +/** + * Types the nonEmptyNestedEntriesArray as: + * - An array of entries of length 1 or greater + * + */ +export const nonEmptyNestedEntriesArray = new t.Type< + NestedEntriesArray, + NestedEntriesArray, + unknown +>( + 'NonEmptyNestedEntriesArray', + nestedEntriesArray.is, + (input, context): Either<t.Errors, NestedEntriesArray> => { + if (Array.isArray(input) && input.length === 0) { + return t.failure(input, context); + } else { + return nestedEntriesArray.validate(input, context); + } + }, + t.identity +); + +export type NonEmptyNestedEntriesArray = t.OutputOf<typeof nonEmptyNestedEntriesArray>; +export type NonEmptyNestedEntriesArrayDecoded = t.TypeOf<typeof nonEmptyNestedEntriesArray>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.test.ts new file mode 100644 index 000000000000..3b8ca29afa4e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.test.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { osType, osTypeArrayOrUndefined } from '.'; + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; + +describe('osType', () => { + test('it will validate a correct osType', () => { + const payload = 'windows'; + const decoded = osType.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will fail to validate an incorrect osType', () => { + const payload = 'foo'; + const decoded = osType.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "foo" supplied to ""linux" | "macos" | "windows""', + ]); + expect(message.schema).toEqual({}); + }); + + test('it will default to an empty array when osTypeArrayOrUndefined is used', () => { + const payload = undefined; + const decoded = osTypeArrayOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.ts new file mode 100644 index 000000000000..6e8cbb85b631 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/os_type/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { DefaultArray } from '@kbn/securitysolution-io-ts-types'; + +export const osType = t.keyof({ + linux: null, + macos: null, + windows: null, +}); +export type OsType = t.TypeOf<typeof osType>; + +export const osTypeArray = DefaultArray(osType); +export type OsTypeArray = t.TypeOf<typeof osTypeArray>; + +export const osTypeArrayOrUndefined = t.union([osTypeArray, t.undefined]); +export type OsTypeArrayOrUndefined = t.OutputOf<typeof osTypeArray>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/page/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/page/index.ts new file mode 100644 index 000000000000..36faf8e7e5e2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/page/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const page = t.number; // TODO: Change this out for PositiveNumber from siem +export type Page = t.TypeOf<typeof page>; + +export const pageOrUndefined = t.union([page, t.undefined]); +export type PageOrUndefined = t.TypeOf<typeof pageOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/per_page/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/per_page/index.ts new file mode 100644 index 000000000000..669e0699b366 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/per_page/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const per_page = t.number; // TODO: Change this out for PositiveNumber from siem +export type PerPage = t.TypeOf<typeof per_page>; + +export const perPageOrUndefined = t.union([per_page, t.undefined]); +export type PerPageOrUndefined = t.TypeOf<typeof perPageOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.test.ts new file mode 100644 index 000000000000..9cafd8ed5ec0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { pitOrUndefined } from '.'; + +import * as t from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; + +describe('pitOrUndefined', () => { + test('it will validate a correct pit', () => { + const payload = { id: '123', keepAlive: '1m' }; + const decoded = pitOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will validate with the value of "undefined"', () => { + const obj = t.exact( + t.type({ + pit_id: pitOrUndefined, + }) + ); + const payload: t.TypeOf<typeof obj> = { + pit_id: undefined, + }; + const decoded = obj.decode({ + pit_id: undefined, + }); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will validate a correct pit without having a "keepAlive"', () => { + const payload = { id: '123' }; + const decoded = pitOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will fail to validate an incorrect pit', () => { + const payload = 'foo'; + const decoded = pitOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "foo" supplied to "({| id: string, keepAlive: (string | undefined) |} | undefined)"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.ts new file mode 100644 index 000000000000..97b6957e07e5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const pitId = t.string; +export const pit = t.exact( + t.type({ + id: pitId, + keepAlive: t.union([t.string, t.undefined]), + }) +); +export const pitOrUndefined = t.union([pit, t.undefined]); + +export type Pit = t.TypeOf<typeof pit>; +export type PitId = t.TypeOf<typeof pitId>; +export type PitOrUndefined = t.TypeOf<typeof pitOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/refresh/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/refresh/index.ts new file mode 100644 index 000000000000..d1a7fe34fc70 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/refresh/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const refresh = t.union([t.literal('true'), t.literal('false')]); +export const refreshWithWaitFor = t.union([ + t.literal('true'), + t.literal('false'), + t.literal('wait_for'), +]); +export type Refresh = t.TypeOf<typeof refresh>; +export type RefreshWithWaitFor = t.TypeOf<typeof refreshWithWaitFor>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/required_keep_undefined/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/required_keep_undefined/index.ts new file mode 100644 index 000000000000..5854fc42a3a7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/required_keep_undefined/index.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. + */ + +/** + * This makes any optional property the same as Required<T> would but also has the + * added benefit of keeping your undefined. + * + * For example: + * type A = RequiredKeepUndefined<{ a?: undefined; b: number }>; + * + * will yield a type of: + * type A = { a: undefined; b: number; } + * @deprecated This has no replacement. We should stop using/relying on this and just remove it. + */ +export type RequiredKeepUndefined<T> = { [K in keyof T]-?: [T[K]] } extends infer U + ? U extends Record<keyof U, [unknown]> + ? { [K in keyof U]: U[K][0] } + : never + : never; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.test.ts new file mode 100644 index 000000000000..6c45f50c9c89 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { searchOrUndefined } from '.'; + +import * as t from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; + +describe('search', () => { + test('it will validate a correct search', () => { + const payload = 'name:foo'; + const decoded = searchOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will validate with the value of "undefined"', () => { + const obj = t.exact( + t.type({ + search: searchOrUndefined, + }) + ); + const payload: t.TypeOf<typeof obj> = { + search: undefined, + }; + const decoded = obj.decode({ + pit_id: undefined, + }); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will fail to validate an incorrect search', () => { + const payload = ['foo']; + const decoded = searchOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "["foo"]" supplied to "(string | undefined)"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts new file mode 100644 index 000000000000..b93c4631cd22 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const search = t.string; +export type Search = t.TypeOf<typeof search>; + +export const searchOrUndefined = t.union([search, t.undefined]); +export type SearchOrUndefined = t.TypeOf<typeof searchOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.test.ts new file mode 100644 index 000000000000..bc176af4fa30 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { searchAfterOrUndefined } from '.'; + +import * as t from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; + +describe('searchAfter', () => { + test('it will validate a correct search_after', () => { + const payload = ['test-1', 'test-2']; + const decoded = searchAfterOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will validate with the value of "undefined"', () => { + const obj = t.exact( + t.type({ + search_after: searchAfterOrUndefined, + }) + ); + const payload: t.TypeOf<typeof obj> = { + search_after: undefined, + }; + const decoded = obj.decode({ + pit_id: undefined, + }); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will fail to validate an incorrect search_after', () => { + const payload = 'foo'; + const decoded = searchAfterOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "foo" supplied to "(Array<string> | undefined)"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.ts new file mode 100644 index 000000000000..17735728d341 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const search_after = t.array(t.string); +export type SearchAfter = t.TypeOf<typeof search_after>; + +export const searchAfterOrUndefined = t.union([search_after, t.undefined]); +export type SearchAfterOrUndefined = t.TypeOf<typeof searchAfterOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/serializer/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/serializer/index.ts new file mode 100644 index 000000000000..28322ff2e991 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/serializer/index.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. + */ + +import * as t from 'io-ts'; + +export const serializer = t.string; +export type Serializer = t.TypeOf<typeof serializer>; +export const serializerOrUndefined = t.union([serializer, t.undefined]); +export type SerializerOrUndefined = t.TypeOf<typeof serializerOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_field/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_field/index.ts new file mode 100644 index 000000000000..762fdf80c227 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_field/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const sort_field = t.string; +export const sortFieldOrUndefined = t.union([sort_field, t.undefined]); +export type SortFieldOrUndefined = t.TypeOf<typeof sortFieldOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_order/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_order/index.ts new file mode 100644 index 000000000000..a849e45c316e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/sort_order/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const sort_order = t.keyof({ asc: null, desc: null }); +export const sortOrderOrUndefined = t.union([sort_order, t.undefined]); +export type SortOrderOrUndefined = t.TypeOf<typeof sortOrderOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/tags/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/tags/index.ts new file mode 100644 index 000000000000..0ad756f05771 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/tags/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { DefaultStringArray } from '@kbn/securitysolution-io-ts-types'; + +export const tags = DefaultStringArray; +export type Tags = t.TypeOf<typeof tags>; +export const tagsOrUndefined = t.union([tags, t.undefined]); +export type TagsOrUndefined = t.TypeOf<typeof tagsOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/tie_breaker_id/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/tie_breaker_id/index.ts new file mode 100644 index 000000000000..362e50089081 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/tie_breaker_id/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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const tie_breaker_id = t.string; // TODO: Use UUID for this instead of a string for validation diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/timestamp/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/timestamp/index.ts new file mode 100644 index 000000000000..30472bcd101f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/timestamp/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. + */ + +import * as t from 'io-ts'; +import { IsoDateString } from '@kbn/securitysolution-io-ts-types'; + +export const timestamp = IsoDateString; +export const timestampOrUndefined = t.union([IsoDateString, t.undefined]); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/total/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/total/index.ts new file mode 100644 index 000000000000..f67ca0f2fc5e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/total/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. + */ + +import * as t from 'io-ts'; + +export const total = t.number; // TODO: Change this out for PositiveNumber from siem +export const totalUndefined = t.union([total, t.undefined]); +export type TotalOrUndefined = t.TypeOf<typeof totalUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.test.ts new file mode 100644 index 000000000000..d87ea94728b1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.test.ts @@ -0,0 +1,33 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { Type, type } from '.'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('type', () => { + test('it will work with a given expected type', () => { + const payload: Type = 'keyword'; + const decoded = type.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will give an error if given a type that does not exist', () => { + const payload: Type | 'madeup' = 'madeup'; + const decoded = type.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "madeup" supplied to ""binary" | "boolean" | "byte" | "date" | "date_nanos" | "date_range" | "double" | "double_range" | "float" | "float_range" | "geo_point" | "geo_shape" | "half_float" | "integer" | "integer_range" | "ip" | "ip_range" | "keyword" | "long" | "long_range" | "shape" | "short" | "text""', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.ts new file mode 100644 index 000000000000..5a1d234d5f2f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +/** + * Types of all the regular single value list items but not exception list + * or exception list types. Those types are in the list_types folder. + */ +export const type = t.keyof({ + binary: null, + boolean: null, + byte: null, + date: null, + date_nanos: null, + date_range: null, + double: null, + double_range: null, + float: null, + float_range: null, + geo_point: null, + geo_shape: null, + half_float: null, + integer: null, + integer_range: null, + ip: null, + ip_range: null, + keyword: null, + long: null, + long_range: null, + shape: null, + short: null, + text: null, +}); + +export const typeOrUndefined = t.union([type, t.undefined]); +export type Type = t.TypeOf<typeof type>; +export type TypeOrUndefined = t.TypeOf<typeof typeOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/underscore_version/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/underscore_version/index.ts new file mode 100644 index 000000000000..6e47b89af816 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/underscore_version/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. + */ + +import * as t from 'io-ts'; + +export const _version = t.string; +export const _versionOrUndefined = t.union([_version, t.undefined]); +export type _VersionOrUndefined = t.TypeOf<typeof _versionOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.mock.ts new file mode 100644 index 000000000000..c0cbfe5e3019 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.mock.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 { UpdateComment, UpdateCommentsArray } from '.'; +import { ID } from '../../constants/index.mock'; + +export const getUpdateCommentMock = (): UpdateComment => ({ + comment: 'some comment', + id: ID, +}); + +export const getUpdateCommentsArrayMock = (): UpdateCommentsArray => [ + getUpdateCommentMock(), + getUpdateCommentMock(), +]; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.test.ts new file mode 100644 index 000000000000..c1f1be8be9c1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.test.ts @@ -0,0 +1,148 @@ +/* + * 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 { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { getUpdateCommentMock, getUpdateCommentsArrayMock } from './index.mock'; +import { + UpdateComment, + updateComment, + UpdateCommentsArray, + updateCommentsArray, + UpdateCommentsArrayOrUndefined, + updateCommentsArrayOrUndefined, +} from '.'; +import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +describe('UpdateComment', () => { + describe('updateComment', () => { + test('it should pass validation when supplied typical comment update', () => { + const payload = getUpdateCommentMock(); + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an undefined for "comment"', () => { + const payload = getUpdateCommentMock(); + // @ts-expect-error + delete payload.comment; + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "comment"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an empty string for "comment"', () => { + const payload = { ...getUpdateCommentMock(), comment: '' }; + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "comment"']); + expect(message.schema).toEqual({}); + }); + + test('it should pass validation when supplied an undefined for "id"', () => { + const payload = getUpdateCommentMock(); + delete payload.id; + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an empty string for "id"', () => { + const payload = { ...getUpdateCommentMock(), id: '' }; + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should strip out extra key passed in', () => { + const payload: UpdateComment & { + extraKey?: string; + } = { ...getUpdateCommentMock(), extraKey: 'some new value' }; + const decoded = updateComment.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getUpdateCommentMock()); + }); + }); + + describe('updateCommentsArray', () => { + test('it should pass validation when supplied an array of comments', () => { + const payload = getUpdateCommentsArrayMock(); + const decoded = updateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when undefined', () => { + const payload = undefined; + const decoded = updateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when array includes non comments types', () => { + const payload = [1] as unknown as UpdateCommentsArray; + const decoded = updateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + }); + + describe('updateCommentsArrayOrUndefined', () => { + test('it should pass validation when supplied an array of comments', () => { + const payload = getUpdateCommentsArrayMock(); + const decoded = updateCommentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should pass validation when supplied when undefined', () => { + const payload = undefined; + const decoded = updateCommentsArrayOrUndefined.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when array includes non comments types', () => { + const payload = [1] as unknown as UpdateCommentsArrayOrUndefined; + const decoded = updateCommentsArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"', + ]); + expect(message.schema).toEqual({}); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.ts new file mode 100644 index 000000000000..6a0b4b08c772 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/update_comment/index.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 * as t from 'io-ts'; +import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; +import { id } from '../id'; + +export const updateComment = t.intersection([ + t.exact( + t.type({ + comment: NonEmptyString, + }) + ), + t.exact( + t.partial({ + id, + }) + ), +]); + +export type UpdateComment = t.TypeOf<typeof updateComment>; +export const updateCommentsArray = t.array(updateComment); +export type UpdateCommentsArray = t.TypeOf<typeof updateCommentsArray>; +export const updateCommentsArrayOrUndefined = t.union([updateCommentsArray, t.undefined]); +export type UpdateCommentsArrayOrUndefined = t.TypeOf<typeof updateCommentsArrayOrUndefined>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_at/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_at/index.ts new file mode 100644 index 000000000000..bafbadcd8f09 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_at/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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const updated_at = t.string; // TODO: Make this into an ISO Date string check diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_by/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_by/index.ts new file mode 100644 index 000000000000..58c5b2d666bd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/updated_by/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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as t from 'io-ts'; + +export const updated_by = t.string; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/value/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/value/index.ts new file mode 100644 index 000000000000..38530a942275 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/value/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. + */ + +import * as t from 'io-ts'; + +export const value = t.string; +export const valueOrUndefined = t.union([value, t.undefined]); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.mock.ts new file mode 100644 index 000000000000..ae91d63d6b7e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.mock.ts @@ -0,0 +1,113 @@ +/* + * 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 { EndpointEntriesArray } from '../common/endpoint/entries'; +import { EntriesArray, Entry } from '../common/entries'; +import { EntryMatch } from '../common/entry_match'; +import { EntryNested } from '../common/entry_nested'; +import { OsTypeArray } from '../common/os_type'; + +export const DATE_NOW = '2020-04-20T15:25:31.830Z'; +export const OLD_DATE_RELATIVE_TO_DATE_NOW = '2020-04-19T15:25:31.830Z'; +export const USER = 'some user'; +export const ELASTIC_USER = 'elastic'; +export const LIST_INDEX = '.lists'; +export const LIST_ITEM_INDEX = '.items'; +export const NAME = 'some name'; +export const DESCRIPTION = 'some description'; +export const LIST_ID = 'some-list-id'; +export const LIST_ITEM_ID = 'some-list-item-id'; +export const TIE_BREAKER = '6a76b69d-80df-4ab2-8c3e-85f466b06a0e'; +export const TIE_BREAKERS = [ + '21530991-4051-46ec-bc35-2afa09a1b0b5', + '3c662054-ae37-4aa9-9936-3e8e2ea26775', + '60e49a20-3a23-48b6-8bf9-ed5e3b70f7a0', + '38814080-a40f-4358-992a-3b875f9b7dec', + '29fa61be-aaaf-411c-a78a-7059e3f723f1', + '9c19c959-cb9d-4cd2-99e4-1ea2baf0ef0e', + 'd409308c-f94b-4b3a-8234-bbd7a80c9140', + '87824c99-cd83-45c4-8aa6-4ad95dfea62c', + '7b940c17-9355-479f-b882-f3e575718f79', + '5983ad0c-4ef4-4fa0-8308-80ab9ecc4f74', +]; +export const META = {}; +export const TYPE = 'ip'; +export const VALUE = '127.0.0.1'; +export const VALUE_2 = '255.255.255'; +export const NAMESPACE_TYPE = 'single'; +export const NESTED_FIELD = 'parent.field'; + +// Exception List specific +export const ID = 'uuid_here'; +export const ITEM_ID = 'some-list-item-id'; +export const DETECTION_TYPE = 'detection'; +export const ENDPOINT_TYPE = 'endpoint'; +export const FIELD = 'host.name'; +export const OPERATOR = 'included'; +export const OPERATOR_EXCLUDED = 'excluded'; +export const ENTRY_VALUE = 'some host name'; +export const MATCH = 'match'; +export const MATCH_ANY = 'match_any'; +export const WILDCARD = 'wildcard'; +export const MAX_IMPORT_PAYLOAD_BYTES = 9000000; +export const IMPORT_BUFFER_SIZE = 1000; +export const LIST = 'list'; +export const EXISTS = 'exists'; +export const NESTED = 'nested'; +export const ENTRIES: EntriesArray = [ + { + entries: [{ field: 'nested.field', operator: 'included', type: 'match', value: 'some value' }], + field: 'some.parentField', + type: 'nested', + }, + { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, +]; +export const ENDPOINT_ENTRIES: EndpointEntriesArray = [ + { + entries: [{ field: 'nested.field', operator: 'included', type: 'match', value: 'some value' }], + field: 'some.parentField', + type: 'nested', + }, + { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, +]; +// ENTRIES_WITH_IDS should only be used to mock out functionality of a collection of transforms +// that are UI specific and useful for UI concerns that are inserted between the +// API and the actual user interface. In some ways these might be viewed as +// technical debt or to compensate for the differences and preferences +// of how ReactJS might prefer data vs. how we want to model data. +export const ENTRIES_WITH_IDS: EntriesArray = [ + { + entries: [ + { + field: 'nested.field', + id: '123', + operator: 'included', + type: 'match', + value: 'some value', + } as EntryMatch & { id: string }, + ], + field: 'some.parentField', + id: '123', + type: 'nested', + } as EntryNested & { id: string }, + { + field: 'some.not.nested.field', + id: '123', + operator: 'included', + type: 'match', + value: 'some value', + } as Entry & { id: string }, +]; +export const ITEM_TYPE = 'simple'; +export const OS_TYPES: OsTypeArray = ['windows']; +export const TAGS = []; +export const COMMENTS = []; +export const FILTER = 'name:Nicolas Bourbaki'; +export const CURSOR = 'c29tZXN0cmluZ2ZvcnlvdQ=='; +export const _VERSION = 'WzI5NywxXQ=='; +export const VERSION = 1; +export const IMMUTABLE = false; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..0efac9d4de7f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.mock.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 { CreateEndpointListItemSchema } from '.'; +import { + COMMENTS, + DESCRIPTION, + ENDPOINT_ENTRIES, + ITEM_TYPE, + META, + NAME, + OS_TYPES, + TAGS, +} from '../../constants/index.mock'; + +export const getCreateEndpointListItemSchemaMock = (): CreateEndpointListItemSchema => ({ + comments: COMMENTS, + description: DESCRIPTION, + entries: ENDPOINT_ENTRIES, + item_id: undefined, + meta: META, + name: NAME, + os_types: OS_TYPES, + tags: TAGS, + type: ITEM_TYPE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.test.ts new file mode 100644 index 000000000000..78d578dd5d47 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.test.ts @@ -0,0 +1,205 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { getCreateEndpointListItemSchemaMock } from './index.mock'; +import { CreateEndpointListItemSchema, createEndpointListItemSchema } from '.'; +import { getCreateCommentsArrayMock } from '../../common/create_comment/index.mock'; +import { getCommentsMock } from '../../common/comment/index.mock'; +import { CommentsArray } from '../../common/comment'; + +describe('create_endpoint_list_item_schema', () => { + test('it should pass validation when supplied a typical list item request not counting the auto generated uuid', () => { + const payload = getCreateEndpointListItemSchemaMock(); + const decoded = createEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateEndpointListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an undefined for "description"', () => { + const payload = getCreateEndpointListItemSchemaMock(); + // @ts-expect-error + delete payload.description; + const decoded = createEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an undefined for "name"', () => { + const payload = getCreateEndpointListItemSchemaMock(); + // @ts-expect-error + delete payload.name; + const decoded = createEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "name"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an undefined for "type"', () => { + const payload = getCreateEndpointListItemSchemaMock(); + // @ts-expect-error + delete payload.type; + const decoded = createEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied a "list_id" since it does not required one', () => { + const inputPayload: CreateEndpointListItemSchema & { list_id: string } = { + ...getCreateEndpointListItemSchemaMock(), + list_id: 'list-123', + }; + const decoded = createEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "list_id"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied a "namespace_type" since it does not required one', () => { + const inputPayload: CreateEndpointListItemSchema & { namespace_type: string } = { + ...getCreateEndpointListItemSchemaMock(), + namespace_type: 'single', + }; + const decoded = createEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "namespace_type"']); + expect(message.schema).toEqual({}); + }); + + test('it should pass validation when supplied an undefined for "meta" but strip it out and generate a correct body not counting the auto generated uuid', () => { + const payload = getCreateEndpointListItemSchemaMock(); + const outputPayload = getCreateEndpointListItemSchemaMock(); + delete payload.meta; + delete outputPayload.meta; + const decoded = createEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateEndpointListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should pass validation when supplied an undefined for "comments" but return an array and generate a correct body not counting the auto generated uuid', () => { + const inputPayload = getCreateEndpointListItemSchemaMock(); + const outputPayload = getCreateEndpointListItemSchemaMock(); + delete inputPayload.comments; + outputPayload.comments = []; + const decoded = createEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateEndpointListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should pass validation when supplied "comments" array', () => { + const inputPayload = { + ...getCreateEndpointListItemSchemaMock(), + comments: getCreateCommentsArrayMock(), + }; + const decoded = createEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateEndpointListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(inputPayload); + }); + + test('it should fail validation when supplied "comments" with "created_at", "created_by", or "id" values', () => { + const inputPayload: Omit<CreateEndpointListItemSchema, 'comments'> & { + comments?: CommentsArray; + } = { + ...getCreateEndpointListItemSchemaMock(), + comments: [getCommentsMock()], + }; + const decoded = createEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "created_at,created_by,id"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an undefined for "entries"', () => { + const inputPayload = getCreateEndpointListItemSchemaMock(); + const outputPayload = getCreateEndpointListItemSchemaMock(); + // @ts-expect-error + delete inputPayload.entries; + outputPayload.entries = []; + const decoded = createEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateEndpointListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should pass validation when supplied an undefined for "tags" but return an array and generate a correct body not counting the auto generated uuid', () => { + const inputPayload = getCreateEndpointListItemSchemaMock(); + const outputPayload = getCreateEndpointListItemSchemaMock(); + delete inputPayload.tags; + outputPayload.tags = []; + const decoded = createEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateEndpointListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => { + const inputPayload = getCreateEndpointListItemSchemaMock(); + delete inputPayload.item_id; + const decoded = createEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect((message.schema as CreateEndpointListItemSchema).item_id).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i + ); + }); + + test('it should pass validation when supplied an undefined for "item_id" and generate a correct body not counting the uuid', () => { + const inputPayload = getCreateEndpointListItemSchemaMock(); + delete inputPayload.item_id; + const decoded = createEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateEndpointListItemSchema).item_id; + expect(message.schema).toEqual(inputPayload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: CreateEndpointListItemSchema & { + extraKey: string; + } = { ...getCreateEndpointListItemSchemaMock(), extraKey: 'some new value' }; + const decoded = createEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.ts new file mode 100644 index 000000000000..08fd5e339ade --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { DefaultUuid } from '@kbn/securitysolution-io-ts-types'; +import { nonEmptyEndpointEntriesArray } from '../../common/endpoint/entries'; +import { exceptionListItemType } from '../../common/exception_list_item_type'; +import { DefaultCreateCommentsArray } from '../../common/default_create_comments_array'; +import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { CreateCommentsArray } from '../../common/create_comment'; +import { Tags, tags } from '../../common/tags'; +import { ItemId } from '../../common/item_id'; +import { EntriesArray } from '../../common/entries'; +import { description } from '../../common/description'; +import { name } from '../../common/name'; +import { meta } from '../../common/meta'; + +export const createEndpointListItemSchema = t.intersection([ + t.exact( + t.type({ + description, + entries: nonEmptyEndpointEntriesArray, + name, + type: exceptionListItemType, + }) + ), + t.exact( + t.partial({ + comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode + item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode + meta, // defaults to undefined if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type CreateEndpointListItemSchema = t.OutputOf<typeof createEndpointListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type CreateEndpointListItemSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof createEndpointListItemSchema>>, + 'tags' | 'item_id' | 'entries' | 'comments' | 'os_types' +> & { + comments: CreateCommentsArray; + tags: Tags; + item_id: ItemId; + entries: EntriesArray; + os_types: OsTypeArray; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..4ccc82bf2af0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.mock.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CreateExceptionListItemSchema } from '.'; +import { + COMMENTS, + DESCRIPTION, + ENTRIES, + ITEM_ID, + ITEM_TYPE, + LIST_ID, + META, + NAME, + NAMESPACE_TYPE, + OS_TYPES, + TAGS, +} from '../../constants/index.mock'; + +export const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemSchema => ({ + comments: COMMENTS, + description: DESCRIPTION, + entries: ENTRIES, + item_id: undefined, + list_id: LIST_ID, + meta: META, + name: NAME, + namespace_type: NAMESPACE_TYPE, + os_types: OS_TYPES, + tags: TAGS, + type: ITEM_TYPE, +}); + +/** + * Useful for end to end testing + */ +export const getCreateExceptionListItemMinimalSchemaMock = (): CreateExceptionListItemSchema => ({ + description: DESCRIPTION, + entries: ENTRIES, + item_id: ITEM_ID, + list_id: LIST_ID, + name: NAME, + os_types: OS_TYPES, + type: ITEM_TYPE, +}); + +/** + * Useful for end to end testing + */ +export const getCreateExceptionListItemMinimalSchemaMockWithoutId = + (): CreateExceptionListItemSchema => ({ + description: DESCRIPTION, + entries: ENTRIES, + list_id: LIST_ID, + name: NAME, + os_types: OS_TYPES, + type: ITEM_TYPE, + }); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.test.ts new file mode 100644 index 000000000000..4d03b83112a3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.test.ts @@ -0,0 +1,209 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getCreateExceptionListItemSchemaMock } from './index.mock'; +import { CreateExceptionListItemSchema, createExceptionListItemSchema } from '.'; +import { getCreateCommentsArrayMock } from '../../common/create_comment/index.mock'; +import { getCommentsMock } from '../../common/comment/index.mock'; +import { CommentsArray } from '../../common/comment'; + +describe('create_exception_list_item_schema', () => { + test('it should pass validation when supplied a typical exception list item request not counting the auto generated uuid', () => { + const payload = getCreateExceptionListItemSchemaMock(); + const decoded = createExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should fail validation when supplied an undefined for "description"', () => { + const payload = getCreateExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.description; + const decoded = createExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an undefined for "name"', () => { + const payload = getCreateExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.name; + const decoded = createExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "name"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an undefined for "type"', () => { + const payload = getCreateExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.type; + const decoded = createExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an undefined for "list_id"', () => { + const inputPayload = getCreateExceptionListItemSchemaMock(); + // @ts-expect-error + delete inputPayload.list_id; + const decoded = createExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should pass validation when supplied an undefined for "meta" but strip it out and generate a correct body not counting the auto generated uuid', () => { + const payload = getCreateExceptionListItemSchemaMock(); + const outputPayload = getCreateExceptionListItemSchemaMock(); + delete payload.meta; + delete outputPayload.meta; + const decoded = createExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should pass validation when supplied an undefined for "comments" but return an array and generate a correct body not counting the auto generated uuid', () => { + const inputPayload = getCreateExceptionListItemSchemaMock(); + const outputPayload = getCreateExceptionListItemSchemaMock(); + delete inputPayload.comments; + outputPayload.comments = []; + const decoded = createExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should pass validation when supplied "comments" array', () => { + const inputPayload = { + ...getCreateExceptionListItemSchemaMock(), + comments: getCreateCommentsArrayMock(), + }; + const decoded = createExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(inputPayload); + }); + + test('it should fail validation when supplied "comments" with "created_at" or "created_by" values', () => { + const inputPayload: Omit<CreateExceptionListItemSchema, 'comments'> & { + comments?: CommentsArray; + } = { + ...getCreateExceptionListItemSchemaMock(), + comments: [getCommentsMock()], + }; + const decoded = createExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "created_at,created_by,id"']); + expect(message.schema).toEqual({}); + }); + + test('it should fail validation when supplied an undefined for "entries"', () => { + const inputPayload = getCreateExceptionListItemSchemaMock(); + const outputPayload = getCreateExceptionListItemSchemaMock(); + // @ts-expect-error + delete inputPayload.entries; + outputPayload.entries = []; + const decoded = createExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should pass validation when supplied an undefined for "namespace_type" but return enum "single" and generate a correct body not counting the auto generated uuid', () => { + const inputPayload = getCreateExceptionListItemSchemaMock(); + const outputPayload = getCreateExceptionListItemSchemaMock(); + delete inputPayload.namespace_type; + outputPayload.namespace_type = 'single'; + const decoded = createExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should pass validation when supplied an undefined for "tags" but return an array and generate a correct body not counting the auto generated uuid', () => { + const inputPayload = getCreateExceptionListItemSchemaMock(); + const outputPayload = getCreateExceptionListItemSchemaMock(); + delete inputPayload.tags; + outputPayload.tags = []; + const decoded = createExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListItemSchema).item_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => { + const inputPayload = getCreateExceptionListItemSchemaMock(); + delete inputPayload.item_id; + const decoded = createExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect((message.schema as CreateExceptionListItemSchema).item_id).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i + ); + }); + + test('it should pass validation when supplied an undefined for "item_id" and generate a correct body not counting the uuid', () => { + const inputPayload = getCreateExceptionListItemSchemaMock(); + delete inputPayload.item_id; + const decoded = createExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListItemSchema).item_id; + expect(message.schema).toEqual(inputPayload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: CreateExceptionListItemSchema & { + extraKey?: string; + } = getCreateExceptionListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = createExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.ts new file mode 100644 index 000000000000..9276a0dafa8b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { DefaultUuid } from '@kbn/securitysolution-io-ts-types'; + +import { DefaultCreateCommentsArray } from '../../common/default_create_comments_array'; +import { CreateCommentsArray } from '../../common/create_comment'; +import { Tags, tags } from '../../common/tags'; +import { ItemId } from '../../common/item_id'; +import { EntriesArray } from '../../common/entries'; +import { NamespaceType } from '../../common/default_namespace'; +import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { description } from '../../common/description'; +import { list_id } from '../../common/list_id'; +import { name } from '../../common/name'; +import { exceptionListItemType } from '../../common/exception_list_item_type'; +import { meta } from '../../common/meta'; +import { namespace_type } from '../../common/namespace_type'; +import { nonEmptyEntriesArray } from '../../common/non_empty_entries_array'; +import { ExpireTimeOrUndefined, expireTimeOrUndefined } from '../../common'; + +export const createExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + description, + entries: nonEmptyEntriesArray, + list_id, + name, + type: exceptionListItemType, + }) + ), + t.exact( + t.partial({ + comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode + expire_time: expireTimeOrUndefined, + item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode + meta, // defaults to undefined if not set during decode + namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type CreateExceptionListItemSchema = t.OutputOf<typeof createExceptionListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type CreateExceptionListItemSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof createExceptionListItemSchema>>, + 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' | 'expire_time' +> & { + comments: CreateCommentsArray; + expire_time: ExpireTimeOrUndefined; + tags: Tags; + item_id: ItemId; + entries: EntriesArray; + namespace_type: NamespaceType; + os_types: OsTypeArray; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.mock.ts new file mode 100644 index 000000000000..2110a0056f0c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.mock.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + DESCRIPTION, + ENDPOINT_TYPE, + LIST_ID, + META, + NAME, + NAMESPACE_TYPE, + VERSION, +} from '../../constants/index.mock'; + +import { CreateExceptionListSchema } from '.'; + +export const getCreateExceptionListSchemaMock = (): CreateExceptionListSchema => ({ + description: DESCRIPTION, + list_id: undefined, + meta: META, + name: NAME, + namespace_type: NAMESPACE_TYPE, + os_types: [], + tags: [], + type: ENDPOINT_TYPE, + version: VERSION, +}); + +/** + * Useful for end to end testing + */ +export const getCreateExceptionListMinimalSchemaMock = (): CreateExceptionListSchema => ({ + description: DESCRIPTION, + list_id: LIST_ID, + name: NAME, + type: ENDPOINT_TYPE, +}); + +/** + * Useful for end to end testing + */ +export const getCreateExceptionListMinimalSchemaMockWithoutId = (): CreateExceptionListSchema => ({ + description: DESCRIPTION, + name: NAME, + type: ENDPOINT_TYPE, +}); + +/** + * Useful for end to end testing with detections + */ +export const getCreateExceptionListDetectionSchemaMock = (): CreateExceptionListSchema => ({ + description: DESCRIPTION, + list_id: LIST_ID, + name: NAME, + type: 'detection', +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.test.ts new file mode 100644 index 000000000000..26399804591e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.test.ts @@ -0,0 +1,83 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { CreateExceptionListSchema, createExceptionListSchema } from '.'; +import { getCreateExceptionListSchemaMock } from './index.mock'; + +describe('create_exception_list_schema', () => { + test('it should validate a typical exception lists request and generate a correct body not counting the uuid', () => { + const payload = getCreateExceptionListSchemaMock(); + const decoded = createExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListSchema).list_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "meta" and generate a correct body not counting the uuid', () => { + const payload = getCreateExceptionListSchemaMock(); + delete payload.meta; + const decoded = createExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListSchema).list_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "tags" but return an array and generate a correct body not counting the uuid', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + const outputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload.tags; + outputPayload.tags = []; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListSchema).list_id; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for "list_id" and auto generate a uuid', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload.list_id; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect((message.schema as CreateExceptionListSchema).list_id).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i + ); + }); + + test('it should accept an undefined for "list_id" and generate a correct body not counting the uuid', () => { + const inputPayload = getCreateExceptionListSchemaMock(); + delete inputPayload.list_id; + const decoded = createExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as CreateExceptionListSchema).list_id; + expect(message.schema).toEqual(inputPayload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: CreateExceptionListSchema & { + extraKey?: string; + } = getCreateExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = createExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.ts new file mode 100644 index 000000000000..000b4c65a01f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_schema/index.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { + DefaultUuid, + DefaultVersionNumber, + DefaultVersionNumberDecoded, +} from '@kbn/securitysolution-io-ts-types'; + +import { exceptionListType } from '../../common/exception_list'; +import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { Tags, tags } from '../../common/tags'; +import { ListId } from '../../common/list_id'; +import { NamespaceType } from '../../common/default_namespace'; +import { name } from '../../common/name'; +import { description } from '../../common/description'; +import { namespace_type } from '../../common/namespace_type'; +import { meta } from '../../common/meta'; + +export const createExceptionListSchema = t.intersection([ + t.exact( + t.type({ + description, + name, + type: exceptionListType, + }) + ), + t.exact( + t.partial({ + list_id: DefaultUuid, // defaults to a GUID (UUID v4) string if not set during decode + meta, // defaults to undefined if not set during decode + namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode + tags, // defaults to empty array if not set during decode + version: DefaultVersionNumber, // defaults to numerical 1 if not set during decode + }) + ), +]); + +export type CreateExceptionListSchema = t.OutputOf<typeof createExceptionListSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type CreateExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof createExceptionListSchema>>, + 'tags' | 'list_id' | 'namespace_type' | 'os_types' +> & { + tags: Tags; + list_id: ListId; + namespace_type: NamespaceType; + os_types: OsTypeArray; + version: DefaultVersionNumberDecoded; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..03085b3cfa8e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.mock.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 { LIST_ID, LIST_ITEM_ID, META, VALUE } from '../../constants/index.mock'; + +import { CreateListItemSchema } from '.'; + +export const getCreateListItemSchemaMock = (): CreateListItemSchema => ({ + id: LIST_ITEM_ID, + list_id: LIST_ID, + meta: META, + value: VALUE, +}); + +/** + * Useful for end to end testing + */ +export const getCreateMinimalListItemSchemaMock = (): CreateListItemSchema => ({ + id: LIST_ITEM_ID, + list_id: LIST_ID, + value: VALUE, +}); + +/** + * Useful for end to end testing + */ +export const getCreateMinimalListItemSchemaMockWithoutId = (): CreateListItemSchema => ({ + list_id: LIST_ID, + value: VALUE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.test.ts new file mode 100644 index 000000000000..509cbdbf0465 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.test.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getCreateListItemSchemaMock } from './index.mock'; +import { CreateListItemSchema, createListItemSchema } from '.'; + +describe('create_list_item_schema', () => { + test('it should validate a typical list item request', () => { + const payload = getCreateListItemSchemaMock(); + const decoded = createListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for an id', () => { + const payload = getCreateListItemSchemaMock(); + delete payload.id; + const decoded = createListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for meta', () => { + const payload = getCreateListItemSchemaMock(); + delete payload.meta; + const decoded = createListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: CreateListItemSchema & { extraKey?: string } = getCreateListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = createListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.ts new file mode 100644 index 000000000000..d18e7930c3ee --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_item_schema/index.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 * as t from 'io-ts'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { list_id } from '../../common/list_id'; +import { value } from '../../common/value'; +import { id } from '../../common/id'; +import { meta } from '../../common/meta'; +import { refreshWithWaitFor } from '../../common/refresh'; + +export const createListItemSchema = t.intersection([ + t.exact( + t.type({ + list_id, + value, + }) + ), + t.exact(t.partial({ id, meta, refresh: refreshWithWaitFor })), +]); + +export type CreateListItemSchema = t.OutputOf<typeof createListItemSchema>; +export type CreateListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf<typeof createListItemSchema> +>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.mock.ts new file mode 100644 index 000000000000..aa4f45d74f4a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.mock.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DESCRIPTION, LIST_ID, META, NAME, TYPE, VERSION } from '../../constants/index.mock'; + +import { CreateListSchema } from '.'; + +export const getCreateListSchemaMock = (): CreateListSchema => ({ + description: DESCRIPTION, + deserializer: undefined, + id: LIST_ID, + meta: META, + name: NAME, + serializer: undefined, + type: TYPE, + version: VERSION, +}); + +/** + * Useful for end to end tests and other mechanisms which want to fill in the values + */ +export const getCreateMinimalListSchemaMock = (): CreateListSchema => ({ + description: DESCRIPTION, + id: LIST_ID, + name: NAME, + type: TYPE, +}); + +/** + * Useful for end to end tests and other mechanisms which want to fill in the values + */ +export const getCreateMinimalListSchemaMockWithoutId = (): CreateListSchema => ({ + description: DESCRIPTION, + name: NAME, + type: TYPE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.test.ts new file mode 100644 index 000000000000..a6043addd72a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.test.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { CreateListSchema, createListSchema } from '.'; +import { getCreateListSchemaMock } from './index.mock'; + +describe('create_list_schema', () => { + test('it should validate a typical lists request', () => { + const payload = getCreateListSchemaMock(); + const decoded = createListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for an id', () => { + const payload = getCreateListSchemaMock(); + delete payload.id; + const decoded = createListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for meta', () => { + const payload = getCreateListSchemaMock(); + delete payload.meta; + const decoded = createListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for serializer', () => { + const payload = getCreateListSchemaMock(); + delete payload.serializer; + const decoded = createListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for deserializer', () => { + const payload = getCreateListSchemaMock(); + delete payload.deserializer; + const decoded = createListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: CreateListSchema & { extraKey?: string } = getCreateListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = createListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.ts new file mode 100644 index 000000000000..fd771d40a501 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { + DefaultVersionNumber, + DefaultVersionNumberDecoded, +} from '@kbn/securitysolution-io-ts-types'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { name } from '../../common/name'; +import { description } from '../../common/description'; +import { type } from '../../common/type'; +import { deserializer } from '../../common/deserializer'; +import { id } from '../../common/id'; +import { meta } from '../../common/meta'; +import { serializer } from '../../common/serializer'; + +export const createListSchema = t.intersection([ + t.exact( + t.type({ + description, + name, + type, + }) + ), + t.exact( + t.partial({ + deserializer, // defaults to undefined if not set during decode + id, // defaults to undefined if not set during decode + meta, // defaults to undefined if not set during decode + serializer, // defaults to undefined if not set during decode + version: DefaultVersionNumber, // defaults to a numerical 1 if not set during decode + }) + ), +]); + +export type CreateListSchema = t.OutputOf<typeof createListSchema>; +export type CreateListSchemaDecoded = RequiredKeepUndefined< + Omit<t.TypeOf<typeof createListSchema>, 'version'> +> & { version: DefaultVersionNumberDecoded }; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.test.ts new file mode 100644 index 000000000000..bd95fdd0e086 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.test.ts @@ -0,0 +1,125 @@ +/* + * 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 { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { CreateRuleExceptionListItemSchema, createRuleExceptionListItemSchema } from '.'; +import { CreateExceptionListItemSchema } from '../create_exception_list_item_schema'; + +const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemSchema => ({ + comments: [], + description: 'some description', + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['foo', 'bar'], + }, + ], + item_id: undefined, + list_id: 'some-list-id', + name: 'some name', + namespace_type: 'single', + os_types: [], + tags: [], + type: 'simple', +}); + +describe('createRuleExceptionListItemSchema', () => { + test('empty objects do not validate', () => { + const payload = {} as CreateRuleExceptionListItemSchema; + + const decoded = createRuleExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + 'Invalid value "undefined" supplied to "entries"', + 'Invalid value "undefined" supplied to "name"', + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('items without list_id validate', () => { + const payload: CreateRuleExceptionListItemSchema = { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: 'Sample exception item', + type: 'simple', + }; + + const decoded = createRuleExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual( + expect.objectContaining({ + comments: [], + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: 'Sample exception item', + os_types: [], + tags: [], + type: 'simple', + }) + ); + }); + + test('items with list_id do not validate', () => { + const payload = + getCreateExceptionListItemSchemaMock() as unknown as CreateRuleExceptionListItemSchema; + + const decoded = createRuleExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "some-list-id" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('made up parameters do not validate', () => { + const payload: Partial<CreateRuleExceptionListItemSchema> & { madeUp: string } = { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: 'Sample exception item', + type: 'simple', + madeUp: 'invalid value', + }; + + const decoded = createRuleExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "madeUp"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.ts new file mode 100644 index 000000000000..ef0446d9207a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/create_rule_exception_item_schema/index.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { DefaultUuid } from '@kbn/securitysolution-io-ts-types'; + +import { + CreateCommentsArray, + DefaultCreateCommentsArray, + description, + EntriesArray, + exceptionListItemType, + ItemId, + meta, + NamespaceType, + namespaceType, + nonEmptyEntriesArray, + OsTypeArray, + osTypeArrayOrUndefined, + Tags, + tags, + name, + ExpireTimeOrUndefined, + expireTimeOrUndefined, +} from '../../common'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; + +export const createRuleExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + description, + entries: nonEmptyEntriesArray, + name, + type: exceptionListItemType, + }) + ), + t.exact( + t.partial({ + comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode + item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode + list_id: t.undefined, + meta, // defaults to undefined if not set during decode + namespace_type: namespaceType, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode + tags, // defaults to empty array if not set during decode + expire_time: expireTimeOrUndefined, + }) + ), +]); + +export type CreateRuleExceptionListItemSchema = t.OutputOf< + typeof createRuleExceptionListItemSchema +>; + +// This type is used after a decode since some things are defaults after a decode. +export type CreateRuleExceptionListItemSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof createRuleExceptionListItemSchema>>, + 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' | 'expire_time' +> & { + comments: CreateCommentsArray; + tags: Tags; + item_id: ItemId; + entries: EntriesArray; + namespace_type: NamespaceType; + os_types: OsTypeArray; + expire_time: ExpireTimeOrUndefined; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..2547180e114f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.mock.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 { ID } from '../../constants/index.mock'; + +import { DeleteEndpointListItemSchema } from '.'; + +export const getDeleteEndpointListItemSchemaMock = (): DeleteEndpointListItemSchema => ({ + id: ID, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.test.ts new file mode 100644 index 000000000000..09697db0c5f4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.test.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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { DeleteEndpointListItemSchema, deleteEndpointListItemSchema } from '.'; +import { getDeleteEndpointListItemSchemaMock } from './index.mock'; + +describe('delete_endpoint_list_item_schema', () => { + test('it should validate a typical endpoint list item request', () => { + const payload = getDeleteEndpointListItemSchemaMock(); + const decoded = deleteEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept a value for "namespace_type" since it does not require one', () => { + const payload: DeleteEndpointListItemSchema & { + namespace_type: string; + } = { ...getDeleteEndpointListItemSchemaMock(), namespace_type: 'single' }; + // @ts-expect-error + delete payload.namespace_type; + const decoded = deleteEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getDeleteEndpointListItemSchemaMock()); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: DeleteEndpointListItemSchema & { + extraKey?: string; + } = { ...getDeleteEndpointListItemSchemaMock(), extraKey: 'some new value' }; + const decoded = deleteEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.ts new file mode 100644 index 000000000000..9825b7414238 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_endpoint_list_item_schema/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { id } from '../../common/id'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { item_id } from '../../common/item_id'; + +export const deleteEndpointListItemSchema = t.exact( + t.partial({ + id, + item_id, + }) +); + +export type DeleteEndpointListItemSchema = t.OutputOf<typeof deleteEndpointListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type DeleteEndpointListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf<typeof deleteEndpointListItemSchema> +>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..98404e189beb --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.mock.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ID, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { DeleteExceptionListItemSchema } from '.'; + +export const getDeleteExceptionListItemSchemaMock = (): DeleteExceptionListItemSchema => ({ + id: ID, + namespace_type: NAMESPACE_TYPE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.test.ts new file mode 100644 index 000000000000..7131a969a795 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.test.ts @@ -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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { DeleteExceptionListItemSchema, deleteExceptionListItemSchema } from '.'; +import { getDeleteExceptionListItemSchemaMock } from './index.mock'; + +describe('delete_exception_list_item_schema', () => { + test('it should validate a typical exception list item request', () => { + const payload = getDeleteExceptionListItemSchemaMock(); + const decoded = deleteExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "namespace_type" but default to "single"', () => { + const payload = getDeleteExceptionListItemSchemaMock(); + delete payload.namespace_type; + const decoded = deleteExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getDeleteExceptionListItemSchemaMock()); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: DeleteExceptionListItemSchema & { + extraKey?: string; + } = getDeleteExceptionListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = deleteExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.ts new file mode 100644 index 000000000000..02bcd800712e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_item_schema/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { NamespaceType } from '../../common/default_namespace'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { item_id } from '../../common/item_id'; +import { namespace_type } from '../../common/namespace_type'; + +export const deleteExceptionListItemSchema = t.exact( + t.partial({ + id, + item_id, + namespace_type, // defaults to 'single' if not set during decode + }) +); + +export type DeleteExceptionListItemSchema = t.OutputOf<typeof deleteExceptionListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type DeleteExceptionListItemSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof deleteExceptionListItemSchema>>, + 'namespace_type' +> & { + namespace_type: NamespaceType; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.mock.ts new file mode 100644 index 000000000000..7eca83a9d713 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.mock.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ID, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { DeleteExceptionListSchema } from '.'; + +export const getDeleteExceptionListSchemaMock = (): DeleteExceptionListSchema => ({ + id: ID, + namespace_type: NAMESPACE_TYPE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.test.ts new file mode 100644 index 000000000000..a439ac040322 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.test.ts @@ -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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { DeleteExceptionListSchema, deleteExceptionListSchema } from '.'; +import { getDeleteExceptionListSchemaMock } from './index.mock'; + +describe('delete_exception_list_schema', () => { + test('it should validate a typical exception list request', () => { + const payload = getDeleteExceptionListSchemaMock(); + const decoded = deleteExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "namespace_type" but default to "single"', () => { + const payload = getDeleteExceptionListSchemaMock(); + delete payload.namespace_type; + const decoded = deleteExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getDeleteExceptionListSchemaMock()); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: DeleteExceptionListSchema & { + extraKey?: string; + } = getDeleteExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = deleteExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.ts new file mode 100644 index 000000000000..1c04c0fdc17a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_exception_list_schema/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { NamespaceType } from '../../common/default_namespace'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { list_id } from '../../common/list_id'; +import { namespace_type } from '../../common/namespace_type'; + +export const deleteExceptionListSchema = t.exact( + t.partial({ + id, + list_id, + namespace_type, // defaults to 'single' if not set during decode + }) +); + +export type DeleteExceptionListSchema = t.OutputOf<typeof deleteExceptionListSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type DeleteExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof deleteExceptionListSchema>>, + 'namespace_type' +> & { + namespace_type: NamespaceType; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..ef4ff721119a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ID, LIST_ID, VALUE } from '../../constants/index.mock'; + +import { DeleteListItemSchema } from '.'; + +export const getDeleteListItemSchemaMock = (): DeleteListItemSchema => ({ + id: ID, + list_id: LIST_ID, + value: VALUE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.test.ts new file mode 100644 index 000000000000..07db295d3ef2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { DeleteListItemSchema, deleteListItemSchema } from '.'; +import { getDeleteListItemSchemaMock } from './index.mock'; + +describe('delete_list_item_schema', () => { + test('it should validate a typical list item request', () => { + const payload = getDeleteListItemSchemaMock(); + const decoded = deleteListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: DeleteListItemSchema & { + extraKey?: string; + } = { ...getDeleteListItemSchemaMock(), extraKey: 'some new value' }; + const decoded = deleteListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.ts new file mode 100644 index 000000000000..4b9ef6763014 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_item_schema/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { list_id } from '../../common/list_id'; +import { valueOrUndefined } from '../../common/value'; +import { refresh } from '../../common/refresh'; + +export const deleteListItemSchema = t.intersection([ + t.exact( + t.type({ + value: valueOrUndefined, + }) + ), + t.exact(t.partial({ id, list_id, refresh })), +]); + +export type DeleteListItemSchema = t.OutputOf<typeof deleteListItemSchema>; +export type DeleteListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf<typeof deleteListItemSchema> +>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.mock.ts new file mode 100644 index 000000000000..19b7a2aa15b9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LIST_ID } from '../../constants/index.mock'; + +import { DeleteListSchema } from '.'; + +export const getDeleteListSchemaMock = (): DeleteListSchema => ({ + deleteReferences: false, + id: LIST_ID, + ignoreReferences: true, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.test.ts new file mode 100644 index 000000000000..5077c0795df3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { DeleteListSchema, deleteListSchema } from '.'; +import { getDeleteListSchemaMock } from './index.mock'; + +describe('delete_list_schema', () => { + test('it should validate a typical lists request', () => { + const payload = getDeleteListSchemaMock(); + const decoded = deleteListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for an id', () => { + const payload = getDeleteListSchemaMock(); + // @ts-expect-error + delete payload.id; + const decoded = deleteListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: DeleteListSchema & { extraKey?: string } = getDeleteListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = deleteListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.ts new file mode 100644 index 000000000000..da1d252d9e36 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/delete_list_schema/index.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 * as t from 'io-ts'; +import { DefaultStringBooleanFalse } from '@kbn/securitysolution-io-ts-types'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; + +export const deleteListSchema = t.intersection([ + t.exact( + t.type({ + id, + }) + ), + t.exact( + t.partial({ + deleteReferences: DefaultStringBooleanFalse, + ignoreReferences: DefaultStringBooleanFalse, + }) + ), +]); + +export type DeleteListSchema = RequiredKeepUndefined<t.TypeOf<typeof deleteListSchema>>; +export type DeleteListSchemaEncoded = t.OutputOf<typeof deleteListSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.mock.ts new file mode 100644 index 000000000000..d3f9b59d5cf0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { DuplicateExceptionListQuerySchema } from '.'; + +export const getDuplicateExceptionListQuerySchemaMock = (): DuplicateExceptionListQuerySchema => ({ + list_id: LIST_ID, + namespace_type: NAMESPACE_TYPE, + include_expired_exceptions: 'true', +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.test.ts new file mode 100644 index 000000000000..73762793e3d2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.test.ts @@ -0,0 +1,65 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { DuplicateExceptionListQuerySchema, duplicateExceptionListQuerySchema } from '.'; +import { getDuplicateExceptionListQuerySchemaMock } from './index.mock'; + +describe('duplicate_exceptionList_query_schema', () => { + test('it should validate a typical lists request', () => { + const payload = getDuplicateExceptionListQuerySchemaMock(); + const decoded = duplicateExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should default namespace_type to "single" if an undefined given for namespacetype', () => { + const payload = getDuplicateExceptionListQuerySchemaMock(); + delete payload.namespace_type; + const decoded = duplicateExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(message.schema).toEqual({ + include_expired_exceptions: 'true', + list_id: 'some-list-id', + namespace_type: 'single', + }); + }); + + test('it should NOT accept an undefined for an list_id', () => { + const payload = getDuplicateExceptionListQuerySchemaMock(); + // @ts-expect-error + delete payload.list_id; + const decoded = duplicateExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: DuplicateExceptionListQuerySchema & { + extraKey?: string; + } = getDuplicateExceptionListQuerySchemaMock(); + payload.extraKey = 'some new value'; + const decoded = duplicateExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts new file mode 100644 index 000000000000..2a90eb4aeb05 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { NamespaceType } from '../../common'; + +import { includeExpiredExceptionsOrUndefined } from '../../common/include_expired_exceptions'; +import { list_id } from '../../common/list_id'; +import { namespace_type } from '../../common/namespace_type'; + +export const duplicateExceptionListQuerySchema = t.exact( + t.type({ + list_id, + namespace_type, + include_expired_exceptions: includeExpiredExceptionsOrUndefined, + }) +); + +export type DuplicateExceptionListQuerySchema = t.OutputOf< + typeof duplicateExceptionListQuerySchema +>; + +// This type is used after a decode since some things are defaults after a decode. +export type DuplicateExceptionListQuerySchemaDecoded = Omit< + t.TypeOf<typeof duplicateExceptionListQuerySchema>, + 'namespace_type' +> & { + namespace_type: NamespaceType; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.mock.ts new file mode 100644 index 000000000000..2b1d6a439064 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ID, LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { ExportExceptionListQuerySchema } from '.'; + +export const getExportExceptionListQuerySchemaMock = (): ExportExceptionListQuerySchema => ({ + id: ID, + list_id: LIST_ID, + namespace_type: NAMESPACE_TYPE, + include_expired_exceptions: 'true', +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.test.ts new file mode 100644 index 000000000000..dea087dca36d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.test.ts @@ -0,0 +1,78 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { ExportExceptionListQuerySchema, exportExceptionListQuerySchema } from '.'; +import { getExportExceptionListQuerySchemaMock } from './index.mock'; + +describe('export_exception_list_schema', () => { + test('it should validate a typical lists request', () => { + const payload = getExportExceptionListQuerySchemaMock(); + const decoded = exportExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for an id', () => { + const payload = getExportExceptionListQuerySchemaMock(); + // @ts-expect-error + delete payload.id; + const decoded = exportExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should default namespace_type to "single" if an undefined given for namespacetype', () => { + const payload = getExportExceptionListQuerySchemaMock(); + delete payload.namespace_type; + const decoded = exportExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(message.schema).toEqual({ + id: 'uuid_here', + include_expired_exceptions: 'true', + list_id: 'some-list-id', + namespace_type: 'single', + }); + }); + + test('it should NOT accept an undefined for an list_id', () => { + const payload = getExportExceptionListQuerySchemaMock(); + // @ts-expect-error + delete payload.list_id; + const decoded = exportExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ExportExceptionListQuerySchema & { + extraKey?: string; + } = getExportExceptionListQuerySchemaMock(); + payload.extraKey = 'some new value'; + const decoded = exportExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.ts new file mode 100644 index 000000000000..8967e45c5a61 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_exception_list_query_schema/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { id } from '../../common/id'; +import { includeExpiredExceptionsOrUndefined } from '../../common/include_expired_exceptions'; +import { list_id } from '../../common/list_id'; +import { namespace_type } from '../../common/namespace_type'; + +export const exportExceptionListQuerySchema = t.exact( + t.type({ + id, + list_id, + namespace_type, + include_expired_exceptions: includeExpiredExceptionsOrUndefined, + // TODO: Add file_name here with a default value + }) +); + +export type ExportExceptionListQuerySchema = t.OutputOf<typeof exportExceptionListQuerySchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.mock.ts new file mode 100644 index 000000000000..85076f592d16 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.mock.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 { LIST_ID } from '../../constants/index.mock'; + +import { ExportListItemQuerySchema } from '.'; + +export const getExportListItemQuerySchemaMock = (): ExportListItemQuerySchema => ({ + list_id: LIST_ID, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.test.ts new file mode 100644 index 000000000000..f05e530cbb41 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.test.ts @@ -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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { ExportListItemQuerySchema, exportListItemQuerySchema } from '.'; +import { getExportListItemQuerySchemaMock } from './index.mock'; + +describe('export_list_item_schema', () => { + test('it should validate a typical lists request', () => { + const payload = getExportListItemQuerySchemaMock(); + const decoded = exportListItemQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for an id', () => { + const payload = getExportListItemQuerySchemaMock(); + // @ts-expect-error + delete payload.list_id; + const decoded = exportListItemQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ExportListItemQuerySchema & { + extraKey?: string; + } = getExportListItemQuerySchemaMock(); + payload.extraKey = 'some new value'; + const decoded = exportListItemQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.ts new file mode 100644 index 000000000000..a07e6c73434e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/export_list_item_query_schema/index.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 * as t from 'io-ts'; + +import { list_id } from '../../common/list_id'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; + +export const exportListItemQuerySchema = t.exact( + t.type({ + list_id, + // TODO: Add file_name here with a default value + }) +); + +export type ExportListItemQuerySchema = RequiredKeepUndefined< + t.TypeOf<typeof exportListItemQuerySchema> +>; +export type ExportListItemQuerySchemaEncoded = t.OutputOf<typeof exportListItemQuerySchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..5df0322cc803 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.mock.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FILTER } from '../../constants/index.mock'; + +import { FindEndpointListItemSchema, FindEndpointListItemSchemaDecoded } from '.'; + +export const getFindEndpointListItemSchemaMock = (): FindEndpointListItemSchema => ({ + filter: FILTER, + page: '1', + per_page: '25', + sort_field: undefined, + sort_order: undefined, +}); + +export const getFindEndpointListItemSchemaDecodedMock = (): FindEndpointListItemSchemaDecoded => ({ + filter: FILTER, + page: 1, + per_page: 25, + sort_field: undefined, + sort_order: undefined, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.test.ts new file mode 100644 index 000000000000..1ce9618781c4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.test.ts @@ -0,0 +1,107 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { + getFindEndpointListItemSchemaDecodedMock, + getFindEndpointListItemSchemaMock, +} from './index.mock'; +import { FindEndpointListItemSchema, findEndpointListItemSchema } from '.'; + +describe('find_endpoint_list_item_schema', () => { + test('it should validate a typical find item request', () => { + const payload = getFindEndpointListItemSchemaMock(); + const decoded = findEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getFindEndpointListItemSchemaDecodedMock()); + }); + + test('it should validate and empty object since everything is optional and has defaults', () => { + const payload: FindEndpointListItemSchema = {}; + const decoded = findEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate with page missing', () => { + const payload = getFindEndpointListItemSchemaMock(); + delete payload.page; + const decoded = findEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindEndpointListItemSchemaDecodedMock(); + delete expected.page; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with pre_page missing', () => { + const payload = getFindEndpointListItemSchemaMock(); + delete payload.per_page; + const decoded = findEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindEndpointListItemSchemaDecodedMock(); + delete expected.per_page; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with filter missing', () => { + const payload = getFindEndpointListItemSchemaMock(); + delete payload.filter; + const decoded = findEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindEndpointListItemSchemaDecodedMock(); + delete expected.filter; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with sort_field missing', () => { + const payload = getFindEndpointListItemSchemaMock(); + delete payload.sort_field; + const decoded = findEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindEndpointListItemSchemaDecodedMock(); + delete expected.sort_field; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with sort_order missing', () => { + const payload = getFindEndpointListItemSchemaMock(); + delete payload.sort_order; + const decoded = findEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindEndpointListItemSchemaDecodedMock(); + delete expected.sort_order; + expect(message.schema).toEqual(expected); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: FindEndpointListItemSchema & { + extraKey: string; + } = { ...getFindEndpointListItemSchemaMock(), extraKey: 'some new value' }; + const decoded = findEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.ts new file mode 100644 index 000000000000..05f32f452f5d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_endpoint_list_item_schema/index.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 * as t from 'io-ts'; +import { StringToPositiveNumber } from '@kbn/securitysolution-io-ts-types'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { filter } from '../../common/filter'; +import { sort_field } from '../../common/sort_field'; +import { sort_order } from '../../common/sort_order'; + +export const findEndpointListItemSchema = t.exact( + t.partial({ + filter, // defaults to undefined if not set during decode + page: StringToPositiveNumber, // defaults to undefined if not set during decode + per_page: StringToPositiveNumber, // defaults to undefined if not set during decode + sort_field, // defaults to undefined if not set during decode + sort_order, // defaults to undefined if not set during decode + }) +); + +export type FindEndpointListItemSchema = t.OutputOf<typeof findEndpointListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type FindEndpointListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf<typeof findEndpointListItemSchema> +>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..16b2ee7a8a37 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.mock.ts @@ -0,0 +1,56 @@ +/* + * 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 { FILTER, LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { FindExceptionListItemSchema, FindExceptionListItemSchemaDecoded } from '.'; + +export const getFindExceptionListItemSchemaMock = (): FindExceptionListItemSchema => ({ + filter: FILTER, + list_id: LIST_ID, + namespace_type: NAMESPACE_TYPE, + page: '1', + per_page: '25', + search: undefined, + sort_field: undefined, + sort_order: undefined, +}); + +export const getFindExceptionListItemSchemaMultipleMock = (): FindExceptionListItemSchema => ({ + filter: 'name:Sofia Kovalevskaya,name:Hypatia,name:Sophie Germain', + list_id: 'list-1,list-2,list-3', + namespace_type: 'single,single,agnostic', + page: '1', + per_page: '25', + search: undefined, + sort_field: undefined, + sort_order: undefined, +}); + +export const getFindExceptionListItemSchemaDecodedMock = + (): FindExceptionListItemSchemaDecoded => ({ + filter: [FILTER], + list_id: [LIST_ID], + namespace_type: [NAMESPACE_TYPE], + page: 1, + per_page: 25, + search: undefined, + sort_field: undefined, + sort_order: undefined, + }); + +export const getFindExceptionListItemSchemaDecodedMultipleMock = + (): FindExceptionListItemSchemaDecoded => ({ + filter: ['name:Sofia Kovalevskaya', 'name:Hypatia', 'name:Sophie Germain'], + list_id: ['list-1', 'list-2', 'list-3'], + namespace_type: ['single', 'single', 'agnostic'], + page: 1, + per_page: 25, + search: undefined, + sort_field: undefined, + sort_order: undefined, + }); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.test.ts new file mode 100644 index 000000000000..1d6696933321 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.test.ts @@ -0,0 +1,148 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { LIST_ID } from '../../constants/index.mock'; + +import { + getFindExceptionListItemSchemaDecodedMock, + getFindExceptionListItemSchemaDecodedMultipleMock, + getFindExceptionListItemSchemaMock, + getFindExceptionListItemSchemaMultipleMock, +} from './index.mock'; +import { + FindExceptionListItemSchema, + FindExceptionListItemSchemaDecoded, + findExceptionListItemSchema, +} from '.'; + +describe('find_list_item_schema', () => { + test('it should validate a typical find item request', () => { + const payload = getFindExceptionListItemSchemaMock(); + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getFindExceptionListItemSchemaDecodedMock()); + }); + + test('it should validate a typical find item request with multiple input strings turned into array elements', () => { + const payload = getFindExceptionListItemSchemaMultipleMock(); + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getFindExceptionListItemSchemaDecodedMultipleMock()); + }); + + test('it should validate just a list_id where it decodes into an array for list_id and adds a namespace_type of "single" as an array', () => { + const payload: FindExceptionListItemSchema = { list_id: LIST_ID }; + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected: FindExceptionListItemSchemaDecoded = { + filter: [], + list_id: [LIST_ID], + namespace_type: ['single'], + page: undefined, + per_page: undefined, + search: undefined, + sort_field: undefined, + sort_order: undefined, + }; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with page missing', () => { + const payload = getFindExceptionListItemSchemaMock(); + delete payload.page; + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListItemSchemaDecodedMock(); + delete expected.page; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with per_page missing', () => { + const payload = getFindExceptionListItemSchemaMock(); + delete payload.per_page; + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListItemSchemaDecodedMock(); + delete expected.per_page; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with filter missing and add filter as an empty array', () => { + const payload = getFindExceptionListItemSchemaMock(); + delete payload.filter; + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected: FindExceptionListItemSchemaDecoded = { + ...getFindExceptionListItemSchemaDecodedMock(), + filter: [], + }; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with sort_field missing', () => { + const payload = getFindExceptionListItemSchemaMock(); + delete payload.sort_field; + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListItemSchemaDecodedMock(); + delete expected.sort_field; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with sort_order missing', () => { + const payload = getFindExceptionListItemSchemaMock(); + delete payload.sort_order; + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListItemSchemaDecodedMock(); + delete expected.sort_order; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with search missing', () => { + const payload = getFindExceptionListItemSchemaMock(); + delete payload.search; + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListItemSchemaDecodedMock(); + delete expected.search; + expect(message.schema).toEqual(expected); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: FindExceptionListItemSchema & { + extraKey: string; + } = { ...getFindExceptionListItemSchemaMock(), extraKey: 'some new value' }; + const decoded = findExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts new file mode 100644 index 000000000000..7177a7144c7f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_item_schema/index.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { + EmptyStringArray, + EmptyStringArrayDecoded, + NonEmptyStringArray, + StringToPositiveNumber, +} from '@kbn/securitysolution-io-ts-types'; + +import { + DefaultNamespaceArray, + DefaultNamespaceArrayTypeDecoded, +} from '../../common/default_namespace_array'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { sort_field } from '../../common/sort_field'; +import { sort_order } from '../../common/sort_order'; +import { search } from '../../common/search'; + +export const findExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + list_id: NonEmptyStringArray, + }) + ), + t.exact( + t.partial({ + filter: EmptyStringArray, // defaults to an empty array [] if not set during decode + namespace_type: DefaultNamespaceArray, // defaults to ['single'] if not set during decode + page: StringToPositiveNumber, // defaults to undefined if not set during decode + per_page: StringToPositiveNumber, // defaults to undefined if not set during decode + search, + sort_field, // defaults to undefined if not set during decode + sort_order, // defaults to undefined if not set during decode + }) + ), +]); + +export type FindExceptionListItemSchema = t.OutputOf<typeof findExceptionListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type FindExceptionListItemSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof findExceptionListItemSchema>>, + 'namespace_type' | 'filter' +> & { + filter: EmptyStringArrayDecoded; + namespace_type: DefaultNamespaceArrayTypeDecoded; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.mock.ts new file mode 100644 index 000000000000..f963c6650cf2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.mock.ts @@ -0,0 +1,28 @@ +/* + * 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 { FILTER, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { FindExceptionListSchema, FindExceptionListSchemaDecoded } from '.'; + +export const getFindExceptionListSchemaMock = (): FindExceptionListSchema => ({ + filter: FILTER, + namespace_type: NAMESPACE_TYPE, + page: '1', + per_page: '25', + sort_field: undefined, + sort_order: undefined, +}); + +export const getFindExceptionListSchemaDecodedMock = (): FindExceptionListSchemaDecoded => ({ + filter: FILTER, + namespace_type: [NAMESPACE_TYPE], + page: 1, + per_page: 25, + sort_field: undefined, + sort_order: undefined, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.test.ts new file mode 100644 index 000000000000..8669a3657f02 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.test.ts @@ -0,0 +1,119 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { + getFindExceptionListSchemaDecodedMock, + getFindExceptionListSchemaMock, +} from './index.mock'; +import { + FindExceptionListSchema, + FindExceptionListSchemaDecoded, + findExceptionListSchema, +} from '.'; + +describe('find_exception_list_schema', () => { + test('it should validate a typical find item request', () => { + const payload = getFindExceptionListSchemaMock(); + const decoded = findExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getFindExceptionListSchemaDecodedMock()); + }); + + test('it should validate and empty object since everything is optional and will respond only with namespace_type filled out to be "single"', () => { + const payload: FindExceptionListSchema = {}; + const decoded = findExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected: FindExceptionListSchemaDecoded = { + filter: undefined, + namespace_type: ['single'], + page: undefined, + per_page: undefined, + sort_field: undefined, + sort_order: undefined, + }; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with page missing', () => { + const payload = getFindExceptionListSchemaMock(); + delete payload.page; + const decoded = findExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListSchemaDecodedMock(); + delete expected.page; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with pre_page missing', () => { + const payload = getFindExceptionListSchemaMock(); + delete payload.per_page; + const decoded = findExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListSchemaDecodedMock(); + delete expected.per_page; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with filter missing', () => { + const payload = getFindExceptionListSchemaMock(); + delete payload.filter; + const decoded = findExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListSchemaDecodedMock(); + delete expected.filter; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with sort_field missing', () => { + const payload = getFindExceptionListSchemaMock(); + delete payload.sort_field; + const decoded = findExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListSchemaDecodedMock(); + delete expected.sort_field; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with sort_order missing', () => { + const payload = getFindExceptionListSchemaMock(); + delete payload.sort_order; + const decoded = findExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindExceptionListSchemaDecodedMock(); + delete expected.sort_order; + expect(message.schema).toEqual(expected); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: FindExceptionListSchema & { + extraKey: string; + } = { ...getFindExceptionListSchemaMock(), extraKey: 'some new value' }; + const decoded = findExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.ts new file mode 100644 index 000000000000..dc9acdefd7a9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_exception_list_schema/index.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { StringToPositiveNumber } from '@kbn/securitysolution-io-ts-types'; + +import { DefaultNamespaceArray, NamespaceTypeArray } from '../../common/default_namespace_array'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { filter } from '../../common/filter'; +import { sort_field } from '../../common/sort_field'; +import { sort_order } from '../../common/sort_order'; + +export const findExceptionListSchema = t.exact( + t.partial({ + filter, // defaults to undefined if not set during decode + namespace_type: DefaultNamespaceArray, // defaults to 'single' if not set during decode + page: StringToPositiveNumber, // defaults to undefined if not set during decode + per_page: StringToPositiveNumber, // defaults to undefined if not set during decode + sort_field, // defaults to undefined if not set during decode + sort_order, // defaults to undefined if not set during decode + }) +); + +export type FindExceptionListSchema = t.OutputOf<typeof findExceptionListSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type FindExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof findExceptionListSchema>>, + 'namespace_type' +> & { + namespace_type: NamespaceTypeArray; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..44c952a9a382 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.mock.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 { CURSOR, FILTER, LIST_ID } from '../../constants/index.mock'; + +import { FindListItemSchema, FindListItemSchemaDecoded } from '.'; + +export const getFindListItemSchemaMock = (): FindListItemSchema => ({ + cursor: CURSOR, + filter: FILTER, + list_id: LIST_ID, + page: '1', + per_page: '25', + sort_field: undefined, + sort_order: undefined, +}); + +export const getFindListItemSchemaDecodedMock = (): FindListItemSchemaDecoded => ({ + cursor: CURSOR, + filter: FILTER, + list_id: LIST_ID, + page: 1, + per_page: 25, + sort_field: undefined, + sort_order: undefined, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.test.ts new file mode 100644 index 000000000000..589a40f641c6 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.test.ts @@ -0,0 +1,103 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { LIST_ID } from '../../constants/index.mock'; + +import { FindListItemSchema, FindListItemSchemaDecoded, findListItemSchema } from '.'; +import { getFindListItemSchemaDecodedMock, getFindListItemSchemaMock } from './index.mock'; + +describe('find_list_item_schema', () => { + test('it should validate a typical find item request', () => { + const payload = getFindListItemSchemaMock(); + const decoded = findListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getFindListItemSchemaDecodedMock()); + }); + + test('it should validate just a list_id where it decodes into an array for list_id and adds a namespace_type of "single"', () => { + const payload: FindListItemSchema = { list_id: LIST_ID }; + const decoded = findListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected: FindListItemSchemaDecoded = { + cursor: undefined, + filter: undefined, + list_id: LIST_ID, + page: undefined, + per_page: undefined, + sort_field: undefined, + sort_order: undefined, + }; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with page missing', () => { + const payload = getFindListItemSchemaMock(); + delete payload.page; + const decoded = findListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindListItemSchemaDecodedMock(); + delete expected.page; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with pre_page missing', () => { + const payload = getFindListItemSchemaMock(); + delete payload.per_page; + const decoded = findListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindListItemSchemaDecodedMock(); + delete expected.per_page; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with sort_field missing', () => { + const payload = getFindListItemSchemaMock(); + delete payload.sort_field; + const decoded = findListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindListItemSchemaDecodedMock(); + delete expected.sort_field; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with sort_order missing', () => { + const payload = getFindListItemSchemaMock(); + delete payload.sort_order; + const decoded = findListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindListItemSchemaDecodedMock(); + delete expected.sort_order; + expect(message.schema).toEqual(expected); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: FindListItemSchema & { + extraKey: string; + } = { ...getFindListItemSchemaMock(), extraKey: 'some new value' }; + const decoded = findListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.ts new file mode 100644 index 000000000000..4279d5ef4a07 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_item_schema/index.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { StringToPositiveNumber } from '@kbn/securitysolution-io-ts-types'; +import { filter } from '../../common/filter'; +import { cursor } from '../../common/cursor'; +import { sort_field } from '../../common/sort_field'; +import { sort_order } from '../../common/sort_order'; +import { list_id } from '../../common/list_id'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; + +export const findListItemSchema = t.intersection([ + t.exact(t.type({ list_id })), + t.exact( + t.partial({ + cursor, // defaults to undefined if not set during decode + filter, // defaults to undefined if not set during decode + page: StringToPositiveNumber, // defaults to undefined if not set during decode + per_page: StringToPositiveNumber, // defaults to undefined if not set during decode + sort_field, // defaults to undefined if not set during decode + sort_order, // defaults to undefined if not set during decode + }) + ), +]); + +export type FindListItemSchema = t.OutputOf<typeof findListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type FindListItemSchemaDecoded = RequiredKeepUndefined<t.TypeOf<typeof findListItemSchema>>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.mock.ts new file mode 100644 index 000000000000..b80369fb6b43 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.mock.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FILTER } from '../../constants/index.mock'; + +import { FindListSchema, FindListSchemaEncoded } from '.'; + +export const getFindListSchemaMock = (): FindListSchemaEncoded => ({ + filter: FILTER, + page: '1', + per_page: '25', + sort_field: undefined, + sort_order: undefined, +}); + +export const getFindListSchemaDecodedMock = (): FindListSchema => ({ + cursor: undefined, + filter: FILTER, + page: 1, + per_page: 25, + sort_field: undefined, + sort_order: undefined, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.test.ts new file mode 100644 index 000000000000..06b37bacf845 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getFindListSchemaDecodedMock, getFindListSchemaMock } from './index.mock'; +import { FindListSchemaEncoded, findListSchema } from '.'; + +describe('find_list_schema', () => { + test('it should validate a typical find item request', () => { + const payload = getFindListSchemaMock(); + const decoded = findListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getFindListSchemaDecodedMock()); + }); + + test('it should validate and empty object since everything is optional and will respond with an empty object', () => { + const payload: FindListSchemaEncoded = {}; + const decoded = findListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate with page missing', () => { + const payload = getFindListSchemaMock(); + delete payload.page; + const decoded = findListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindListSchemaDecodedMock(); + delete expected.page; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with pre_page missing', () => { + const payload = getFindListSchemaMock(); + delete payload.per_page; + const decoded = findListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindListSchemaDecodedMock(); + delete expected.per_page; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with filter missing', () => { + const payload = getFindListSchemaMock(); + delete payload.filter; + const decoded = findListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindListSchemaDecodedMock(); + delete expected.filter; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with sort_field missing', () => { + const payload = getFindListSchemaMock(); + delete payload.sort_field; + const decoded = findListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindListSchemaDecodedMock(); + delete expected.sort_field; + expect(message.schema).toEqual(expected); + }); + + test('it should validate with sort_order missing', () => { + const payload = getFindListSchemaMock(); + delete payload.sort_order; + const decoded = findListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + const expected = getFindListSchemaDecodedMock(); + delete expected.sort_order; + expect(message.schema).toEqual(expected); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: FindListSchemaEncoded & { + extraKey: string; + } = { ...getFindListSchemaMock(), extraKey: 'some new value' }; + const decoded = findListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.ts new file mode 100644 index 000000000000..8f1520d0caf1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/find_list_schema/index.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 * as t from 'io-ts'; + +import { StringToPositiveNumber } from '@kbn/securitysolution-io-ts-types'; +import { cursor } from '../../common/cursor'; +import { filter } from '../../common/filter'; +import { sort_field } from '../../common/sort_field'; +import { sort_order } from '../../common/sort_order'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; + +export const findListSchema = t.exact( + t.partial({ + cursor, // defaults to undefined if not set during decode + filter, // defaults to undefined if not set during decode + page: StringToPositiveNumber, // defaults to undefined if not set during decode + per_page: StringToPositiveNumber, // defaults to undefined if not set during decode + sort_field, // defaults to undefined if not set during decode + sort_order, // defaults to undefined if not set during decode + }) +); + +export type FindListSchema = RequiredKeepUndefined<t.TypeOf<typeof findListSchema>>; +export type FindListSchemaEncoded = t.OutputOf<typeof findListSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/get_exception_filter_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/get_exception_filter_schema/index.ts new file mode 100644 index 000000000000..656d6cea6164 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/get_exception_filter_schema/index.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { namespaceType } from '../../common/default_namespace'; +import { exceptionListItemSchema } from '../../response'; +import { createExceptionListItemSchema } from '../create_exception_list_item_schema'; + +const exceptionListId = t.type({ + exception_list_id: t.string, + namespace_type: namespaceType, +}); + +export const exceptionListIds = t.type({ + exception_list_ids: t.array(exceptionListId), + type: t.literal('exception_list_ids'), +}); + +export const exceptions = t.type({ + exceptions: t.array(t.union([exceptionListItemSchema, createExceptionListItemSchema])), + type: t.literal('exception_items'), +}); + +const optionalExceptionParams = t.exact( + t.partial({ alias: t.string, chunk_size: t.number, exclude_exceptions: t.boolean }) +); + +export const getExceptionFilterSchema = t.intersection([ + t.union([exceptions, exceptionListIds]), + optionalExceptionParams, +]); + +export type GetExceptionFilterSchema = t.TypeOf<typeof getExceptionFilterSchema>; +export type ExceptionListId = t.TypeOf<typeof exceptionListId>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.mock.ts new file mode 100644 index 000000000000..a50c455a9d60 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.mock.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 { ENTRIES } from '../../constants/index.mock'; +import { ImportExceptionListItemSchema, ImportExceptionListItemSchemaDecoded } from '.'; + +export const getImportExceptionsListItemSchemaMock = ( + itemId = 'item_id_1', + listId = 'detection_list_id' +): ImportExceptionListItemSchema => ({ + description: 'some description', + entries: ENTRIES, + item_id: itemId, + list_id: listId, + name: 'Query with a rule id', + type: 'simple', +}); + +export const getImportExceptionsListItemSchemaDecodedMock = ( + itemId = 'item_id_1', + listId = 'detection_list_id' +): ImportExceptionListItemSchemaDecoded => ({ + ...getImportExceptionsListItemSchemaMock(itemId, listId), + comments: [], + meta: undefined, + namespace_type: 'single', + os_types: [], + tags: [], + expire_time: undefined, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.test.ts new file mode 100644 index 000000000000..61e135bfd59a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.test.ts @@ -0,0 +1,172 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { importExceptionListItemSchema, ImportExceptionListItemSchema } from '.'; +import { + getImportExceptionsListItemSchemaDecodedMock, + getImportExceptionsListItemSchemaMock, +} from './index.mock'; +import { getCommentsArrayMock } from '../../common/comment/index.mock'; + +describe('import_list_item_schema', () => { + test('it should validate a typical item request', () => { + const payload = getImportExceptionsListItemSchemaMock(); + const decoded = importExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getImportExceptionsListItemSchemaDecodedMock()); + }); + + test('it should validate a typical item request with comments', () => { + const payload = { + ...getImportExceptionsListItemSchemaMock(), + comments: getCommentsArrayMock(), + }; + const decoded = importExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual({ + ...getImportExceptionsListItemSchemaDecodedMock(), + comments: [ + { + comment: 'some old comment', + created_at: '2020-04-20T15:25:31.830Z', + created_by: 'some user', + id: 'uuid_here', + }, + { + comment: 'some old comment', + created_at: '2020-04-20T15:25:31.830Z', + created_by: 'some user', + id: 'uuid_here', + }, + ], + }); + }); + + test('it should NOT accept an undefined for "item_id"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = + getImportExceptionsListItemSchemaMock(); + delete payload.item_id; + const decoded = importExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "item_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "list_id"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = + getImportExceptionsListItemSchemaMock(); + delete payload.list_id; + const decoded = importExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "description"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = + getImportExceptionsListItemSchemaMock(); + delete payload.description; + const decoded = importExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "name"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = + getImportExceptionsListItemSchemaMock(); + delete payload.name; + const decoded = importExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "name"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "type"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = + getImportExceptionsListItemSchemaMock(); + delete payload.type; + const decoded = importExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "entries"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsListItemSchemaMock>> = + getImportExceptionsListItemSchemaMock(); + delete payload.entries; + const decoded = importExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should accept any partial fields', () => { + const payload: ImportExceptionListItemSchema = { + ...getImportExceptionsListItemSchemaMock(), + id: '123', + namespace_type: 'single', + comments: [], + os_types: [], + tags: ['123'], + created_at: '2018-08-24T17:49:30.145142000', + created_by: 'elastic', + updated_at: '2018-08-24T17:49:30.145142000', + updated_by: 'elastic', + tie_breaker_id: '123', + _version: '3', + meta: undefined, + }; + + const decoded = importExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ImportExceptionListItemSchema & { + extraKey?: string; + } = getImportExceptionsListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = importExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.ts new file mode 100644 index 000000000000..a2d60e292aff --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; +import { Tags, tags } from '../../common/tags'; +import { NamespaceType } from '../../common/default_namespace'; +import { name } from '../../common/name'; +import { description } from '../../common/description'; +import { namespace_type } from '../../common/namespace_type'; +import { meta } from '../../common/meta'; +import { list_id } from '../../common/list_id'; +import { item_id, ItemId } from '../../common/item_id'; +import { id } from '../../common/id'; +import { created_at } from '../../common/created_at'; +import { created_by } from '../../common/created_by'; +import { updated_at } from '../../common/updated_at'; +import { updated_by } from '../../common/updated_by'; +import { _version } from '../../common/underscore_version'; +import { tie_breaker_id } from '../../common/tie_breaker_id'; +import { nonEmptyEntriesArray } from '../../common/non_empty_entries_array'; +import { exceptionListItemType } from '../../common/exception_list_item_type'; +import { EntriesArray } from '../../common/entries'; +import { DefaultImportCommentsArray } from '../../common/default_import_comments_array'; +import { ExpireTimeOrUndefined, expireTimeOrUndefined, ImportCommentsArray } from '../../common'; + +/** + * Differences from this and the createExceptionsListItemSchema are + * - item_id is required + * - id is optional (but ignored in the import code - item_id is exclusively used for imports) + * - immutable is optional but if it is any value other than false it will be rejected + * - created_at is optional (but ignored in the import code) + * - updated_at is optional (but ignored in the import code) + * - created_by is optional (but ignored in the import code) + * - updated_by is optional (but ignored in the import code) + */ +export const importExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + description, + entries: nonEmptyEntriesArray, + item_id, + list_id, + name, + type: exceptionListItemType, + }) + ), + t.exact( + t.partial({ + id, // defaults to undefined if not set during decode + comments: DefaultImportCommentsArray, // defaults to empty array if not set during decode + created_at, // defaults undefined if not set during decode + updated_at, // defaults undefined if not set during decode + created_by, // defaults undefined if not set during decode + updated_by, // defaults undefined if not set during decode + _version, // defaults to undefined if not set during decode + tie_breaker_id, + meta, // defaults to undefined if not set during decode + namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode + tags, // defaults to empty array if not set during decode + expire_time: expireTimeOrUndefined, + }) + ), +]); + +export type ImportExceptionListItemSchema = t.OutputOf<typeof importExceptionListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type ImportExceptionListItemSchemaDecoded = Omit< + ImportExceptionListItemSchema, + 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' | 'expire_time' +> & { + comments: ImportCommentsArray; + tags: Tags; + item_id: ItemId; + entries: EntriesArray; + namespace_type: NamespaceType; + os_types: OsTypeArray; + expire_time: ExpireTimeOrUndefined; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.mock.ts new file mode 100644 index 000000000000..438b0cd54fb0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.mock.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 { ImportExceptionListSchemaDecoded, ImportExceptionsListSchema } from '.'; + +export const getImportExceptionsListSchemaMock = ( + listId = 'detection_list_id' +): ImportExceptionsListSchema => ({ + description: 'some description', + list_id: listId, + name: 'Query with a rule id', + type: 'detection', +}); + +export const getImportExceptionsListSchemaDecodedMock = ( + listId = 'detection_list_id' +): ImportExceptionListSchemaDecoded => ({ + ...getImportExceptionsListSchemaMock(listId), + immutable: false, + meta: undefined, + namespace_type: 'single', + os_types: [], + tags: [], + version: 1, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.test.ts new file mode 100644 index 000000000000..e1d0a55a1770 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.test.ts @@ -0,0 +1,131 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { importExceptionsListSchema, ImportExceptionsListSchema } from '.'; +import { + getImportExceptionsListSchemaMock, + getImportExceptionsListSchemaDecodedMock, +} from './index.mock'; + +describe('import_list_item_schema', () => { + test('it should validate a typical lists request', () => { + const payload = getImportExceptionsListSchemaMock(); + const decoded = importExceptionsListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getImportExceptionsListSchemaDecodedMock()); + }); + + test('it should NOT accept an undefined for "list_id"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsListSchemaMock>> = + getImportExceptionsListSchemaMock(); + delete payload.list_id; + const decoded = importExceptionsListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "description"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsListSchemaMock>> = + getImportExceptionsListSchemaMock(); + delete payload.description; + const decoded = importExceptionsListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "name"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsListSchemaMock>> = + getImportExceptionsListSchemaMock(); + delete payload.name; + const decoded = importExceptionsListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "name"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "type"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsListSchemaMock>> = + getImportExceptionsListSchemaMock(); + delete payload.type; + const decoded = importExceptionsListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept value of "true" for "immutable"', () => { + const payload: ImportExceptionsListSchema = { + ...getImportExceptionsListSchemaMock(), + immutable: true, + }; + + const decoded = importExceptionsListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "true" supplied to "immutable"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should accept any partial fields', () => { + const payload: ImportExceptionsListSchema = { + ...getImportExceptionsListSchemaMock(), + namespace_type: 'single', + immutable: false, + os_types: [], + tags: ['123'], + created_at: '2018-08-24T17:49:30.145142000', + created_by: 'elastic', + updated_at: '2018-08-24T17:49:30.145142000', + updated_by: 'elastic', + version: 3, + tie_breaker_id: '123', + _version: '3', + meta: undefined, + }; + + const decoded = importExceptionsListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ImportExceptionsListSchema & { + extraKey?: string; + } = getImportExceptionsListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = importExceptionsListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.ts new file mode 100644 index 000000000000..98f5988daf1c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_list_schema/index.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { + DefaultVersionNumber, + DefaultVersionNumberDecoded, + OnlyFalseAllowed, +} from '@kbn/securitysolution-io-ts-types'; + +import { exceptionListType } from '../../common/exception_list'; +import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; +import { Tags, tags } from '../../common/tags'; +import { ListId, list_id } from '../../common/list_id'; +import { NamespaceType } from '../../common/default_namespace'; +import { name } from '../../common/name'; +import { description } from '../../common/description'; +import { namespace_type } from '../../common/namespace_type'; +import { meta } from '../../common/meta'; +import { id } from '../../common/id'; +import { created_at } from '../../common/created_at'; +import { created_by } from '../../common/created_by'; +import { updated_at } from '../../common/updated_at'; +import { updated_by } from '../../common/updated_by'; +import { _version } from '../../common/underscore_version'; +import { tie_breaker_id } from '../../common/tie_breaker_id'; + +/** + * Differences from this and the createExceptionsSchema are + * - list_id is required + * - id is optional (but ignored in the import code - list_id is exclusively used for imports) + * - immutable is optional but if it is any value other than false it will be rejected + * - created_at is optional (but ignored in the import code) + * - updated_at is optional (but ignored in the import code) + * - created_by is optional (but ignored in the import code) + * - updated_by is optional (but ignored in the import code) + */ +export const importExceptionsListSchema = t.intersection([ + t.exact( + t.type({ + description, + name, + type: exceptionListType, + list_id, + }) + ), + t.exact( + t.partial({ + id, // defaults to undefined if not set during decode + immutable: OnlyFalseAllowed, + meta, // defaults to undefined if not set during decode + namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode + tags, // defaults to empty array if not set during decode + created_at, // defaults "undefined" if not set during decode + updated_at, // defaults "undefined" if not set during decode + created_by, // defaults "undefined" if not set during decode + updated_by, // defaults "undefined" if not set during decode + _version, // defaults to undefined if not set during decode + tie_breaker_id, + version: DefaultVersionNumber, // defaults to numerical 1 if not set during decode + }) + ), +]); + +export type ImportExceptionsListSchema = t.TypeOf<typeof importExceptionsListSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type ImportExceptionListSchemaDecoded = Omit< + ImportExceptionsListSchema, + 'tags' | 'list_id' | 'namespace_type' | 'os_types' | 'immutable' +> & { + immutable: false; + tags: Tags; + list_id: ListId; + namespace_type: NamespaceType; + os_types: OsTypeArray; + version: DefaultVersionNumberDecoded; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.mock.ts new file mode 100644 index 000000000000..fc5fc0ca703b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LIST_ID, TYPE } from '../../constants/index.mock'; + +import { ImportListItemQuerySchema } from '.'; + +export const getImportListItemQuerySchemaMock = (): ImportListItemQuerySchema => ({ + deserializer: undefined, + list_id: LIST_ID, + serializer: undefined, + type: TYPE, + refresh: 'false', +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.test.ts new file mode 100644 index 000000000000..26d1ba486468 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.test.ts @@ -0,0 +1,88 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { ImportListItemQuerySchema, importListItemQuerySchema } from '.'; +import { getImportListItemQuerySchemaMock } from './index.mock'; + +describe('import_list_item_schema', () => { + test('it should validate a typical lists request', () => { + const payload = getImportListItemQuerySchemaMock(); + const decoded = importListItemQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "list_id"', () => { + const payload = getImportListItemQuerySchemaMock(); + delete payload.list_id; + const decoded = importListItemQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "type"', () => { + const payload = getImportListItemQuerySchemaMock(); + delete payload.type; + const decoded = importListItemQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "type" and "list_id', () => { + const payload = getImportListItemQuerySchemaMock(); + delete payload.type; + delete payload.list_id; + const decoded = importListItemQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "serializer"', () => { + const payload = getImportListItemQuerySchemaMock(); + delete payload.serializer; + const decoded = importListItemQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "deserializer"', () => { + const payload = getImportListItemQuerySchemaMock(); + delete payload.deserializer; + const decoded = importListItemQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ImportListItemQuerySchema & { + extraKey?: string; + } = getImportListItemQuerySchemaMock(); + payload.extraKey = 'some new value'; + const decoded = importListItemQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.ts new file mode 100644 index 000000000000..19cd873825d2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { deserializer } from '../../common/deserializer'; +import { list_id } from '../../common/list_id'; +import { type } from '../../common/type'; +import { serializer } from '../../common/serializer'; +import { refreshWithWaitFor } from '../../common/refresh'; + +export const importListItemQuerySchema = t.exact( + t.partial({ deserializer, list_id, serializer, type, refresh: refreshWithWaitFor }) +); + +export type ImportListItemQuerySchema = RequiredKeepUndefined< + t.TypeOf<typeof importListItemQuerySchema> +>; +export type ImportListItemQuerySchemaEncoded = t.OutputOf<typeof importListItemQuerySchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..32fee251c078 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.mock.ts @@ -0,0 +1,21 @@ +/* + * 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 { ImportListItemSchema } from '.'; + +export const getImportListItemSchemaMock = (): ImportListItemSchema => ({ + file: {}, +}); + +/** + * This is useful for end to end tests, it will return a buffer given a string array + * of things to import. + * @param input Array of strings of things to import + */ +export const getImportListItemAsBuffer = (input: string[]): Buffer => { + return Buffer.from(input.join('\r\n')); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.test.ts new file mode 100644 index 000000000000..0e572255b574 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.test.ts @@ -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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { ImportListItemSchema, importListItemSchema } from '.'; +import { getImportListItemSchemaMock } from './index.mock'; + +describe('import_list_item_schema', () => { + test('it should validate a typical lists request', () => { + const payload = getImportListItemSchemaMock(); + const decoded = importListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for a file', () => { + const payload = getImportListItemSchemaMock(); + // @ts-expect-error + delete payload.file; + const decoded = importListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "file"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ImportListItemSchema & { + extraKey?: string; + } = getImportListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = importListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.ts new file mode 100644 index 000000000000..4072cf029bae --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_schema/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { file } from '../../common/file'; + +export const importListItemSchema = t.exact( + t.type({ + file, + }) +); + +export type ImportListItemSchema = RequiredKeepUndefined<t.TypeOf<typeof importListItemSchema>>; +export type ImportListItemSchemaEncoded = t.OutputOf<typeof importListItemSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/index.ts new file mode 100644 index 000000000000..a6a3099ddde8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/index.ts @@ -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. + */ + +export * from './create_endpoint_list_item_schema'; +export * from './create_exception_list_item_schema'; +export * from './create_exception_list_schema'; +export * from './create_rule_exception_item_schema'; +export * from './create_list_item_schema'; +export * from './create_list_schema'; +export * from './delete_endpoint_list_item_schema'; +export * from './delete_exception_list_schema'; +export * from './delete_exception_list_item_schema'; +export * from './delete_list_item_schema'; +export * from './delete_list_schema'; +export * from './duplicate_exception_list_query_schema'; +export * from './export_exception_list_query_schema'; +export * from './export_list_item_query_schema'; +export * from './find_endpoint_list_item_schema'; +export * from './find_exception_list_schema'; +export * from './find_exception_list_item_schema'; +export * from './find_list_item_schema'; +export * from './find_list_schema'; +export * from './get_exception_filter_schema'; +export * from './import_list_item_query_schema'; +export * from './import_exception_list_schema'; +export * from './import_exception_item_schema'; +export * from './import_list_item_schema'; +export * from './patch_list_item_schema'; +export * from './patch_list_schema'; +export * from './read_endpoint_list_item_schema'; +export * from './read_exception_list_item_schema'; +export * from './read_exception_list_schema'; +export * from './read_list_item_schema'; +export * from './read_list_schema'; +export * from './summary_exception_list_schema'; +export * from './update_endpoint_list_item_schema'; +export * from './update_exception_list_item_schema'; +export * from './update_exception_list_schema'; +export * from './update_list_item_schema'; +export * from './update_list_schema'; + +// Internal routes +export * from './internal/create_exception_list_schema'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.test.ts new file mode 100644 index 000000000000..8ab58a7b0bdd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.test.ts @@ -0,0 +1,68 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; + +import { ExceptionListTypeEnum } from '../../../common/exception_list'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { internalCreateExceptionListSchema } from '.'; +import { getCreateExceptionListSchemaMock } from '../../create_exception_list_schema/index.mock'; + +describe('create_exception_list_schema', () => { + test('it should accept artifact list_id', () => { + const payload = { + ...getCreateExceptionListSchemaMock(), + list_id: ExceptionListTypeEnum.ENDPOINT_BLOCKLISTS, + }; + const decoded = internalCreateExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + test('it should fail when invalid list_id', () => { + const payload = { + ...getCreateExceptionListSchemaMock(), + list_id: ExceptionListTypeEnum.DETECTION, + }; + const decoded = internalCreateExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "detection" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + test('it should accept artifact type', () => { + const payload = { + ...getCreateExceptionListSchemaMock(), + list_id: ExceptionListTypeEnum.ENDPOINT_BLOCKLISTS, + type: ExceptionListTypeEnum.ENDPOINT_BLOCKLISTS, + }; + const decoded = internalCreateExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + test('it should fail when invalid type', () => { + const payload = { + ...getCreateExceptionListSchemaMock(), + list_id: ExceptionListTypeEnum.ENDPOINT_BLOCKLISTS, + type: ExceptionListTypeEnum.DETECTION, + }; + const decoded = internalCreateExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "detection" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.ts new file mode 100644 index 000000000000..ff8f9670a4e0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/internal/create_exception_list_schema/index.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 { ENDPOINT_ARTIFACT_LIST_IDS } from '@kbn/securitysolution-list-constants'; +import * as t from 'io-ts'; + +import { + createExceptionListSchema, + CreateExceptionListSchemaDecoded, +} from '../../create_exception_list_schema'; + +export const internalCreateExceptionListSchema = t.intersection([ + t.exact( + t.type({ + type: t.keyof({ + endpoint: null, + endpoint_events: null, + endpoint_host_isolation_exceptions: null, + endpoint_blocklists: null, + }), + }) + ), + t.exact( + t.partial({ + list_id: t.keyof( + ENDPOINT_ARTIFACT_LIST_IDS.reduce<Record<string, null>>((mapOfListIds, listId) => { + mapOfListIds[listId] = null; + + return mapOfListIds; + }, {}) + ), + }) + ), + createExceptionListSchema, +]); + +export type InternalCreateExceptionListSchema = t.OutputOf< + typeof internalCreateExceptionListSchema +>; + +// This type is used after a decode since some things are defaults after a decode. +export type InternalCreateExceptionListSchemaDecoded = CreateExceptionListSchemaDecoded; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..fd30cba20439 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LIST_ITEM_ID, META, VALUE } from '../../constants/index.mock'; + +import { PatchListItemSchema } from '.'; + +export const getPathListItemSchemaMock = (): PatchListItemSchema => ({ + id: LIST_ITEM_ID, + meta: META, + value: VALUE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.test.ts new file mode 100644 index 000000000000..55050ea6eab8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.test.ts @@ -0,0 +1,81 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getPathListItemSchemaMock } from './index.mock'; +import { PatchListItemSchema, patchListItemSchema } from '.'; + +describe('patch_list_item_schema', () => { + test('it should validate a typical list item request', () => { + const payload = getPathListItemSchemaMock(); + const decoded = patchListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "id"', () => { + const payload = getPathListItemSchemaMock(); + // @ts-expect-error + delete payload.id; + const decoded = patchListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "meta"', () => { + const payload = getPathListItemSchemaMock(); + delete payload.meta; + const decoded = patchListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "value"', () => { + const payload = getPathListItemSchemaMock(); + delete payload.value; + const decoded = patchListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "meta" and "value"', () => { + const payload = getPathListItemSchemaMock(); + delete payload.meta; + delete payload.value; + const decoded = patchListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: PatchListItemSchema & { extraKey?: string } = getPathListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = patchListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.ts new file mode 100644 index 000000000000..caa0d62f003f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_item_schema/index.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 * as t from 'io-ts'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { _version } from '../../common/underscore_version'; +import { id } from '../../common/id'; +import { meta } from '../../common/meta'; +import { value } from '../../common/value'; +import { refresh } from '../../common/refresh'; + +export const patchListItemSchema = t.intersection([ + t.exact( + t.type({ + id, + }) + ), + t.exact(t.partial({ _version, meta, value, refresh })), +]); + +export type PatchListItemSchema = t.OutputOf<typeof patchListItemSchema>; +export type PatchListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf<typeof patchListItemSchema> +>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.mock.ts new file mode 100644 index 000000000000..2b71a46c4665 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DESCRIPTION, LIST_ITEM_ID, META, NAME } from '../../constants/index.mock'; + +import { PatchListSchema } from '.'; + +export const getPathListSchemaMock = (): PatchListSchema => ({ + description: DESCRIPTION, + id: LIST_ITEM_ID, + meta: META, + name: NAME, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.test.ts new file mode 100644 index 000000000000..be0dd3b2f6ea --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.test.ts @@ -0,0 +1,129 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getPathListSchemaMock } from './index.mock'; +import { PatchListSchema, patchListSchema } from '.'; + +describe('patch_list_schema', () => { + test('it should validate a typical list item request', () => { + const payload = getPathListSchemaMock(); + const decoded = patchListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "id"', () => { + const payload = getPathListSchemaMock(); + // @ts-expect-error + delete payload.id; + const decoded = patchListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "meta"', () => { + const payload = getPathListSchemaMock(); + delete payload.meta; + const decoded = patchListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "name"', () => { + const payload = getPathListSchemaMock(); + delete payload.name; + const decoded = patchListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "description"', () => { + const payload = getPathListSchemaMock(); + delete payload.description; + const decoded = patchListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "description", "meta", "name', () => { + const payload = getPathListSchemaMock(); + delete payload.description; + delete payload.name; + delete payload.meta; + const decoded = patchListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "description", "meta"', () => { + const payload = getPathListSchemaMock(); + delete payload.description; + delete payload.meta; + const decoded = patchListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "description", "name"', () => { + const payload = getPathListSchemaMock(); + delete payload.description; + delete payload.name; + const decoded = patchListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "meta", "name"', () => { + const payload = getPathListSchemaMock(); + delete payload.meta; + delete payload.name; + const decoded = patchListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: PatchListSchema & { extraKey?: string } = getPathListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = patchListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.ts new file mode 100644 index 000000000000..cb72d902f189 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/patch_list_schema/index.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { version } from '@kbn/securitysolution-io-ts-types'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { _version } from '../../common/underscore_version'; +import { meta } from '../../common/meta'; +import { name } from '../../common/name'; +import { description } from '../../common/description'; + +export const patchListSchema = t.intersection([ + t.exact( + t.type({ + id, + }) + ), + t.exact( + t.partial({ + _version, // is undefined if not set during decode + description, // is undefined if not set during decode + meta, // is undefined if not set during decode + name, // is undefined if not set during decode + version, // is undefined if not set during decode + }) + ), +]); + +export type PatchListSchema = t.OutputOf<typeof patchListSchema>; +export type PatchListSchemaDecoded = RequiredKeepUndefined<t.TypeOf<typeof patchListSchema>>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..7b83368ad73e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.mock.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ID, ITEM_ID } from '../../constants/index.mock'; + +import { ReadEndpointListItemSchema } from '.'; + +export const getReadEndpointListItemSchemaMock = (): ReadEndpointListItemSchema => ({ + id: ID, + item_id: ITEM_ID, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.test.ts new file mode 100644 index 000000000000..dc8a14b2ef10 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.test.ts @@ -0,0 +1,85 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getReadEndpointListItemSchemaMock } from './index.mock'; +import { ReadEndpointListItemSchema, readEndpointListItemSchema } from '.'; + +describe('read_endpoint_list_item_schema', () => { + test('it should validate a typical list request', () => { + const payload = getReadEndpointListItemSchemaMock(); + const decoded = readEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id"', () => { + const payload = getReadEndpointListItemSchemaMock(); + delete payload.id; + const decoded = readEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "item_id"', () => { + const payload = getReadEndpointListItemSchemaMock(); + delete payload.item_id; + const decoded = readEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept "namespace_type" since endpoint list items do not need it', () => { + const payload: ReadEndpointListItemSchema & { + namespace_type: string; + } = { ...getReadEndpointListItemSchemaMock(), namespace_type: 'single' }; + // @ts-expect-error + delete payload.namespace_type; + const decoded = readEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getReadEndpointListItemSchemaMock()); + }); + + test('it should accept an undefined for "id", "item_id"', () => { + const payload = getReadEndpointListItemSchemaMock(); + delete payload.id; + delete payload.item_id; + const decoded = readEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ReadEndpointListItemSchema & { + extraKey?: string; + } = getReadEndpointListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = readEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.ts new file mode 100644 index 000000000000..6e32b1fe5186 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_endpoint_list_item_schema/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { item_id } from '../../common/item_id'; + +export const readEndpointListItemSchema = t.exact( + t.partial({ + id, + item_id, + }) +); + +export type ReadEndpointListItemSchema = t.OutputOf<typeof readEndpointListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type ReadEndpointListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf<typeof readEndpointListItemSchema> +>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..6ce6e68ec6e0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ID, ITEM_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { ReadExceptionListItemSchema } from '.'; + +export const getReadExceptionListItemSchemaMock = (): ReadExceptionListItemSchema => ({ + id: ID, + item_id: ITEM_ID, + namespace_type: NAMESPACE_TYPE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.test.ts new file mode 100644 index 000000000000..68f636ccb870 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.test.ts @@ -0,0 +1,126 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getReadExceptionListItemSchemaMock } from './index.mock'; +import { ReadExceptionListItemSchema, readExceptionListItemSchema } from '.'; + +describe('read_exception_list_item_schema', () => { + test('it should validate a typical exception list request', () => { + const payload = getReadExceptionListItemSchemaMock(); + const decoded = readExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id"', () => { + const payload = getReadExceptionListItemSchemaMock(); + delete payload.id; + const decoded = readExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "item_id"', () => { + const payload = getReadExceptionListItemSchemaMock(); + delete payload.item_id; + const decoded = readExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "namespace_type" but default to "single"', () => { + const payload = getReadExceptionListItemSchemaMock(); + delete payload.namespace_type; + const decoded = readExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getReadExceptionListItemSchemaMock()); + }); + + test('it should accept an undefined for "id", "item_id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getReadExceptionListItemSchemaMock(); + delete payload.id; + delete payload.namespace_type; + delete payload.item_id; + const output = getReadExceptionListItemSchemaMock(); + delete output.id; + delete output.item_id; + const decoded = readExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should accept an undefined for "id", "item_id"', () => { + const payload = getReadExceptionListItemSchemaMock(); + delete payload.id; + delete payload.item_id; + const decoded = readExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getReadExceptionListItemSchemaMock(); + delete payload.id; + delete payload.namespace_type; + const output = getReadExceptionListItemSchemaMock(); + delete output.id; + const decoded = readExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should accept an undefined for "item_id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getReadExceptionListItemSchemaMock(); + delete payload.namespace_type; + delete payload.item_id; + const output = getReadExceptionListItemSchemaMock(); + delete output.item_id; + const decoded = readExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ReadExceptionListItemSchema & { + extraKey?: string; + } = getReadExceptionListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = readExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.ts new file mode 100644 index 000000000000..9111587af7e9 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_item_schema/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { NamespaceType } from '../../common/default_namespace'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { item_id } from '../../common/item_id'; +import { namespace_type } from '../../common/namespace_type'; + +export const readExceptionListItemSchema = t.exact( + t.partial({ + id, + item_id, + namespace_type, // defaults to 'single' if not set during decode + }) +); + +export type ReadExceptionListItemSchema = t.OutputOf<typeof readExceptionListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type ReadExceptionListItemSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof readExceptionListItemSchema>>, + 'namespace_type' +> & { + namespace_type: NamespaceType; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.mock.ts new file mode 100644 index 000000000000..f122f1d20339 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ID, LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { ReadExceptionListSchema } from '.'; + +export const getReadExceptionListSchemaMock = (): ReadExceptionListSchema => ({ + id: ID, + list_id: LIST_ID, + namespace_type: NAMESPACE_TYPE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.test.ts new file mode 100644 index 000000000000..89cd869bd17c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.test.ts @@ -0,0 +1,126 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getReadExceptionListSchemaMock } from './index.mock'; +import { ReadExceptionListSchema, readExceptionListSchema } from '.'; + +describe('read_exception_list_schema', () => { + test('it should validate a typical exception list request', () => { + const payload = getReadExceptionListSchemaMock(); + const decoded = readExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id"', () => { + const payload = getReadExceptionListSchemaMock(); + delete payload.id; + const decoded = readExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "list_id"', () => { + const payload = getReadExceptionListSchemaMock(); + delete payload.list_id; + const decoded = readExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "namespace_type" but default to "single"', () => { + const payload = getReadExceptionListSchemaMock(); + delete payload.namespace_type; + const decoded = readExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getReadExceptionListSchemaMock()); + }); + + test('it should accept an undefined for "id", "list_id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getReadExceptionListSchemaMock(); + delete payload.id; + delete payload.namespace_type; + delete payload.list_id; + const output = getReadExceptionListSchemaMock(); + delete output.id; + delete output.list_id; + const decoded = readExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should accept an undefined for "id", "list_id"', () => { + const payload = getReadExceptionListSchemaMock(); + delete payload.id; + delete payload.list_id; + const decoded = readExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getReadExceptionListSchemaMock(); + delete payload.id; + delete payload.namespace_type; + const output = getReadExceptionListSchemaMock(); + delete output.id; + const decoded = readExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should accept an undefined for "list_id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getReadExceptionListSchemaMock(); + delete payload.namespace_type; + delete payload.list_id; + const output = getReadExceptionListSchemaMock(); + delete output.list_id; + const decoded = readExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ReadExceptionListSchema & { + extraKey?: string; + } = getReadExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = readExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.ts new file mode 100644 index 000000000000..b4d9b7309fb5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_exception_list_schema/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { NamespaceType } from '../../common/default_namespace'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { list_id } from '../../common/list_id'; +import { namespace_type } from '../../common/namespace_type'; + +export const readExceptionListSchema = t.exact( + t.partial({ + id, + list_id, + namespace_type, // defaults to 'single' if not set during decode + }) +); + +export type ReadExceptionListSchema = t.OutputOf<typeof readExceptionListSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type ReadExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof readExceptionListSchema>>, + 'namespace_type' +> & { + namespace_type: NamespaceType; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..a23911123140 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LIST_ID, LIST_ITEM_ID, VALUE } from '../../constants/index.mock'; + +import { ReadListItemSchema } from '.'; + +export const getReadListItemSchemaMock = (): ReadListItemSchema => ({ + id: LIST_ITEM_ID, + list_id: LIST_ID, + value: VALUE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.test.ts new file mode 100644 index 000000000000..9370aa5e3d45 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.test.ts @@ -0,0 +1,117 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getReadListItemSchemaMock } from './index.mock'; +import { ReadListItemSchema, readListItemSchema } from '.'; + +describe('read_list_item_schema', () => { + test('it should validate a typical list item request', () => { + const payload = getReadListItemSchemaMock(); + const decoded = readListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id"', () => { + const payload = getReadListItemSchemaMock(); + delete payload.id; + const decoded = readListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "list_id"', () => { + const payload = getReadListItemSchemaMock(); + delete payload.list_id; + const decoded = readListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "value"', () => { + const payload = getReadListItemSchemaMock(); + delete payload.value; + const decoded = readListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id", "list_id", "value"', () => { + const payload = getReadListItemSchemaMock(); + delete payload.id; + delete payload.value; + delete payload.list_id; + const decoded = readListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id", "list_id"', () => { + const payload = getReadListItemSchemaMock(); + delete payload.id; + delete payload.list_id; + const decoded = readListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id", "value"', () => { + const payload = getReadListItemSchemaMock(); + delete payload.id; + delete payload.value; + const decoded = readListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "list_id", "value"', () => { + const payload = getReadListItemSchemaMock(); + delete payload.value; + delete payload.list_id; + const decoded = readListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ReadListItemSchema & { extraKey?: string } = getReadListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = readListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.ts new file mode 100644 index 000000000000..571f37ff403e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_item_schema/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { list_id } from '../../common/list_id'; +import { value } from '../../common/value'; + +export const readListItemSchema = t.exact(t.partial({ id, list_id, value })); + +export type ReadListItemSchema = t.OutputOf<typeof readListItemSchema>; +export type ReadListItemSchemaDecoded = RequiredKeepUndefined<t.TypeOf<typeof readListItemSchema>>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.mock.ts new file mode 100644 index 000000000000..c82b34381554 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.mock.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 { LIST_ID } from '../../constants/index.mock'; + +import { ReadListSchema } from '.'; + +export const getReadListSchemaMock = (): ReadListSchema => ({ + id: LIST_ID, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.test.ts new file mode 100644 index 000000000000..1953341d2be4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.test.ts @@ -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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getReadListSchemaMock } from './index.mock'; +import { ReadListSchema, readListSchema } from '.'; + +describe('read_list_schema', () => { + test('it should validate a typical list item request', () => { + const payload = getReadListSchemaMock(); + const decoded = readListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "id"', () => { + const payload = getReadListSchemaMock(); + // @ts-expect-error + delete payload.id; + const decoded = readListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ReadListSchema & { extraKey?: string } = getReadListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = readListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.ts new file mode 100644 index 000000000000..6382a7c0ad97 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/read_list_schema/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { id } from '../../common/id'; + +export const readListSchema = t.exact( + t.type({ + id, + }) +); + +export type ReadListSchema = t.OutputOf<typeof readListSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.mock.ts new file mode 100644 index 000000000000..af6dc55b61da --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FILTER, ID, LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { SummaryExceptionListSchema } from '.'; + +export const getSummaryExceptionListSchemaMock = (): SummaryExceptionListSchema => ({ + filter: FILTER, + id: ID, + list_id: LIST_ID, + namespace_type: NAMESPACE_TYPE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts new file mode 100644 index 000000000000..53a734ed034e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.test.ts @@ -0,0 +1,136 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getSummaryExceptionListSchemaMock } from './index.mock'; +import { SummaryExceptionListSchema, summaryExceptionListSchema } from '.'; + +describe('summary_exception_list_schema', () => { + test('it should validate a typical exception list request', () => { + const payload = getSummaryExceptionListSchemaMock(); + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = foldLeftRight(checked); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "filter"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.filter; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = foldLeftRight(checked); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = foldLeftRight(checked); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "list_id"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.list_id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = foldLeftRight(checked); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "namespace_type" but default to "single"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.namespace_type; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = foldLeftRight(checked); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getSummaryExceptionListSchemaMock()); + }); + + test('it should accept an undefined for "id", "list_id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.id; + delete payload.namespace_type; + delete payload.list_id; + const output = getSummaryExceptionListSchemaMock(); + delete output.id; + delete output.list_id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = foldLeftRight(checked); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should accept an undefined for "id", "list_id"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.id; + delete payload.list_id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = foldLeftRight(checked); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.id; + delete payload.namespace_type; + const output = getSummaryExceptionListSchemaMock(); + delete output.id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = foldLeftRight(checked); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should accept an undefined for "list_id", "namespace_type" but default "namespace_type" to "single"', () => { + const payload = getSummaryExceptionListSchemaMock(); + delete payload.namespace_type; + delete payload.list_id; + const output = getSummaryExceptionListSchemaMock(); + delete output.list_id; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = foldLeftRight(checked); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(output); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: SummaryExceptionListSchema & { + extraKey?: string; + } = getSummaryExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = summaryExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = foldLeftRight(checked); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.ts new file mode 100644 index 000000000000..6be4f0c57b08 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/summary_exception_list_schema/index.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { NamespaceType } from '../../common/default_namespace'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { filter, Filter } from '../../common/filter'; +import { list_id } from '../../common/list_id'; +import { namespace_type } from '../../common/namespace_type'; + +export const summaryExceptionListSchema = t.exact( + t.partial({ + filter, + id, + list_id, + namespace_type, // defaults to 'single' if not set during decode + }) +); + +export type SummaryExceptionListSchema = t.OutputOf<typeof summaryExceptionListSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type SummaryExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof summaryExceptionListSchema>>, + 'namespace_type' +> & { + namespace_type: NamespaceType; + filter: Filter; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..57244aac0426 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.mock.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + COMMENTS, + DESCRIPTION, + ENTRIES, + ID, + ITEM_TYPE, + LIST_ITEM_ID, + META, + NAME, + OS_TYPES, + TAGS, +} from '../../constants/index.mock'; + +import { UpdateEndpointListItemSchema } from '.'; + +export const getUpdateEndpointListItemSchemaMock = (): UpdateEndpointListItemSchema => ({ + _version: undefined, + comments: COMMENTS, + description: DESCRIPTION, + entries: ENTRIES, + id: ID, + item_id: LIST_ITEM_ID, + meta: META, + name: NAME, + os_types: OS_TYPES, + tags: TAGS, + type: ITEM_TYPE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.test.ts new file mode 100644 index 000000000000..1def91ca1a8e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.test.ts @@ -0,0 +1,138 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { UpdateEndpointListItemSchema, updateEndpointListItemSchema } from '.'; +import { getUpdateEndpointListItemSchemaMock } from './index.mock'; + +describe('update_endpoint_list_item_schema', () => { + test('it should validate a typical list item request', () => { + const payload = getUpdateEndpointListItemSchemaMock(); + const decoded = updateEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not accept an undefined for "description"', () => { + const payload = getUpdateEndpointListItemSchemaMock(); + // @ts-expect-error + delete payload.description; + const decoded = updateEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not accept an undefined for "name"', () => { + const payload = getUpdateEndpointListItemSchemaMock(); + // @ts-expect-error + delete payload.name; + const decoded = updateEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "name"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not accept an undefined for "type"', () => { + const payload = getUpdateEndpointListItemSchemaMock(); + // @ts-expect-error + delete payload.type; + const decoded = updateEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not accept a value for "list_id"', () => { + const payload: UpdateEndpointListItemSchema & { + list_id?: string; + } = getUpdateEndpointListItemSchemaMock(); + payload.list_id = 'some new list_id'; + const decoded = updateEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "list_id"']); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "meta" but strip it out', () => { + const payload = getUpdateEndpointListItemSchemaMock(); + const outputPayload = getUpdateEndpointListItemSchemaMock(); + delete payload.meta; + const decoded = updateEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete outputPayload.meta; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for "comments" but return an array', () => { + const inputPayload = getUpdateEndpointListItemSchemaMock(); + const outputPayload = getUpdateEndpointListItemSchemaMock(); + delete inputPayload.comments; + outputPayload.comments = []; + const decoded = updateEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should NOT accept an undefined for "entries"', () => { + const inputPayload = getUpdateEndpointListItemSchemaMock(); + const outputPayload = getUpdateEndpointListItemSchemaMock(); + // @ts-expect-error + delete inputPayload.entries; + outputPayload.entries = []; + const decoded = updateEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "tags" but return an array', () => { + const inputPayload = getUpdateEndpointListItemSchemaMock(); + const outputPayload = getUpdateEndpointListItemSchemaMock(); + delete inputPayload.tags; + outputPayload.tags = []; + const decoded = updateEndpointListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: UpdateEndpointListItemSchema & { + extraKey?: string; + } = getUpdateEndpointListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = updateEndpointListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.ts new file mode 100644 index 000000000000..e301137058af --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { DefaultUpdateCommentsArray } from '../../common/default_update_comments_array'; +import { exceptionListItemType } from '../../common/exception_list_item_type'; +import { nonEmptyEntriesArray } from '../../common/non_empty_entries_array'; +import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; +import { description } from '../../common/description'; +import { name } from '../../common/name'; +import { _version } from '../../common/underscore_version'; +import { id } from '../../common/id'; +import { meta } from '../../common/meta'; +import { Tags, tags } from '../../common/tags'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { UpdateCommentsArray } from '../../common/update_comment'; +import { EntriesArray } from '../../common/entries'; + +export const updateEndpointListItemSchema = t.intersection([ + t.exact( + t.type({ + description, + entries: nonEmptyEntriesArray, + name, + type: exceptionListItemType, + }) + ), + t.exact( + t.partial({ + _version, // defaults to undefined if not set during decode + comments: DefaultUpdateCommentsArray, // defaults to empty array if not set during decode + id, // defaults to undefined if not set during decode + item_id: t.union([t.string, t.undefined]), + meta, // defaults to undefined if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type UpdateEndpointListItemSchema = t.OutputOf<typeof updateEndpointListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type UpdateEndpointListItemSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof updateEndpointListItemSchema>>, + 'tags' | 'entries' | 'comments' +> & { + comments: UpdateCommentsArray; + tags: Tags; + entries: EntriesArray; + os_types: OsTypeArray; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..c7560348179d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.mock.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + COMMENTS, + DESCRIPTION, + ENTRIES, + ID, + ITEM_ID, + ITEM_TYPE, + LIST_ITEM_ID, + META, + NAME, + NAMESPACE_TYPE, + OS_TYPES, + TAGS, +} from '../../constants/index.mock'; + +import { UpdateExceptionListItemSchema } from '.'; + +export const getUpdateExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ + _version: undefined, + comments: COMMENTS, + description: DESCRIPTION, + entries: ENTRIES, + id: ID, + item_id: LIST_ITEM_ID, + meta: META, + name: NAME, + namespace_type: NAMESPACE_TYPE, + os_types: ['linux'], + tags: TAGS, + type: ITEM_TYPE, +}); + +/** + * Useful for end to end tests and other mechanisms which want to fill in the values + * after doing a get of the structure. + */ +export const getUpdateMinimalExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ + description: DESCRIPTION, + entries: ENTRIES, + item_id: ITEM_ID, + name: NAME, + os_types: OS_TYPES, + type: ITEM_TYPE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.test.ts new file mode 100644 index 000000000000..0adbf1224d72 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.test.ts @@ -0,0 +1,160 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { UpdateExceptionListItemSchema, updateExceptionListItemSchema } from '.'; +import { getUpdateExceptionListItemSchemaMock } from './index.mock'; + +describe('update_exception_list_item_schema', () => { + test('it should validate a typical exception list item request', () => { + const payload = getUpdateExceptionListItemSchemaMock(); + const decoded = updateExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not accept an undefined for "description"', () => { + const payload = getUpdateExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.description; + const decoded = updateExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not accept an undefined for "name"', () => { + const payload = getUpdateExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.name; + const decoded = updateExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "name"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not accept an undefined for "type"', () => { + const payload = getUpdateExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.type; + const decoded = updateExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not accept a value for "list_id"', () => { + const payload: UpdateExceptionListItemSchema & { + list_id?: string; + } = getUpdateExceptionListItemSchemaMock(); + payload.list_id = 'some new list_id'; + const decoded = updateExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "list_id"']); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "meta" but strip it out', () => { + const payload = getUpdateExceptionListItemSchemaMock(); + const outputPayload = getUpdateExceptionListItemSchemaMock(); + delete payload.meta; + const decoded = updateExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete outputPayload.meta; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for "comments" but return an array', () => { + const inputPayload = getUpdateExceptionListItemSchemaMock(); + const outputPayload = getUpdateExceptionListItemSchemaMock(); + delete inputPayload.comments; + outputPayload.comments = []; + const decoded = updateExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should NOT accept an undefined for "entries"', () => { + const inputPayload = getUpdateExceptionListItemSchemaMock(); + const outputPayload = getUpdateExceptionListItemSchemaMock(); + // @ts-expect-error + delete inputPayload.entries; + outputPayload.entries = []; + const decoded = updateExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "namespace_type" but return enum "single"', () => { + const inputPayload = getUpdateExceptionListItemSchemaMock(); + const outputPayload = getUpdateExceptionListItemSchemaMock(); + delete inputPayload.namespace_type; + outputPayload.namespace_type = 'single'; + const decoded = updateExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for "tags" but return an array', () => { + const inputPayload = getUpdateExceptionListItemSchemaMock(); + const outputPayload = getUpdateExceptionListItemSchemaMock(); + delete inputPayload.tags; + outputPayload.tags = []; + const decoded = updateExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for "item_id" and generate a correct body not counting the uuid', () => { + const inputPayload = getUpdateExceptionListItemSchemaMock(); + delete inputPayload.item_id; + const decoded = updateExceptionListItemSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as UpdateExceptionListItemSchema).item_id; + expect(message.schema).toEqual(inputPayload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: UpdateExceptionListItemSchema & { + extraKey?: string; + } = getUpdateExceptionListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = updateExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.ts new file mode 100644 index 000000000000..910acbe2c942 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { ExceptionListItemEntryArray } from '@kbn/securitysolution-exceptions-common/api'; +import { NamespaceType } from '../../common/default_namespace'; +import { DefaultUpdateCommentsArray } from '../../common/default_update_comments_array'; +import { exceptionListItemType } from '../../common/exception_list_item_type'; +import { nonEmptyEntriesArray } from '../../common/non_empty_entries_array'; +import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { Tags, tags } from '../../common/tags'; +import { UpdateCommentsArray } from '../../common/update_comment'; +import { description } from '../../common/description'; +import { name } from '../../common/name'; +import { _version } from '../../common/underscore_version'; +import { id } from '../../common/id'; +import { item_id } from '../../common/item_id'; +import { meta } from '../../common/meta'; +import { namespace_type } from '../../common/namespace_type'; +import { ExpireTimeOrUndefined, expireTimeOrUndefined } from '../../common'; + +export const updateExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + description, + entries: nonEmptyEntriesArray, + name, + type: exceptionListItemType, + }) + ), + t.exact( + t.partial({ + _version, // defaults to undefined if not set during decode + comments: DefaultUpdateCommentsArray, // defaults to empty array if not set during decode + expire_time: expireTimeOrUndefined, + id, // defaults to undefined if not set during decode + item_id, + meta, // defaults to undefined if not set during decode + namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode + tags, // defaults to empty array if not set during decode + }) + ), +]); + +export type UpdateExceptionListItemSchema = t.OutputOf<typeof updateExceptionListItemSchema>; + +// This type is used after a decode since some things are defaults after a decode. +export type UpdateExceptionListItemSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof updateExceptionListItemSchema>>, + 'tags' | 'entries' | 'namespace_type' | 'comments' | 'os_types' | 'expire_time' +> & { + comments: UpdateCommentsArray; + tags: Tags; + entries: ExceptionListItemEntryArray; + namespace_type: NamespaceType; + os_types: OsTypeArray; + expire_time: ExpireTimeOrUndefined; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.mock.ts new file mode 100644 index 000000000000..9aac2453a7be --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.mock.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 { DESCRIPTION, ID, LIST_ID, META, NAME, NAMESPACE_TYPE } from '../../constants/index.mock'; + +import { UpdateExceptionListSchema } from '.'; + +export const getUpdateExceptionListSchemaMock = (): UpdateExceptionListSchema => ({ + _version: undefined, + description: DESCRIPTION, + id: ID, + list_id: LIST_ID, + meta: META, + name: NAME, + namespace_type: NAMESPACE_TYPE, + os_types: [], + tags: ['malware'], + type: 'endpoint', +}); + +/** + * Useful for end to end tests and other mechanisms which want to fill in the values + * after doing a get of the structure. + */ +export const getUpdateMinimalExceptionListSchemaMock = (): UpdateExceptionListSchema => ({ + description: DESCRIPTION, + list_id: LIST_ID, + name: NAME, + type: 'endpoint', +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.test.ts new file mode 100644 index 000000000000..029751ac4d14 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.test.ts @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { UpdateExceptionListSchema, updateExceptionListSchema } from '.'; +import { getUpdateExceptionListSchemaMock } from './index.mock'; + +describe('update_exception_list_schema', () => { + test('it should validate a typical exception list request', () => { + const payload = getUpdateExceptionListSchemaMock(); + const decoded = updateExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not accept an undefined for "description"', () => { + const payload = getUpdateExceptionListSchemaMock(); + // @ts-expect-error + delete payload.description; + const decoded = updateExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not accept an undefined for "name"', () => { + const payload = getUpdateExceptionListSchemaMock(); + // @ts-expect-error + delete payload.name; + const decoded = updateExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "name"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not accept an undefined for "type"', () => { + const payload = getUpdateExceptionListSchemaMock(); + // @ts-expect-error + delete payload.type; + const decoded = updateExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "meta" but strip it out', () => { + const payload = getUpdateExceptionListSchemaMock(); + const outputPayload = getUpdateExceptionListSchemaMock(); + delete payload.meta; + const decoded = updateExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete outputPayload.meta; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for "namespace_type" but return enum "single"', () => { + const inputPayload = getUpdateExceptionListSchemaMock(); + const outputPayload = getUpdateExceptionListSchemaMock(); + delete inputPayload.namespace_type; + outputPayload.namespace_type = 'single'; + const decoded = updateExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for "tags" but return an array', () => { + const inputPayload = getUpdateExceptionListSchemaMock(); + const outputPayload = getUpdateExceptionListSchemaMock(); + delete inputPayload.tags; + outputPayload.tags = []; + const decoded = updateExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should accept an undefined for "list_id" and generate a correct body not counting the uuid', () => { + const inputPayload = getUpdateExceptionListSchemaMock(); + delete inputPayload.list_id; + const decoded = updateExceptionListSchema.decode(inputPayload); + const checked = exactCheck(inputPayload, decoded); + const message = pipe(checked, foldLeftRight); + delete (message.schema as UpdateExceptionListSchema).list_id; + expect(message.schema).toEqual(inputPayload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: UpdateExceptionListSchema & { + extraKey?: string; + } = getUpdateExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = updateExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.ts new file mode 100644 index 000000000000..be4006c3efa1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_schema/index.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { version } from '@kbn/securitysolution-io-ts-types'; +import { OsTypeArray, osTypeArrayOrUndefined } from '../../common/os_type'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { Tags, tags } from '../../common/tags'; +import { NamespaceType } from '../../common/default_namespace'; +import { description } from '../../common/description'; +import { name } from '../../common/name'; +import { _version } from '../../common/underscore_version'; +import { exceptionListType } from '../../common/exception_list'; +import { id } from '../../common/id'; +import { list_id } from '../../common/list_id'; +import { meta } from '../../common/meta'; +import { namespace_type } from '../../common/namespace_type'; + +export const updateExceptionListSchema = t.intersection([ + t.exact( + t.type({ + description, + name, + type: exceptionListType, + }) + ), + t.exact( + t.partial({ + _version, // defaults to undefined if not set during decode + id, // defaults to undefined if not set during decode + list_id, // defaults to undefined if not set during decode + meta, // defaults to undefined if not set during decode + namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode + tags, // defaults to empty array if not set during decode + version, // defaults to undefined if not set during decode + }) + ), +]); + +export type UpdateExceptionListSchema = t.OutputOf<typeof updateExceptionListSchema>; + +// This type is used after a decode since the arrays turn into defaults of empty arrays. +export type UpdateExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined<t.TypeOf<typeof updateExceptionListSchema>>, + 'tags | namespace_type' | 'os_types' +> & { + tags: Tags; + namespace_type: NamespaceType; + os_types: OsTypeArray; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..7d65f59aa8cd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.mock.ts @@ -0,0 +1,24 @@ +/* + * 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 { ID, LIST_ITEM_ID, META, VALUE } from '../../constants/index.mock'; + +import { UpdateListItemSchema } from '.'; + +export const getUpdateListItemSchemaMock = (): UpdateListItemSchema => ({ + id: ID, + meta: META, + value: VALUE, +}); + +/** + * Useful for end to end testing + */ +export const getUpdateMinimalListItemSchemaMock = (): UpdateListItemSchema => ({ + id: LIST_ITEM_ID, + value: VALUE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.test.ts new file mode 100644 index 000000000000..ca6402455aab --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.test.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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { UpdateListItemSchema, updateListItemSchema } from '.'; +import { getUpdateListItemSchemaMock } from './index.mock'; + +describe('update_list_item_schema', () => { + test('it should validate a typical list item request', () => { + const payload = getUpdateListItemSchemaMock(); + const decoded = updateListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "meta" but strip it out', () => { + const payload = getUpdateListItemSchemaMock(); + const outputPayload = getUpdateListItemSchemaMock(); + delete payload.meta; + const decoded = updateListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete outputPayload.meta; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: UpdateListItemSchema & { + extraKey?: string; + } = getUpdateListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = updateListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.ts new file mode 100644 index 000000000000..2df22b5c57b7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_item_schema/index.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 * as t from 'io-ts'; + +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { _version } from '../../common/underscore_version'; +import { id } from '../../common/id'; +import { value } from '../../common/value'; +import { meta } from '../../common/meta'; + +export const updateListItemSchema = t.intersection([ + t.exact( + t.type({ + id, + value, + }) + ), + t.exact( + t.partial({ + _version, // defaults to undefined if not set during decode + meta, // defaults to undefined if not set during decode + }) + ), +]); + +export type UpdateListItemSchema = t.OutputOf<typeof updateListItemSchema>; +export type UpdateListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf<typeof updateListItemSchema> +>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.mock.ts new file mode 100644 index 000000000000..99362d8ef8a7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.mock.ts @@ -0,0 +1,28 @@ +/* + * 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 { DESCRIPTION, LIST_ID, META, NAME, _VERSION } from '../../constants/index.mock'; + +import { UpdateListSchema } from '.'; + +export const getUpdateListSchemaMock = (): UpdateListSchema => ({ + _version: _VERSION, + description: DESCRIPTION, + id: LIST_ID, + meta: META, + name: NAME, +}); + +/** + * Useful for end to end tests and other mechanisms which want to fill in the values + * after doing a get of the structure. + */ +export const getUpdateMinimalListSchemaMock = (): UpdateListSchema => ({ + description: DESCRIPTION, + id: LIST_ID, + name: NAME, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.test.ts new file mode 100644 index 000000000000..c35861f66e91 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.test.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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { UpdateListSchema, updateListSchema } from '.'; +import { getUpdateListSchemaMock } from './index.mock'; + +describe('update_list_schema', () => { + test('it should validate a typical list request', () => { + const payload = getUpdateListSchemaMock(); + const decoded = updateListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "meta" but strip it out', () => { + const payload = getUpdateListSchemaMock(); + const outputPayload = getUpdateListSchemaMock(); + delete payload.meta; + const decoded = updateListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + delete outputPayload.meta; + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(outputPayload); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: UpdateListSchema & { + extraKey?: string; + } = getUpdateListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = updateListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.ts new file mode 100644 index 000000000000..c6182af2f23f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/request/update_list_schema/index.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { version } from '@kbn/securitysolution-io-ts-types'; +import { RequiredKeepUndefined } from '../../common/required_keep_undefined'; +import { id } from '../../common/id'; +import { name } from '../../common/name'; +import { description } from '../../common/description'; +import { _version } from '../../common/underscore_version'; +import { meta } from '../../common/meta'; + +export const updateListSchema = t.intersection([ + t.exact( + t.type({ + description, + id, + name, + }) + ), + t.exact( + t.partial({ + _version, // defaults to undefined if not set during decode + meta, // defaults to undefined if not set during decode + version, // defaults to undefined if not set during decode + }) + ), +]); + +export type UpdateListSchema = t.OutputOf<typeof updateListSchema>; +export type UpdateListSchemaDecoded = RequiredKeepUndefined<t.TypeOf<typeof updateListSchema>>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.mock.ts new file mode 100644 index 000000000000..2c4ac68f4613 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.mock.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. + */ + +import { AcknowledgeSchema } from '.'; + +export const getAcknowledgeSchemaResponseMock = (): AcknowledgeSchema => ({ + acknowledged: true, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.test.ts new file mode 100644 index 000000000000..4fd4adf861bb --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.test.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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getAcknowledgeSchemaResponseMock } from './index.mock'; +import { AcknowledgeSchema, acknowledgeSchema } from '.'; + +describe('acknowledge_schema', () => { + test('it should validate a typical response', () => { + const payload = getAcknowledgeSchemaResponseMock(); + const decoded = acknowledgeSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + test('it should NOT accept an undefined for "ok"', () => { + const payload = getAcknowledgeSchemaResponseMock(); + // @ts-expect-error + delete payload.acknowledged; + const decoded = acknowledgeSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "acknowledged"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: AcknowledgeSchema & { extraKey?: string } = getAcknowledgeSchemaResponseMock(); + payload.extraKey = 'some new value'; + const decoded = acknowledgeSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/index.ts new file mode 100644 index 000000000000..97a0dc23682c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/acknowledge_schema/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. + */ + +import * as t from 'io-ts'; + +export const acknowledgeSchema = t.exact(t.type({ acknowledged: t.boolean })); + +export type AcknowledgeSchema = t.TypeOf<typeof acknowledgeSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.test.ts new file mode 100644 index 000000000000..2ac63d9719e2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { CreateEndpointListSchema, createEndpointListSchema } from '.'; +import { getExceptionListSchemaMock } from '../exception_list_schema/index.mock'; + +describe('create_endpoint_list_schema', () => { + test('it should validate a typical endpoint list response', () => { + const payload = getExceptionListSchemaMock(); + const decoded = createEndpointListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an empty object when an endpoint list already exists', () => { + const payload: CreateEndpointListSchema = {}; + const decoded = createEndpointListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "list_id"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.list_id; + const decoded = createEndpointListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'invalid keys "_version,created_at,created_by,description,id,immutable,meta,{},name,namespace_type,os_types,["linux"],tags,["user added string for a tag","malware"],tie_breaker_id,type,updated_at,updated_by,version"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT allow missing fields', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.list_id; + const decoded = createEndpointListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors)).length).toEqual(1); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: CreateEndpointListSchema & { + extraKey?: string; + } = getExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = createEndpointListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.ts new file mode 100644 index 000000000000..44f3629973a0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/create_endpoint_list_schema/index.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. + */ + +import * as t from 'io-ts'; +import { exceptionListSchema } from '../exception_list_schema'; + +export const createEndpointListSchema = t.union([exceptionListSchema, t.exact(t.type({}))]); + +export type CreateEndpointListSchema = t.TypeOf<typeof createEndpointListSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..fad9dc5990a8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.mock.ts @@ -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 { + COMMENTS, + DATE_NOW, + DESCRIPTION, + ELASTIC_USER, + ENTRIES, + ITEM_ID, + ITEM_TYPE, + LIST_ID, + META, + NAME, + NAMESPACE_TYPE, + OS_TYPES, + TIE_BREAKER, + USER, +} from '../../constants/index.mock'; + +import { ExceptionListItemSchema } from '.'; + +export const getExceptionListItemSchemaMock = ( + overrides?: Partial<ExceptionListItemSchema> +): ExceptionListItemSchema => ({ + _version: undefined, + comments: COMMENTS, + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, + entries: ENTRIES, + expire_time: undefined, + id: '1', + item_id: 'endpoint_list_item', + list_id: 'endpoint_list_id', + meta: META, + name: NAME, + namespace_type: NAMESPACE_TYPE, + os_types: ['linux'], + tags: ['user added string for a tag', 'malware'], + tie_breaker_id: TIE_BREAKER, + type: ITEM_TYPE, + updated_at: DATE_NOW, + updated_by: USER, + ...(overrides || {}), +}); + +/** + * This is useful for end to end tests where we remove the auto generated parts for comparisons + * such as created_at, updated_at, and id. + */ +export const getExceptionListItemResponseMockWithoutAutoGeneratedValues = + (): Partial<ExceptionListItemSchema> => ({ + comments: [], + created_by: ELASTIC_USER, + description: DESCRIPTION, + entries: ENTRIES, + item_id: ITEM_ID, + list_id: LIST_ID, + name: NAME, + namespace_type: 'single', + os_types: OS_TYPES, + tags: [], + type: ITEM_TYPE, + updated_by: ELASTIC_USER, + }); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.test.ts new file mode 100644 index 000000000000..1231764ae7f5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.test.ts @@ -0,0 +1,240 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getExceptionListItemSchemaMock } from './index.mock'; +import { ExceptionListItemSchema, exceptionListItemSchema } from '.'; + +describe('exception_list_item_schema', () => { + test('it should validate a typical exception list item response', () => { + const payload = getExceptionListItemSchemaMock(); + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "id"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.id; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "list_id"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.list_id; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "item_id"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.item_id; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "item_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "comments"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.comments; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "comments"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "entries"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.entries; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "name"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.name; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "name"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "namespace_type" and return "single" as a default value for "namespace_type"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.namespace_type; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect((message.schema as ExceptionListItemSchema).namespace_type).toEqual('single'); + }); + + test('it should NOT accept an undefined for "description"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.description; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "meta"', () => { + const payload = getExceptionListItemSchemaMock(); + delete payload.meta; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "created_at"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.created_at; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "created_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "created_by"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.created_by; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "created_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "tie_breaker_id"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.tie_breaker_id; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "tie_breaker_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "type"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.type; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "updated_at"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.updated_at; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "updated_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "updated_by"', () => { + const payload = getExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.updated_by; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "updated_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ExceptionListItemSchema & { + extraKey?: string; + } = getExceptionListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = exceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.ts new file mode 100644 index 000000000000..f3f3986c4ea3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { namespace_type } from '../../common/namespace_type'; +import { metaOrUndefined } from '../../common/meta'; +import { name } from '../../common/name'; +import { created_at } from '../../common/created_at'; +import { created_by } from '../../common/created_by'; +import { id } from '../../common/id'; +import { tie_breaker_id } from '../../common/tie_breaker_id'; +import { updated_at } from '../../common/updated_at'; +import { updated_by } from '../../common/updated_by'; +import { list_id } from '../../common/list_id'; +import { description } from '../../common/description'; +import { osTypeArray } from '../../common/os_type'; +import { tags } from '../../common/tags'; +import { _versionOrUndefined } from '../../common/underscore_version'; +import { commentsArray } from '../../common/comment'; +import { entriesArray } from '../../common/entries'; +import { item_id } from '../../common/item_id'; +import { exceptionListItemType } from '../../common/exception_list_item_type'; +import { expireTimeOrUndefined } from '../../common/expire_time'; + +export const exceptionListItemSchema = t.exact( + t.type({ + _version: _versionOrUndefined, + comments: commentsArray, + created_at, + created_by, + description, + entries: entriesArray, + expire_time: expireTimeOrUndefined, + id, + item_id, + list_id, + meta: metaOrUndefined, + name, + namespace_type, + os_types: osTypeArray, + tags, + tie_breaker_id, + type: exceptionListItemType, + updated_at, + updated_by, + }) +); + +export type ExceptionListItemSchema = t.TypeOf<typeof exceptionListItemSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.mock.ts new file mode 100644 index 000000000000..07b47f91fead --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.mock.ts @@ -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 { + DATE_NOW, + DESCRIPTION, + ELASTIC_USER, + ENDPOINT_TYPE, + IMMUTABLE, + LIST_ID, + META, + NAME, + TIE_BREAKER, + USER, + VERSION, + _VERSION, +} from '../../constants/index.mock'; +import { + ENDPOINT_LIST_ID, + ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION, + ENDPOINT_TRUSTED_APPS_LIST_ID, + ENDPOINT_TRUSTED_APPS_LIST_NAME, +} from '@kbn/securitysolution-list-constants'; + +import { ExceptionListSchema } from '.'; + +export const getExceptionListSchemaMock = (): ExceptionListSchema => ({ + _version: _VERSION, + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, + id: '1', + immutable: IMMUTABLE, + list_id: ENDPOINT_LIST_ID, + meta: META, + name: 'Sample Endpoint Exception List', + namespace_type: 'agnostic', + os_types: ['linux'], + tags: ['user added string for a tag', 'malware'], + tie_breaker_id: TIE_BREAKER, + type: ENDPOINT_TYPE, + updated_at: DATE_NOW, + updated_by: 'user_name', + version: VERSION, +}); + +export const getTrustedAppsListSchemaMock = (): ExceptionListSchema => { + return { + ...getExceptionListSchemaMock(), + description: ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION, + list_id: ENDPOINT_TRUSTED_APPS_LIST_ID, + name: ENDPOINT_TRUSTED_APPS_LIST_NAME, + }; +}; + +/** + * This is useful for end to end tests where we remove the auto generated parts for comparisons + * such as created_at, updated_at, and id. + */ +export const getExceptionResponseMockWithoutAutoGeneratedValues = + (): Partial<ExceptionListSchema> => ({ + created_by: ELASTIC_USER, + description: DESCRIPTION, + immutable: IMMUTABLE, + list_id: LIST_ID, + name: NAME, + namespace_type: 'single', + os_types: [], + tags: [], + type: ENDPOINT_TYPE, + updated_by: ELASTIC_USER, + version: VERSION, + }); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.test.ts new file mode 100644 index 000000000000..b043cde4c3f0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.test.ts @@ -0,0 +1,198 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getExceptionListSchemaMock } from './index.mock'; +import { ExceptionListSchema, exceptionListSchema } from '.'; + +describe('exception_list_schema', () => { + test('it should validate a typical exception list response', () => { + const payload = getExceptionListSchemaMock(); + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "id"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.id; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "list_id"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.list_id; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "name"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.name; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "name"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "namespace_type" and make "namespace_type" that of "single"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.namespace_type; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect((message.schema as ExceptionListSchema).namespace_type).toEqual('single'); + }); + + test('it should NOT accept an undefined for "description"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.description; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "meta"', () => { + const payload = getExceptionListSchemaMock(); + delete payload.meta; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "created_at"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.created_at; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "created_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "created_by"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.created_by; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "created_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "tie_breaker_id"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.tie_breaker_id; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "tie_breaker_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "type"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.type; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "updated_at"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.updated_at; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "updated_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "updated_by"', () => { + const payload = getExceptionListSchemaMock(); + // @ts-expect-error + delete payload.updated_by; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "updated_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ExceptionListSchema & { + extraKey?: string; + } = getExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = exceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.ts new file mode 100644 index 000000000000..e04eadb70765 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.ts @@ -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 * as t from 'io-ts'; + +import { version } from '@kbn/securitysolution-io-ts-types'; +import { _versionOrUndefined } from '../../common/underscore_version'; +import { namespace_type } from '../../common/namespace_type'; +import { metaOrUndefined } from '../../common/meta'; +import { name } from '../../common/name'; +import { created_at } from '../../common/created_at'; +import { created_by } from '../../common/created_by'; +import { id } from '../../common/id'; +import { tie_breaker_id } from '../../common/tie_breaker_id'; +import { immutable } from '../../common/immutable'; +import { updated_at } from '../../common/updated_at'; +import { updated_by } from '../../common/updated_by'; +import { list_id } from '../../common/list_id'; +import { description } from '../../common/description'; +import { osTypeArray } from '../../common/os_type'; +import { exceptionListType } from '../../common/exception_list'; +import { tags } from '../../common/tags'; + +export const exceptionListSchema = t.exact( + t.type({ + _version: _versionOrUndefined, + created_at, + created_by, + description, + id, + immutable, + list_id, + meta: metaOrUndefined, + name, + namespace_type, + os_types: osTypeArray, + tags, + tie_breaker_id, + type: exceptionListType, + updated_at, + updated_by, + version, + }) +); + +export type ExceptionListSchema = t.TypeOf<typeof exceptionListSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.mock.ts new file mode 100644 index 000000000000..0acac1da648d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.mock.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ExceptionListSummarySchema } from '.'; + +export const getListSummaryResponseMock = (): ExceptionListSummarySchema => ({ + windows: 0, + linux: 1, + macos: 2, + total: 3, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.test.ts new file mode 100644 index 000000000000..b4b6de2e25d1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.test.ts @@ -0,0 +1,93 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getListSummaryResponseMock } from './index.mock'; +import { ExceptionListSummarySchema, exceptionListSummarySchema } from '.'; + +describe('list_summary_schema', () => { + test('it should validate a typical list summary response', () => { + const payload = getListSummaryResponseMock(); + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "windows"', () => { + const payload = getListSummaryResponseMock(); + // @ts-expect-error + delete payload.windows; + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "windows"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "linux"', () => { + const payload = getListSummaryResponseMock(); + // @ts-expect-error + delete payload.linux; + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "linux"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "macos"', () => { + const payload = getListSummaryResponseMock(); + // @ts-expect-error + delete payload.macos; + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "macos"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "total"', () => { + const payload = getListSummaryResponseMock(); + // @ts-expect-error + delete payload.total; + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "total"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ExceptionListSummarySchema & { + extraKey?: string; + } = getListSummaryResponseMock(); + payload.extraKey = 'some new value'; + const decoded = exceptionListSummarySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts new file mode 100644 index 000000000000..7414ca257fbd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_summary_schema/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PositiveInteger } from '@kbn/securitysolution-io-ts-types'; +import * as t from 'io-ts'; + +export const exceptionListSummarySchema = t.exact( + t.type({ + windows: PositiveInteger, + linux: PositiveInteger, + macos: PositiveInteger, + total: PositiveInteger, + }) +); + +export type ExceptionListSummarySchema = t.TypeOf<typeof exceptionListSummarySchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.mock.ts new file mode 100644 index 000000000000..75330f493743 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.mock.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 { FoundAllListItemsSchema } from '.'; +import { getListItemResponseMock } from '../list_item_schema/index.mock'; + +export const getFoundAllListItemsSchemaMock = (): FoundAllListItemsSchema => ({ + data: [getListItemResponseMock()], + total: 1, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.ts new file mode 100644 index 000000000000..c5812499b3e7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_all_list_items_schema/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { listItemSchema } from '../list_item_schema'; +import { total } from '../../common/total'; + +export const foundAllListItemsSchema = t.exact( + t.type({ + data: t.array(listItemSchema), + total, + }) +); + +export type FoundAllListItemsSchema = t.TypeOf<typeof foundAllListItemsSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..6f8592320895 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FoundExceptionListItemSchema } from '.'; +import { getExceptionListItemSchemaMock } from '../exception_list_item_schema/index.mock'; + +export const getFoundExceptionListItemSchemaMock = (): FoundExceptionListItemSchema => ({ + data: [getExceptionListItemSchemaMock()], + page: 1, + per_page: 1, + total: 1, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.test.ts new file mode 100644 index 000000000000..33d26ca32a5b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.test.ts @@ -0,0 +1,152 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getFoundExceptionListItemSchemaMock } from './index.mock'; +import { FoundExceptionListItemSchema, foundExceptionListItemSchema } from '.'; +import { ExceptionListItemSchema } from '../exception_list_item_schema'; +import { getExceptionListItemSchemaMock } from '../exception_list_item_schema/index.mock'; + +describe('found_exception_list_item_schema', () => { + test('it should validate a typical exception list response', () => { + const payload = getFoundExceptionListItemSchemaMock(); + const decoded = foundExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept a malformed exception list item within "data"', () => { + const item: Omit<ExceptionListItemSchema, 'entries'> & { + entries?: string; + } = { ...getExceptionListItemSchemaMock(), entries: 'I should be an array' }; + const payload: Omit<FoundExceptionListItemSchema, 'data'> & { + data?: Array< + Omit<ExceptionListItemSchema, 'entries'> & { + entries?: string; + } + >; + } = { ...getFoundExceptionListItemSchemaMock(), data: [item] }; + const decoded = foundExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "I should be an array" supplied to "data,entries"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept a string for "page"', () => { + const payload: Omit<FoundExceptionListItemSchema, 'page'> & { + page?: string; + } = { ...getFoundExceptionListItemSchemaMock(), page: '1' }; + const decoded = foundExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "page"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept a string for "per_page"', () => { + const payload: Omit<FoundExceptionListItemSchema, 'per_page'> & { + per_page?: string; + } = { ...getFoundExceptionListItemSchemaMock(), per_page: '20' }; + const decoded = foundExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "20" supplied to "per_page"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept a string for "total"', () => { + const payload: Omit<FoundExceptionListItemSchema, 'total'> & { + total?: string; + } = { ...getFoundExceptionListItemSchemaMock(), total: '1' }; + const decoded = foundExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "total"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "page"', () => { + const payload = getFoundExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.page; + const decoded = foundExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "page"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "per_page"', () => { + const payload = getFoundExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.per_page; + const decoded = foundExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "per_page"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "total"', () => { + const payload = getFoundExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.total; + const decoded = foundExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "total"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "data"', () => { + const payload = getFoundExceptionListItemSchemaMock(); + // @ts-expect-error + delete payload.data; + const decoded = foundExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "data"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: FoundExceptionListItemSchema & { + extraKey?: string; + } = getFoundExceptionListItemSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = foundExceptionListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.ts new file mode 100644 index 000000000000..50f57bf42459 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { page } from '../../common/page'; +import { per_page } from '../../common/per_page'; +import { pitId } from '../../common/pit'; +import { total } from '../../common/total'; +import { exceptionListItemSchema } from '../exception_list_item_schema'; + +export const foundExceptionListItemSchema = t.intersection([ + t.exact( + t.type({ + data: t.array(exceptionListItemSchema), + page, + per_page, + total, + }) + ), + t.exact( + t.partial({ + pit: pitId, + }) + ), +]); + +export type FoundExceptionListItemSchema = t.TypeOf<typeof foundExceptionListItemSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.mock.ts new file mode 100644 index 000000000000..8a6d9fe2d2a7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FoundExceptionListSchema } from '.'; +import { getExceptionListSchemaMock } from '../exception_list_schema/index.mock'; + +export const getFoundExceptionListSchemaMock = (): FoundExceptionListSchema => ({ + data: [getExceptionListSchemaMock()], + page: 1, + per_page: 1, + total: 1, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.test.ts new file mode 100644 index 000000000000..18d96785ad2e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.test.ts @@ -0,0 +1,152 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getFoundExceptionListSchemaMock } from './index.mock'; +import { FoundExceptionListSchema, foundExceptionListSchema } from '.'; +import { getExceptionListSchemaMock } from '../exception_list_schema/index.mock'; +import { ExceptionListSchema } from '../exception_list_schema'; + +describe('exception_list_schema', () => { + test('it should validate a typical exception list response', () => { + const payload = getFoundExceptionListSchemaMock(); + const decoded = foundExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept a malformed exception list item within "data"', () => { + const item: Omit<ExceptionListSchema, 'entries'> & { + entries?: string[]; + } = { ...getExceptionListSchemaMock(), entries: ['I should not be here'] }; + const payload: Omit<FoundExceptionListSchema, 'data'> & { + data?: Array< + Omit<ExceptionListSchema, 'entries'> & { + entries?: string[]; + } + >; + } = { ...getFoundExceptionListSchemaMock(), data: [item] }; + const decoded = foundExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'invalid keys "entries,["I should not be here"]"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept a string for "page"', () => { + const payload: Omit<FoundExceptionListSchema, 'page'> & { + page?: string; + } = { ...getFoundExceptionListSchemaMock(), page: '1' }; + const decoded = foundExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "page"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept a string for "per_page"', () => { + const payload: Omit<FoundExceptionListSchema, 'per_page'> & { + per_page?: string; + } = { ...getFoundExceptionListSchemaMock(), per_page: '20' }; + const decoded = foundExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "20" supplied to "per_page"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept a string for "total"', () => { + const payload: Omit<FoundExceptionListSchema, 'total'> & { + total?: string; + } = { ...getFoundExceptionListSchemaMock(), total: '1' }; + const decoded = foundExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "total"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "page"', () => { + const payload = getFoundExceptionListSchemaMock(); + // @ts-expect-error + delete payload.page; + const decoded = foundExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "page"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "per_page"', () => { + const payload = getFoundExceptionListSchemaMock(); + // @ts-expect-error + delete payload.per_page; + const decoded = foundExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "per_page"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "total"', () => { + const payload = getFoundExceptionListSchemaMock(); + // @ts-expect-error + delete payload.total; + const decoded = foundExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "total"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "data"', () => { + const payload = getFoundExceptionListSchemaMock(); + // @ts-expect-error + delete payload.data; + const decoded = foundExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "data"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: FoundExceptionListSchema & { + extraKey?: string; + } = getFoundExceptionListSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = foundExceptionListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.ts new file mode 100644 index 000000000000..cb7867db7f29 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_schema/index.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 * as t from 'io-ts'; + +import { page } from '../../common/page'; +import { per_page } from '../../common/per_page'; +import { pitId } from '../../common/pit'; +import { total } from '../../common/total'; + +import { exceptionListSchema } from '../exception_list_schema'; + +export const foundExceptionListSchema = t.intersection([ + t.exact( + t.type({ + data: t.array(exceptionListSchema), + page, + per_page, + total, + }) + ), + t.exact(t.partial({ pit: pitId })), +]); + +export type FoundExceptionListSchema = t.TypeOf<typeof foundExceptionListSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..bc6c6b63dbea --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FoundListItemSchema } from '.'; +import { getListItemResponseMock } from '../list_item_schema/index.mock'; + +export const getFoundListItemSchemaMock = (): FoundListItemSchema => ({ + cursor: 'WzI1LFsiNmE3NmI2OWQtODBkZi00YWIyLThjM2UtODVmNDY2YjA2YTBlIl1d', + data: [getListItemResponseMock()], + page: 1, + per_page: 25, + total: 1, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.ts new file mode 100644 index 000000000000..86801ed60bca --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { listItemSchema } from '../list_item_schema'; +import { cursor } from '../../common/cursor'; +import { page } from '../../common/page'; +import { per_page } from '../../common/per_page'; +import { total } from '../../common/total'; + +export const foundListItemSchema = t.exact( + t.type({ + cursor, + data: t.array(listItemSchema), + page, + per_page, + total, + }) +); + +export type FoundListItemSchema = t.TypeOf<typeof foundListItemSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.mock.ts new file mode 100644 index 000000000000..07cbb1d03000 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FoundListSchema } from '.'; +import { getListResponseMock } from '../list_schema/index.mock'; + +export const getFoundListSchemaMock = (): FoundListSchema => ({ + cursor: '123', + data: [getListResponseMock()], + page: 1, + per_page: 1, + total: 1, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.ts new file mode 100644 index 000000000000..16922174e749 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +import { listSchema } from '../list_schema'; +import { cursor } from '../../common/cursor'; +import { page } from '../../common/page'; +import { per_page } from '../../common/per_page'; +import { total } from '../../common/total'; + +export const foundListSchema = t.exact( + t.type({ + cursor, + data: t.array(listSchema), + page, + per_page, + total, + }) +); + +export type FoundListSchema = t.TypeOf<typeof foundListSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.mock.ts new file mode 100644 index 000000000000..8d1a421acef2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.mock.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 { FoundListsBySizeSchema } from '.'; +import { getListResponseMock } from '../list_schema/index.mock'; + +export const getFoundListsBySizeSchemaMock = (): FoundListsBySizeSchema => ({ + smallLists: [getListResponseMock()], + largeLists: [getListResponseMock()], +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.ts new file mode 100644 index 000000000000..0271416b5f3c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/found_lists_by_size_schema/index.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 * as t from 'io-ts'; + +import { listSchema } from '../list_schema'; + +export const foundListsBySizeSchema = t.exact( + t.type({ + largeLists: t.array(listSchema), + smallLists: t.array(listSchema), + }) +); + +export type FoundListsBySizeSchema = t.TypeOf<typeof foundListsBySizeSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.mock.ts new file mode 100644 index 000000000000..9e8293b382cc --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.mock.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ImportExceptionsResponseSchema } from '.'; + +export const getImportExceptionsResponseSchemaMock = ( + success = 0, + lists = 0, + items = 0 +): ImportExceptionsResponseSchema => ({ + errors: [], + success: true, + success_count: success, + success_exception_lists: true, + success_count_exception_lists: lists, + success_exception_list_items: true, + success_count_exception_list_items: items, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.test.ts new file mode 100644 index 000000000000..d5d481fe87be --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.test.ts @@ -0,0 +1,128 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { importExceptionsResponseSchema, ImportExceptionsResponseSchema } from '.'; +import { getImportExceptionsResponseSchemaMock } from './index.mock'; + +describe('importExceptionsResponseSchema', () => { + test('it should validate a typical exceptions import response', () => { + const payload = getImportExceptionsResponseSchemaMock(); + const decoded = importExceptionsResponseSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "errors"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = + getImportExceptionsResponseSchemaMock(); + delete payload.errors; + const decoded = importExceptionsResponseSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "errors"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "success"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = + getImportExceptionsResponseSchemaMock(); + delete payload.success; + const decoded = importExceptionsResponseSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "success"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "success_count"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = + getImportExceptionsResponseSchemaMock(); + delete payload.success_count; + const decoded = importExceptionsResponseSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "success_count"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "success_exception_lists"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = + getImportExceptionsResponseSchemaMock(); + delete payload.success_exception_lists; + const decoded = importExceptionsResponseSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "success_exception_lists"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "success_count_exception_lists"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = + getImportExceptionsResponseSchemaMock(); + delete payload.success_count_exception_lists; + const decoded = importExceptionsResponseSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "success_count_exception_lists"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "success_exception_list_items"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = + getImportExceptionsResponseSchemaMock(); + delete payload.success_exception_list_items; + const decoded = importExceptionsResponseSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "success_exception_list_items"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "success_count_exception_list_items"', () => { + const payload: Partial<ReturnType<typeof getImportExceptionsResponseSchemaMock>> = + getImportExceptionsResponseSchemaMock(); + delete payload.success_count_exception_list_items; + const decoded = importExceptionsResponseSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "success_count_exception_list_items"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ImportExceptionsResponseSchema & { + extraKey?: string; + } = getImportExceptionsResponseSchemaMock(); + payload.extraKey = 'some new value'; + const decoded = importExceptionsResponseSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.ts new file mode 100644 index 000000000000..4a172d2ec10a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/import_exceptions_schema/index.ts @@ -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 * as t from 'io-ts'; + +import { PositiveInteger } from '@kbn/securitysolution-io-ts-types'; + +import { id } from '../../common/id'; +import { list_id } from '../../common/list_id'; +import { item_id } from '../../common/item_id'; + +export const bulkErrorErrorSchema = t.exact( + t.type({ + status_code: t.number, + message: t.string, + }) +); + +export const bulkErrorSchema = t.intersection([ + t.exact( + t.type({ + error: bulkErrorErrorSchema, + }) + ), + t.partial({ + id, + list_id, + item_id, + }), +]); + +export type BulkErrorSchema = t.TypeOf<typeof bulkErrorSchema>; + +export const importExceptionsResponseSchema = t.exact( + t.type({ + errors: t.array(bulkErrorSchema), + success: t.boolean, + success_count: PositiveInteger, + success_exception_lists: t.boolean, + success_count_exception_lists: PositiveInteger, + success_exception_list_items: t.boolean, + success_count_exception_list_items: PositiveInteger, + }) +); + +export type ImportExceptionsResponseSchema = t.TypeOf<typeof importExceptionsResponseSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/index.ts new file mode 100644 index 000000000000..04b00f43c702 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/index.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. + */ + +export * from './acknowledge_schema'; +export * from './create_endpoint_list_schema'; +export * from './exception_list_schema'; +export * from './exception_list_item_schema'; +export * from './found_exception_list_item_schema'; +export * from './found_exception_list_schema'; +export * from './found_all_list_items_schema'; +export * from './found_lists_by_size_schema'; +export * from './found_list_item_schema'; +export * from './found_list_schema'; +export * from './import_exceptions_schema'; +export * from './list_item_schema'; +export * from './list_schema'; +export * from './exception_list_summary_schema'; +export * from './list_item_index_exist_schema'; +export * from './search_list_item_schema'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.mock.ts new file mode 100644 index 000000000000..b3416ab76852 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.mock.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. + */ + +import { ListItemIndexExistSchema } from '.'; + +export const getListItemIndexExistSchemaResponseMock = (): ListItemIndexExistSchema => ({ + list_index: true, + list_item_index: true, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.test.ts new file mode 100644 index 000000000000..0c8812795935 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.test.ts @@ -0,0 +1,65 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getListItemIndexExistSchemaResponseMock } from './index.mock'; +import { ListItemIndexExistSchema, listItemIndexExistSchema } from '.'; + +describe('list_item_index_exist_schema', () => { + test('it should validate a typical list item request', () => { + const payload = getListItemIndexExistSchemaResponseMock(); + const decoded = listItemIndexExistSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "list_index"', () => { + const payload = getListItemIndexExistSchemaResponseMock(); + // @ts-expect-error + delete payload.list_index; + const decoded = listItemIndexExistSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_index"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "list_item_index"', () => { + const payload = getListItemIndexExistSchemaResponseMock(); + // @ts-expect-error + delete payload.list_item_index; + const decoded = listItemIndexExistSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_item_index"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ListItemIndexExistSchema & { + extraKey?: string; + } = getListItemIndexExistSchemaResponseMock(); + payload.extraKey = 'some new value'; + const decoded = listItemIndexExistSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.ts new file mode 100644 index 000000000000..fb1070f72dae --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_index_exist_schema/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const listItemIndexExistSchema = t.exact( + t.type({ + list_index: t.boolean, + list_item_index: t.boolean, + }) +); + +export type ListItemIndexExistSchema = t.TypeOf<typeof listItemIndexExistSchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.mock.ts new file mode 100644 index 000000000000..10283965276d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.mock.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 { ListItemSchema } from '.'; +import { + DATE_NOW, + ELASTIC_USER, + LIST_ID, + LIST_ITEM_ID, + META, + TIE_BREAKER, + TYPE, + USER, + VALUE, +} from '../../constants/index.mock'; + +export const getListItemResponseMock = (): ListItemSchema => ({ + _version: undefined, + '@timestamp': DATE_NOW, + created_at: DATE_NOW, + created_by: USER, + deserializer: undefined, + id: LIST_ITEM_ID, + list_id: LIST_ID, + meta: META, + serializer: undefined, + tie_breaker_id: TIE_BREAKER, + type: TYPE, + updated_at: DATE_NOW, + updated_by: USER, + value: VALUE, +}); + +/** + * This is useful for end to end tests where we remove the auto generated parts for comparisons + * such as created_at, updated_at, and id. + */ +export const getListItemResponseMockWithoutAutoGeneratedValues = (): Partial<ListItemSchema> => ({ + created_by: ELASTIC_USER, + list_id: LIST_ID, + type: TYPE, + updated_by: ELASTIC_USER, + value: VALUE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.test.ts new file mode 100644 index 000000000000..f855448ff26e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.test.ts @@ -0,0 +1,192 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getListItemResponseMock } from './index.mock'; +import { ListItemSchema, listItemSchema } from '.'; + +describe('list_item_schema', () => { + test('it should validate a typical list item response', () => { + const payload = getListItemResponseMock(); + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "id"', () => { + const payload = getListItemResponseMock(); + // @ts-expect-error + delete payload.id; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "list_id"', () => { + const payload = getListItemResponseMock(); + // @ts-expect-error + delete payload.list_id; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "meta"', () => { + const payload = getListItemResponseMock(); + delete payload.meta; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "serializer"', () => { + const payload = getListItemResponseMock(); + delete payload.serializer; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "deserializer"', () => { + const payload = getListItemResponseMock(); + delete payload.deserializer; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "created_at"', () => { + const payload = getListItemResponseMock(); + // @ts-expect-error + delete payload.created_at; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "created_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "created_by"', () => { + const payload = getListItemResponseMock(); + // @ts-expect-error + delete payload.created_by; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "created_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "tie_breaker_id"', () => { + const payload = getListItemResponseMock(); + // @ts-expect-error + delete payload.tie_breaker_id; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "tie_breaker_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "type"', () => { + const payload = getListItemResponseMock(); + // @ts-expect-error + delete payload.type; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "updated_at"', () => { + const payload = getListItemResponseMock(); + // @ts-expect-error + delete payload.updated_at; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "updated_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "updated_by"', () => { + const payload = getListItemResponseMock(); + // @ts-expect-error + delete payload.updated_by; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "updated_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "value"', () => { + const payload = getListItemResponseMock(); + // @ts-expect-error + delete payload.value; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "value"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ListItemSchema & { extraKey?: string } = getListItemResponseMock(); + payload.extraKey = 'some new value'; + const decoded = listItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.ts new file mode 100644 index 000000000000..0e7af6ab2726 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.ts @@ -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 * as t from 'io-ts'; + +import { _versionOrUndefined } from '../../common/underscore_version'; +import { deserializerOrUndefined } from '../../common/deserializer'; +import { metaOrUndefined } from '../../common/meta'; +import { timestampOrUndefined } from '../../common/timestamp'; +import { serializerOrUndefined } from '../../common/serializer'; +import { created_at } from '../../common/created_at'; +import { created_by } from '../../common/created_by'; +import { id } from '../../common/id'; +import { tie_breaker_id } from '../../common/tie_breaker_id'; +import { type } from '../../common/type'; +import { updated_at } from '../../common/updated_at'; +import { updated_by } from '../../common/updated_by'; +import { list_id } from '../../common/list_id'; +import { value } from '../../common/value'; + +export const listItemSchema = t.exact( + t.type({ + _version: _versionOrUndefined, + '@timestamp': timestampOrUndefined, + created_at, + created_by, + deserializer: deserializerOrUndefined, + id, + list_id, + meta: metaOrUndefined, + serializer: serializerOrUndefined, + tie_breaker_id, + type, + updated_at, + updated_by, + value, + }) +); + +export type ListItemSchema = t.TypeOf<typeof listItemSchema>; + +export const listItemArraySchema = t.array(listItemSchema); +export type ListItemArraySchema = t.TypeOf<typeof listItemArraySchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.mock.ts new file mode 100644 index 000000000000..3b6ff22ceb15 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.mock.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ListSchema } from '.'; +import { + DATE_NOW, + DESCRIPTION, + ELASTIC_USER, + IMMUTABLE, + LIST_ID, + META, + NAME, + TIE_BREAKER, + TYPE, + USER, + VERSION, +} from '../../constants/index.mock'; + +export const getListResponseMock = (): ListSchema => ({ + _version: undefined, + '@timestamp': DATE_NOW, + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, + deserializer: undefined, + id: LIST_ID, + immutable: IMMUTABLE, + meta: META, + name: NAME, + serializer: undefined, + tie_breaker_id: TIE_BREAKER, + type: TYPE, + updated_at: DATE_NOW, + updated_by: USER, + version: VERSION, +}); + +/** + * This is useful for end to end tests where we remove the auto generated parts for comparisons + * such as created_at, updated_at, and id. + */ +export const getListResponseMockWithoutAutoGeneratedValues = (): Partial<ListSchema> => ({ + created_by: ELASTIC_USER, + description: DESCRIPTION, + immutable: IMMUTABLE, + name: NAME, + type: TYPE, + updated_by: ELASTIC_USER, + version: VERSION, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.test.ts new file mode 100644 index 000000000000..920941a2135a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.test.ts @@ -0,0 +1,192 @@ +/* + * 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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getListResponseMock } from './index.mock'; +import { ListSchema, listSchema } from '.'; + +describe('list_schema', () => { + test('it should validate a typical list response', () => { + const payload = getListResponseMock(); + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "id"', () => { + const payload = getListResponseMock(); + // @ts-expect-error + delete payload.id; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should accept an undefined for "meta"', () => { + const payload = getListResponseMock(); + delete payload.meta; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "serializer"', () => { + const payload = getListResponseMock(); + delete payload.serializer; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should accept an undefined for "deserializer"', () => { + const payload = getListResponseMock(); + delete payload.deserializer; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for "created_at"', () => { + const payload = getListResponseMock(); + // @ts-expect-error + delete payload.created_at; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "created_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "created_by"', () => { + const payload = getListResponseMock(); + // @ts-expect-error + delete payload.created_by; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "created_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "tie_breaker_id"', () => { + const payload = getListResponseMock(); + // @ts-expect-error + delete payload.tie_breaker_id; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "tie_breaker_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "type"', () => { + const payload = getListResponseMock(); + // @ts-expect-error + delete payload.type; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "type"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "updated_at"', () => { + const payload = getListResponseMock(); + // @ts-expect-error + delete payload.updated_at; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "updated_at"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "updated_by"', () => { + const payload = getListResponseMock(); + // @ts-expect-error + delete payload.updated_by; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "updated_by"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "name"', () => { + const payload = getListResponseMock(); + // @ts-expect-error + delete payload.name; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "name"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should NOT accept an undefined for "description"', () => { + const payload = getListResponseMock(); + // @ts-expect-error + delete payload.description; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "description"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ListSchema & { extraKey?: string } = getListResponseMock(); + payload.extraKey = 'some new value'; + const decoded = listSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.ts new file mode 100644 index 000000000000..687c93ad6f6d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.ts @@ -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 * as t from 'io-ts'; +import { version } from '@kbn/securitysolution-io-ts-types'; +import { _versionOrUndefined } from '../../common/underscore_version'; +import { deserializerOrUndefined } from '../../common/deserializer'; +import { metaOrUndefined } from '../../common/meta'; +import { serializerOrUndefined } from '../../common/serializer'; +import { created_at } from '../../common/created_at'; +import { timestampOrUndefined } from '../../common/timestamp'; +import { created_by } from '../../common/created_by'; +import { description } from '../../common/description'; +import { id } from '../../common/id'; +import { immutable } from '../../common/immutable'; +import { name } from '../../common/name'; +import { tie_breaker_id } from '../../common/tie_breaker_id'; +import { type } from '../../common/type'; +import { updated_at } from '../../common/updated_at'; +import { updated_by } from '../../common/updated_by'; + +export const listSchema = t.exact( + t.type({ + _version: _versionOrUndefined, + '@timestamp': timestampOrUndefined, + created_at, + created_by, + description, + deserializer: deserializerOrUndefined, + id, + immutable, + meta: metaOrUndefined, + name, + serializer: serializerOrUndefined, + tie_breaker_id, + type, + updated_at, + updated_by, + version, + }) +); + +export type ListSchema = t.TypeOf<typeof listSchema>; + +export const listArraySchema = t.array(listSchema); +export type ListArraySchema = t.TypeOf<typeof listArraySchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.mock.ts new file mode 100644 index 000000000000..1ce8e23d4af5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.mock.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SearchListItemSchema } from '.'; +import { VALUE } from '../../constants/index.mock'; +import { getListItemResponseMock } from '../list_item_schema/index.mock'; + +export const getSearchListItemResponseMock = (): SearchListItemSchema => ({ + items: [getListItemResponseMock()], + value: VALUE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.test.ts new file mode 100644 index 000000000000..6f89a58e0bc3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.test.ts @@ -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 { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; + +import { getSearchListItemResponseMock } from './index.mock'; +import { SearchListItemSchema, searchListItemSchema } from '.'; + +describe('search_list_item_schema', () => { + test('it should validate a typical search list item response', () => { + const payload = getSearchListItemResponseMock(); + const decoded = searchListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT validate with an "undefined" for "items"', () => { + const { items, ...noItems } = getSearchListItemResponseMock(); + const decoded = searchListItemSchema.decode(noItems); + const checked = exactCheck(noItems, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "items"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: SearchListItemSchema & { extraKey?: string } = getSearchListItemResponseMock(); + payload.extraKey = 'some new value'; + const decoded = searchListItemSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.ts new file mode 100644 index 000000000000..8c17db076500 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { listItemArraySchema } from '../list_item_schema'; + +/** + * NOTE: Although this is defined within "response" this does not expose a REST API + * endpoint right now for this particular response. Instead this is only used internally + * for the plugins at this moment. If this changes, please remove this message. + */ +export const searchListItemSchema = t.exact( + t.type({ + items: listItemArraySchema, + value: t.unknown, + }) +); + +export type SearchListItemSchema = t.TypeOf<typeof searchListItemSchema>; + +export const searchListItemArraySchema = t.array(searchListItemSchema); +export type SearchListItemArraySchema = t.TypeOf<typeof searchListItemArraySchema>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts new file mode 100644 index 000000000000..8b172753bbc5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts @@ -0,0 +1,242 @@ +/* + * 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 { HttpStart } from '@kbn/core-http-browser'; +import type { NotificationsStart } from '@kbn/core-notifications-browser'; +import type { Filter } from '@kbn/es-query'; +import { NamespaceType } from '../common/default_namespace'; +import { ExceptionListType, ExceptionListTypeEnum } from '../common/exception_list'; +import { Page } from '../common/page'; +import { PerPage } from '../common/per_page'; +import { TotalOrUndefined } from '../common/total'; +import { CreateExceptionListItemSchema } from '../request/create_exception_list_item_schema'; +import { CreateExceptionListSchema } from '../request/create_exception_list_schema'; +import { ExceptionListId } from '../request/get_exception_filter_schema'; +import { UpdateExceptionListItemSchema } from '../request/update_exception_list_item_schema'; +import { UpdateExceptionListSchema } from '../request/update_exception_list_schema'; +import { ExceptionListItemSchema } from '../response/exception_list_item_schema'; +import { ExceptionListSchema } from '../response/exception_list_schema'; + +interface BaseParams { + http: HttpStart; + signal: AbortSignal; +} + +export interface DuplicateExceptionListProps extends BaseParams { + listId: string; + namespaceType: NamespaceType; + includeExpiredExceptions: boolean; +} + +export interface ApiListDuplicateProps + extends Omit<DuplicateExceptionListProps, 'http' | 'signal'> { + onError: (err: Error) => void; + onSuccess: (newList: ExceptionListSchema) => void; +} + +export interface ExceptionListFilter { + name?: string | null; + list_id?: string | null; + created_by?: string | null; + types?: ExceptionListTypeEnum[] | null; + tags?: string | null; +} + +export interface UseExceptionListsProps { + errorMessage: string; + filterOptions?: ExceptionListFilter; + http: HttpStart; + namespaceTypes: NamespaceType[]; + notifications: NotificationsStart; + initialPagination?: Pagination; + hideLists?: readonly string[]; + initialSort?: Sort; +} + +export interface UseExceptionListProps { + http: HttpStart; + lists: ExceptionListIdentifiers[]; + onError?: (arg: string[]) => void; + filterOptions: FilterExceptionsOptions[]; + pagination?: Pagination; + showDetectionsListsOnly: boolean; + showEndpointListsOnly: boolean; + matchFilters: boolean; + onSuccess?: (arg: UseExceptionListItemsSuccess) => void; + sort?: Sort; +} + +export interface FilterExceptionsOptions { + filter: string; + tags: string[]; +} + +export interface ApiCallMemoProps { + id: string; + namespaceType: NamespaceType; + onError: (arg: Error) => void; + onSuccess: () => void; +} + +// TODO: Switch to use ApiCallMemoProps +// after cleaning up exceptions/api file to +// remove unnecessary validation checks +export interface ApiListExportProps { + id: string; + includeExpiredExceptions: boolean; + listId: string; + namespaceType: NamespaceType; + onError: (err: Error) => void; + onSuccess: (blob: Blob) => void; +} + +export interface Sort { + field: string; + order: string; +} +export interface Pagination { + page: Page; + perPage: PerPage; + total: TotalOrUndefined; +} + +export interface UseExceptionListItemsSuccess { + exceptions: ExceptionListItemSchema[]; + pagination: Pagination; +} + +export interface ExceptionListIdentifiers { + id: string; + listId: string; + namespaceType: NamespaceType; + type: ExceptionListType; +} + +export interface ApiCallFindListsItemsMemoProps { + lists: ExceptionListIdentifiers[]; + pagination: Partial<Pagination>; + showDetectionsListsOnly: boolean; + showEndpointListsOnly: boolean; + filter?: string; + onError: (arg: string[]) => void; + onSuccess: (arg: UseExceptionListItemsSuccess) => void; +} + +export interface ApiCallGetExceptionFilterFromIdsMemoProps extends GetExceptionFilterOptionalProps { + exceptionListIds: ExceptionListId[]; + onError: (arg: string[]) => void; + onSuccess: (arg: Filter) => void; +} + +export interface ApiCallGetExceptionFilterFromExceptionsMemoProps + extends GetExceptionFilterOptionalProps { + exceptions: Array<ExceptionListItemSchema | CreateExceptionListItemSchema>; + onError: (arg: string[]) => void; + onSuccess: (arg: Filter) => void; +} + +export interface ExportExceptionListProps { + http: HttpStart; + id: string; + listId: string; + namespaceType: NamespaceType; + includeExpiredExceptions: boolean; + signal: AbortSignal; +} + +export interface AddEndpointExceptionListProps { + http: HttpStart; + signal: AbortSignal; +} + +export interface UpdateExceptionListItemProps { + http: HttpStart; + listItem: UpdateExceptionListItemSchema; + signal: AbortSignal; +} + +export interface UpdateExceptionListProps { + http: HttpStart; + list: UpdateExceptionListSchema; + signal: AbortSignal; +} + +export interface AddExceptionListItemProps { + http: HttpStart; + listItem: CreateExceptionListItemSchema; + signal: AbortSignal; +} + +export interface AddExceptionListProps { + http: HttpStart; + list: CreateExceptionListSchema; + signal: AbortSignal; +} + +export interface UseExceptionListsSuccess { + exceptions: ExceptionListSchema[]; + pagination: Pagination; +} + +export interface ApiCallFetchExceptionListsProps { + http: HttpStart; + namespaceTypes: string; + pagination: Partial<Pagination>; + sort?: Sort; + filters: string; + signal: AbortSignal; +} + +export interface ApiCallByIdProps { + http: HttpStart; + id: string; + namespaceType: NamespaceType; + signal: AbortSignal; +} + +export interface ApiCallByListIdProps { + http: HttpStart; + listIds: string[]; + namespaceTypes: NamespaceType[]; + pagination: Partial<Pagination>; + search?: string; + filter?: string; + signal: AbortSignal; +} + +export type AddExceptionList = UpdateExceptionListSchema | CreateExceptionListSchema; + +export interface PersistHookProps { + http: HttpStart; + onError: (arg: Error) => void; +} + +export interface ExceptionList extends ExceptionListSchema { + totalItems: number; +} + +export interface GetExceptionFilterOptionalProps { + signal?: AbortSignal; + chunkSize?: number; + alias?: string; + excludeExceptions?: boolean; +} + +export interface GetExceptionFilterFromExceptionListIdsProps + extends GetExceptionFilterOptionalProps { + http: HttpStart; + exceptionListIds: ExceptionListId[]; +} + +export interface GetExceptionFilterFromExceptionsProps extends GetExceptionFilterOptionalProps { + http: HttpStart; + exceptions: Array<ExceptionListItemSchema | CreateExceptionListItemSchema>; +} + +export interface ExceptionFilterResponse { + filter: Filter; +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json new file mode 100644 index 000000000000..b8097b0b7b12 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": ["jest", "node"] + }, + "include": ["**/*.ts"], + "kbn_references": [ + "@kbn/securitysolution-io-ts-types", + "@kbn/securitysolution-io-ts-utils", + "@kbn/securitysolution-list-constants", + "@kbn/es-query", + "@kbn/core-http-browser", + "@kbn/core-notifications-browser", + "@kbn/securitysolution-exceptions-common" + ], + "exclude": ["target/**/*"] +} diff --git a/packages/kbn-securitysolution-list-api/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/README.md similarity index 100% rename from packages/kbn-securitysolution-list-api/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-list-api/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/index.ts new file mode 100644 index 000000000000..afe1280b9b85 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/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 * from './src/api'; +export * from './src/fp_utils'; +export * from './src/list_api'; +export * from './src/list_item_api'; +export * from './src/types'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/jest.config.js b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/jest.config.js new file mode 100644 index 000000000000..9e9844e5cbd0 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-list-api'], +}; diff --git a/packages/kbn-securitysolution-list-api/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-list-api/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-list-api/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/package.json new file mode 100644 index 000000000000..5159b04f6204 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/securitysolution-list-api", + "version": "1.0.0", + "description": "security solution list REST API", + "license": "Elastic License 2.0", + "private": true +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/api/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/api/index.ts new file mode 100644 index 000000000000..c8fe4143d1bd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/api/index.ts @@ -0,0 +1,664 @@ +/* + * 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 { chain, fromEither, tryCatch } from 'fp-ts/lib/TaskEither'; +import { flow } from 'fp-ts/lib/function'; +import { validateEither } from '@kbn/securitysolution-io-ts-utils'; +import { + CreateEndpointListSchema, + ExceptionListItemSchema, + ExceptionListSchema, + FoundExceptionListItemSchema, + FoundExceptionListSchema, + createEndpointListSchema, + exceptionListItemSchema, + exceptionListSchema, + foundExceptionListItemSchema, + foundExceptionListSchema, + AddEndpointExceptionListProps, + AddExceptionListItemProps, + AddExceptionListProps, + ApiCallByIdProps, + ApiCallByListIdProps, + ApiCallFetchExceptionListsProps, + ExportExceptionListProps, + UpdateExceptionListItemProps, + UpdateExceptionListProps, + GetExceptionFilterFromExceptionListIdsProps, + GetExceptionFilterFromExceptionsProps, + ExceptionFilterResponse, + DuplicateExceptionListProps, +} from '@kbn/securitysolution-io-ts-list-types'; + +import { + ENDPOINT_LIST_URL, + INTERNAL_EXCEPTION_FILTER, + EXCEPTION_LIST_ITEM_URL, + EXCEPTION_LIST_URL, +} from '@kbn/securitysolution-list-constants'; +import { toError, toPromise } from '../fp_utils'; + +const version = '2023-10-31'; + +/** + * Add new ExceptionList + * + * @param http Kibana http service + * @param list exception list to add + * @param signal to cancel request + * + * @throws An error if response is not OK + * + */ +const addExceptionList = async ({ + http, + list, + signal, +}: AddExceptionListProps): Promise<ExceptionListSchema> => + http.fetch<ExceptionListSchema>(EXCEPTION_LIST_URL, { + body: JSON.stringify(list), + method: 'POST', + signal, + version, + }); + +const addExceptionListWithValidation = async ({ + http, + list, + signal, +}: AddExceptionListProps): Promise<ExceptionListSchema> => + flow( + () => + tryCatch( + () => + addExceptionList({ + http, + list, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(exceptionListSchema, response))), + flow(toPromise) + )(); + +export { addExceptionListWithValidation as addExceptionList }; + +/** + * Add new ExceptionListItem + * + * @param http Kibana http service + * @param listItem exception list item to add + * @param signal to cancel request + * + * @throws An error if response is not OK + * + */ +const addExceptionListItem = async ({ + http, + listItem, + signal, +}: AddExceptionListItemProps): Promise<ExceptionListItemSchema> => + http.fetch<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, { + body: JSON.stringify(listItem), + method: 'POST', + signal, + version, + }); + +const addExceptionListItemWithValidation = async ({ + http, + listItem, + signal, +}: AddExceptionListItemProps): Promise<ExceptionListItemSchema> => + flow( + () => + tryCatch( + () => + addExceptionListItem({ + http, + listItem, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), + flow(toPromise) + )(); + +export { addExceptionListItemWithValidation as addExceptionListItem }; + +/** + * Update existing ExceptionList + * + * @param http Kibana http service + * @param list exception list to add + * @param signal to cancel request + * + * @throws An error if response is not OK + * + */ +const updateExceptionList = async ({ + http, + list, + signal, +}: UpdateExceptionListProps): Promise<ExceptionListSchema> => + http.fetch<ExceptionListSchema>(EXCEPTION_LIST_URL, { + body: JSON.stringify(list), + method: 'PUT', + signal, + version, + }); + +const updateExceptionListWithValidation = async ({ + http, + list, + signal, +}: UpdateExceptionListProps): Promise<ExceptionListSchema> => + flow( + () => + tryCatch( + () => + updateExceptionList({ + http, + list, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(exceptionListSchema, response))), + flow(toPromise) + )(); + +export { updateExceptionListWithValidation as updateExceptionList }; + +/** + * Update existing ExceptionListItem + * + * @param http Kibana http service + * @param listItem exception list item to add + * @param signal to cancel request + * + * @throws An error if response is not OK + * + */ +const updateExceptionListItem = async ({ + http, + listItem, + signal, +}: UpdateExceptionListItemProps): Promise<ExceptionListItemSchema> => + http.fetch<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, { + body: JSON.stringify(listItem), + method: 'PUT', + signal, + version, + }); + +const updateExceptionListItemWithValidation = async ({ + http, + listItem, + signal, +}: UpdateExceptionListItemProps): Promise<ExceptionListItemSchema> => + flow( + () => + tryCatch( + () => + updateExceptionListItem({ + http, + listItem, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), + flow(toPromise) + )(); + +export { updateExceptionListItemWithValidation as updateExceptionListItem }; + +/** + * Fetch all ExceptionLists (optionally by namespaceType) + * + * @param http Kibana http service + * @param namespaceTypes ExceptionList namespace_types of lists to find + * @param filters search bar filters + * @param pagination optional + * @param signal to cancel request + * + * @throws An error if request params or response is not OK + */ +const fetchExceptionLists = async ({ + http, + filters, + namespaceTypes, + pagination, + signal, + sort, +}: ApiCallFetchExceptionListsProps): Promise<FoundExceptionListSchema> => { + const query = { + filter: filters || undefined, + namespace_type: namespaceTypes, + page: pagination.page ? `${pagination.page}` : '1', + per_page: pagination.perPage ? `${pagination.perPage}` : '20', + sort_field: sort?.field ? sort?.field : 'exception-list.created_at', + sort_order: sort?.order ? sort?.order : 'desc', + }; + + return http.fetch<FoundExceptionListSchema>(`${EXCEPTION_LIST_URL}/_find`, { + method: 'GET', + query, + signal, + version, + }); +}; + +const fetchExceptionListsWithValidation = async ({ + filters, + http, + namespaceTypes, + pagination, + signal, + sort, +}: ApiCallFetchExceptionListsProps): Promise<FoundExceptionListSchema> => + flow( + () => + tryCatch( + () => + fetchExceptionLists({ + filters, + http, + namespaceTypes, + pagination, + signal, + sort, + }), + toError + ), + chain((response) => fromEither(validateEither(foundExceptionListSchema, response))), + flow(toPromise) + )(); + +export { fetchExceptionListsWithValidation as fetchExceptionLists }; + +/** + * Fetch an ExceptionList by providing a ExceptionList ID + * + * @param http Kibana http service + * @param id ExceptionList ID (not list_id) + * @param namespaceType ExceptionList namespace_type + * @param signal to cancel request + * + * @throws An error if response is not OK + */ +const fetchExceptionListById = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise<ExceptionListSchema> => + http.fetch<ExceptionListSchema>(EXCEPTION_LIST_URL, { + method: 'GET', + query: { id, namespace_type: namespaceType }, + signal, + version, + }); + +const fetchExceptionListByIdWithValidation = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise<ExceptionListSchema> => + flow( + () => + tryCatch( + () => + fetchExceptionListById({ + http, + id, + namespaceType, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(exceptionListSchema, response))), + flow(toPromise) + )(); + +export { fetchExceptionListByIdWithValidation as fetchExceptionListById }; + +/** + * Fetch an ExceptionList's ExceptionItems by providing a ExceptionList list_id + * + * @param http Kibana http service + * @param listIds ExceptionList list_ids (not ID) + * @param namespaceTypes ExceptionList namespace_types + * @param search optional - simple search string + * @param filter optional + * @param pagination optional + * @param signal to cancel request + * + * @throws An error if response is not OK + */ +const fetchExceptionListsItemsByListIds = async ({ + http, + listIds, + namespaceTypes, + filter, + pagination, + search, + signal, +}: ApiCallByListIdProps): Promise<FoundExceptionListItemSchema> => { + const query = { + list_id: listIds.join(','), + namespace_type: namespaceTypes.join(','), + page: pagination.page ? `${pagination.page}` : '1', + per_page: pagination.perPage ? `${pagination.perPage}` : '20', + search, + sort_field: 'exception-list.created_at', + sort_order: 'desc', + filter, + }; + + return http.fetch<FoundExceptionListItemSchema>(`${EXCEPTION_LIST_ITEM_URL}/_find`, { + method: 'GET', + query, + signal, + version, + }); +}; + +const fetchExceptionListsItemsByListIdsWithValidation = async ({ + filter, + http, + listIds, + namespaceTypes, + pagination, + search, + signal, +}: ApiCallByListIdProps): Promise<FoundExceptionListItemSchema> => + flow( + () => + tryCatch( + () => + fetchExceptionListsItemsByListIds({ + filter, + http, + listIds, + namespaceTypes, + pagination, + search, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(foundExceptionListItemSchema, response))), + flow(toPromise) + )(); + +export { fetchExceptionListsItemsByListIdsWithValidation as fetchExceptionListsItemsByListIds }; + +/** + * Fetch an ExceptionListItem by providing a ExceptionListItem ID + * + * @param http Kibana http service + * @param id ExceptionListItem ID (not item_id) + * @param namespaceType ExceptionList namespace_type + * @param signal to cancel request + * + * @throws An error if response is not OK + */ +const fetchExceptionListItemById = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise<ExceptionListItemSchema> => + http.fetch<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, { + method: 'GET', + query: { id, namespace_type: namespaceType }, + signal, + version, + }); + +const fetchExceptionListItemByIdWithValidation = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise<ExceptionListItemSchema> => + flow( + () => tryCatch(() => fetchExceptionListItemById({ http, id, namespaceType, signal }), toError), + chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), + flow(toPromise) + )(); + +export { fetchExceptionListItemByIdWithValidation as fetchExceptionListItemById }; + +/** + * Delete an ExceptionList by providing a ExceptionList ID + * + * @param http Kibana http service + * @param id ExceptionList ID (not list_id) + * @param namespaceType ExceptionList namespace_type + * @param signal to cancel request + * + * @throws An error if response is not OK + */ +const deleteExceptionListById = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise<ExceptionListSchema> => + http.fetch<ExceptionListSchema>(EXCEPTION_LIST_URL, { + method: 'DELETE', + query: { id, namespace_type: namespaceType }, + signal, + version, + }); + +const deleteExceptionListByIdWithValidation = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise<ExceptionListSchema> => + flow( + () => tryCatch(() => deleteExceptionListById({ http, id, namespaceType, signal }), toError), + chain((response) => fromEither(validateEither(exceptionListSchema, response))), + flow(toPromise) + )(); + +export { deleteExceptionListByIdWithValidation as deleteExceptionListById }; + +/** + * Delete an ExceptionListItem by providing a ExceptionListItem ID + * + * @param http Kibana http service + * @param id ExceptionListItem ID (not item_id) + * @param namespaceType ExceptionList namespace_type + * @param signal to cancel request + * + * @throws An error if response is not OK + */ +const deleteExceptionListItemById = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise<ExceptionListItemSchema> => + http.fetch<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, { + method: 'DELETE', + query: { id, namespace_type: namespaceType }, + signal, + version, + }); + +const deleteExceptionListItemByIdWithValidation = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise<ExceptionListItemSchema> => + flow( + () => tryCatch(() => deleteExceptionListItemById({ http, id, namespaceType, signal }), toError), + chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), + flow(toPromise) + )(); + +export { deleteExceptionListItemByIdWithValidation as deleteExceptionListItemById }; + +/** + * Add new Endpoint ExceptionList + * + * @param http Kibana http service + * @param signal to cancel request + * + * @throws An error if response is not OK + * + */ +const addEndpointExceptionList = async ({ + http, + signal, +}: AddEndpointExceptionListProps): Promise<CreateEndpointListSchema> => + http.fetch<ExceptionListItemSchema>(ENDPOINT_LIST_URL, { + method: 'POST', + signal, + version, + }); + +const addEndpointExceptionListWithValidation = async ({ + http, + signal, +}: AddEndpointExceptionListProps): Promise<CreateEndpointListSchema> => + flow( + () => tryCatch(() => addEndpointExceptionList({ http, signal }), toError), + chain((response) => fromEither(validateEither(createEndpointListSchema, response))), + flow(toPromise) + )(); + +export { addEndpointExceptionListWithValidation as addEndpointExceptionList }; + +/** + * Export an ExceptionList by providing a ExceptionList ID + * + * @param http Kibana http service + * @param id ExceptionList ID (not list_id) + * @param includeExpiredExceptions boolean for including expired exceptions + * @param listId ExceptionList LIST_ID (not id) + * @param namespaceType ExceptionList namespace_type + * @param signal to cancel request + * + * @throws An error if response is not OK + */ +export const exportExceptionList = async ({ + http, + id, + includeExpiredExceptions, + listId, + namespaceType, + signal, +}: ExportExceptionListProps): Promise<Blob> => + http.fetch<Blob>(`${EXCEPTION_LIST_URL}/_export`, { + method: 'POST', + query: { + id, + list_id: listId, + namespace_type: namespaceType, + include_expired_exceptions: includeExpiredExceptions, + }, + signal, + version, + }); + +/** + * Create a Filter query from an exception list id + * + * @param exceptionListId The id of the exception list from which create a Filter query + * @param signal AbortSignal for cancelling request + * + * @throws An error if response is not OK + */ +export const getExceptionFilterFromExceptionListIds = async ({ + alias, + chunkSize, + exceptionListIds, + excludeExceptions, + http, + signal, +}: GetExceptionFilterFromExceptionListIdsProps): Promise<ExceptionFilterResponse> => + http.fetch(INTERNAL_EXCEPTION_FILTER, { + method: 'POST', + version: '1', + body: JSON.stringify({ + exception_list_ids: exceptionListIds, + type: 'exception_list_ids', + alias, + exclude_exceptions: excludeExceptions, + chunk_size: chunkSize, + }), + signal, + }); + +/** + * Create a Filter query from a list of exceptions + * + * @param exceptions Exception items to be made into a `Filter` query + * @param signal AbortSignal for cancelling request + * + * @throws An error if response is not OK + */ +export const getExceptionFilterFromExceptions = async ({ + exceptions, + alias, + excludeExceptions, + http, + chunkSize, + signal, +}: GetExceptionFilterFromExceptionsProps): Promise<ExceptionFilterResponse> => + http.fetch(INTERNAL_EXCEPTION_FILTER, { + method: 'POST', + version: '1', + body: JSON.stringify({ + exceptions, + type: 'exception_items', + alias, + exclude_exceptions: excludeExceptions, + chunk_size: chunkSize, + }), + signal, + }); + +/** + * Duplicate an ExceptionList and its items by providing a ExceptionList list_id + * + * @param http Kibana http service + * @param includeExpiredExceptions boolean for including exception items with expired TTL + * @param listId ExceptionList LIST_ID (not id) + * @param namespaceType ExceptionList namespace_type + * @param signal to cancel request + * + * @throws An error if response is not OK + */ +export const duplicateExceptionList = async ({ + http, + includeExpiredExceptions, + listId, + namespaceType, + signal, +}: DuplicateExceptionListProps): Promise<ExceptionListSchema> => + http.fetch<ExceptionListSchema>(`${EXCEPTION_LIST_URL}/_duplicate`, { + method: 'POST', + query: { + list_id: listId, + namespace_type: namespaceType, + include_expired_exceptions: includeExpiredExceptions, + }, + signal, + version, + }); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/fp_utils/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/fp_utils/index.test.ts new file mode 100644 index 000000000000..574c0f97292e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/fp_utils/index.test.ts @@ -0,0 +1,24 @@ +/* + * 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 { tryCatch } from 'fp-ts/lib/TaskEither'; + +import { toPromise } from '.'; + +describe('toPromise', () => { + it('rejects with left if TaskEither is left', async () => { + const task = tryCatch(() => Promise.reject(new Error('whoops')), String); + + await expect(toPromise(task)).rejects.toEqual('Error: whoops'); + }); + + it('resolves with right if TaskEither is right', async () => { + const task = tryCatch(() => Promise.resolve('success'), String); + + await expect(toPromise(task)).resolves.toEqual('success'); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/fp_utils/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/fp_utils/index.ts new file mode 100644 index 000000000000..1360c78db59e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/fp_utils/index.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 { pipe } from 'fp-ts/lib/pipeable'; +import { TaskEither } from 'fp-ts/lib/TaskEither'; +import { fold } from 'fp-ts/lib/Either'; + +// TODO: This is copied in a few other spots and probably should live within its own kbn package +// rather than living here. A package such as kbn-security-solution-fp-utils +export const toPromise = async <E, A>(taskEither: TaskEither<E, A>): Promise<A> => + pipe( + await taskEither(), + fold( + (e) => Promise.reject(e), + (a) => Promise.resolve(a) + ) + ); + +export const toError = (e: unknown): Error => (e instanceof Error ? e : new Error(String(e))); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/index.test.ts new file mode 100644 index 000000000000..1168b4e49195 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/index.test.ts @@ -0,0 +1,443 @@ +/* + * 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 { createListIndex, deleteList, exportList, findLists, importList, readListIndex } from '.'; +import { + ApiPayload, + DeleteListParams, + ExportListParams, + FindListsParams, + ImportListParams, +} from '../types'; + +import { HttpFetchOptions } from '@kbn/core-http-browser'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; + +import { getFoundListSchemaMock } from './mocks/response/found_list_schema.mock'; +import { getListResponseMock } from './mocks/response/list_schema.mock'; +import { getListItemIndexExistSchemaResponseMock } from './mocks/response/list_item_index_exist_schema.mock'; +import { getAcknowledgeSchemaResponseMock } from './mocks/response/acknowledge_schema.mock'; + +describe('Value Lists API', () => { + let httpMock: ReturnType<typeof httpServiceMock.createStartContract>; + + beforeEach(() => { + httpMock = httpServiceMock.createStartContract(); + }); + describe('deleteList', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getListResponseMock()); + }); + + it('DELETEs specifying the id as a query parameter', async () => { + const abortCtrl = new AbortController(); + const payload: ApiPayload<DeleteListParams> = { + deleteReferences: false, + id: 'list-id', + ignoreReferences: true, + }; + await deleteList({ + http: httpMock, + ...payload, + signal: abortCtrl.signal, + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists', + expect.objectContaining({ + method: 'DELETE', + query: { deleteReferences: false, id: 'list-id', ignoreReferences: true }, + }) + ); + }); + + it('rejects with an error if request payload is invalid (and does not make API call)', async () => { + const abortCtrl = new AbortController(); + const payload: Omit<ApiPayload<DeleteListParams>, 'id'> & { + id: number; + } = { id: 23 }; + + await expect( + deleteList({ + http: httpMock, + ...(payload as unknown as ApiPayload<DeleteListParams>), + signal: abortCtrl.signal, + }) + ).rejects.toEqual(new Error('Invalid value "23" supplied to "id"')); + expect(httpMock.fetch).not.toHaveBeenCalled(); + }); + + it('rejects with an error if response payload is invalid', async () => { + const abortCtrl = new AbortController(); + const payload: ApiPayload<DeleteListParams> = { id: 'list-id' }; + const badResponse = { ...getListResponseMock(), id: undefined }; + httpMock.fetch.mockResolvedValue(badResponse); + + await expect( + deleteList({ + http: httpMock, + ...payload, + signal: abortCtrl.signal, + }) + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); + }); + }); + describe('findLists', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getFoundListSchemaMock()); + }); + + it('GETs from the lists endpoint', async () => { + const abortCtrl = new AbortController(); + await findLists({ + http: httpMock, + pageIndex: 1, + pageSize: 10, + signal: abortCtrl.signal, + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/_find', + expect.objectContaining({ + method: 'GET', + }) + ); + }); + + it('sends pagination as query parameters', async () => { + const abortCtrl = new AbortController(); + await findLists({ + cursor: 'cursor', + http: httpMock, + pageIndex: 1, + pageSize: 10, + signal: abortCtrl.signal, + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/_find', + expect.objectContaining({ + query: { + cursor: 'cursor', + page: 1, + per_page: 10, + }, + }) + ); + }); + + it('sends sort_field and sort_order as query parameters', async () => { + const abortCtrl = new AbortController(); + await findLists({ + cursor: 'cursor', + http: httpMock, + pageIndex: 1, + pageSize: 10, + signal: abortCtrl.signal, + sortField: 'created_at', + sortOrder: 'desc', + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/_find', + expect.objectContaining({ + query: { + cursor: 'cursor', + page: 1, + per_page: 10, + sort_field: 'created_at', + sort_order: 'desc', + }, + }) + ); + }); + + it('rejects with an error if request payload is invalid (and does not make API call)', async () => { + const abortCtrl = new AbortController(); + const payload: ApiPayload<FindListsParams> = { + pageIndex: 10, + pageSize: 0, + }; + + await expect( + findLists({ + http: httpMock, + ...payload, + signal: abortCtrl.signal, + }) + ).rejects.toEqual(new Error('Invalid value "0" supplied to "per_page"')); + expect(httpMock.fetch).not.toHaveBeenCalled(); + }); + + it('rejects with an error if response payload is invalid', async () => { + const abortCtrl = new AbortController(); + const payload: ApiPayload<FindListsParams> = { + pageIndex: 1, + pageSize: 10, + }; + const badResponse = { ...getFoundListSchemaMock(), cursor: undefined }; + httpMock.fetch.mockResolvedValue(badResponse); + + await expect( + findLists({ + http: httpMock, + ...payload, + signal: abortCtrl.signal, + }) + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "cursor"')); + }); + }); + describe('importList', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getListResponseMock()); + }); + + it('POSTs the file', async () => { + const abortCtrl = new AbortController(); + const file = new File([], 'name'); + + await importList({ + file, + http: httpMock, + listId: 'my_list', + signal: abortCtrl.signal, + type: 'keyword', + }); + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/items/_import', + expect.objectContaining({ + method: 'POST', + }) + ); + + // httpmock's fetch signature is inferred incorrectly + const [[, { body }]] = httpMock.fetch.mock.calls as unknown as Array< + [unknown, HttpFetchOptions] + >; + const actualFile = (body as FormData).get('file'); + expect(actualFile).toEqual(file); + }); + + it('sends type and id as query parameters', async () => { + const abortCtrl = new AbortController(); + const file = new File([], 'name'); + + await importList({ + file, + http: httpMock, + listId: 'my_list', + signal: abortCtrl.signal, + type: 'keyword', + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/items/_import', + expect.objectContaining({ + query: { list_id: 'my_list', type: 'keyword' }, + }) + ); + }); + + it('rejects with an error if request body is invalid (and does not make API call)', async () => { + const abortCtrl = new AbortController(); + const payload: ApiPayload<ImportListParams> = { + file: undefined as unknown as File, + listId: 'list-id', + type: 'ip', + }; + + await expect( + importList({ + http: httpMock, + ...payload, + signal: abortCtrl.signal, + }) + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "file"')); + expect(httpMock.fetch).not.toHaveBeenCalled(); + }); + + it('rejects with an error if request params are invalid (and does not make API call)', async () => { + const abortCtrl = new AbortController(); + const file = new File([], 'name'); + const payload: ApiPayload<ImportListParams> = { + file, + listId: 'list-id', + type: 'other' as 'ip', + }; + + await expect( + importList({ + http: httpMock, + ...payload, + signal: abortCtrl.signal, + }) + ).rejects.toEqual(new Error('Invalid value "other" supplied to "type"')); + expect(httpMock.fetch).not.toHaveBeenCalled(); + }); + + it('rejects with an error if response payload is invalid', async () => { + const abortCtrl = new AbortController(); + const file = new File([], 'name'); + const payload: ApiPayload<ImportListParams> = { + file, + listId: 'list-id', + type: 'ip', + }; + const badResponse = { ...getListResponseMock(), id: undefined }; + httpMock.fetch.mockResolvedValue(badResponse); + + await expect( + importList({ + http: httpMock, + ...payload, + signal: abortCtrl.signal, + }) + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); + }); + }); + describe('exportList', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue({}); + }); + + it('POSTs to the export endpoint', async () => { + const abortCtrl = new AbortController(); + + await exportList({ + http: httpMock, + listId: 'my_list', + signal: abortCtrl.signal, + }); + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/items/_export', + expect.objectContaining({ + method: 'POST', + }) + ); + }); + + it('sends type and id as query parameters', async () => { + const abortCtrl = new AbortController(); + + await exportList({ + http: httpMock, + listId: 'my_list', + signal: abortCtrl.signal, + }); + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/items/_export', + expect.objectContaining({ + query: { list_id: 'my_list' }, + }) + ); + }); + + it('rejects with an error if request params are invalid (and does not make API call)', async () => { + const abortCtrl = new AbortController(); + const payload: ApiPayload<ExportListParams> = { + listId: 23 as unknown as string, + }; + + await expect( + exportList({ + http: httpMock, + ...payload, + signal: abortCtrl.signal, + }) + ).rejects.toEqual(new Error('Invalid value "23" supplied to "list_id"')); + expect(httpMock.fetch).not.toHaveBeenCalled(); + }); + }); + + describe('readListIndex', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getListItemIndexExistSchemaResponseMock()); + }); + + it('GETs the list index', async () => { + const abortCtrl = new AbortController(); + await readListIndex({ + http: httpMock, + signal: abortCtrl.signal, + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/index', + expect.objectContaining({ + method: 'GET', + }) + ); + }); + + it('returns the response when valid', async () => { + const abortCtrl = new AbortController(); + const result = await readListIndex({ + http: httpMock, + signal: abortCtrl.signal, + }); + + expect(result).toEqual(getListItemIndexExistSchemaResponseMock()); + }); + + it('rejects with an error if response payload is invalid', async () => { + const abortCtrl = new AbortController(); + const badResponse = { ...getListItemIndexExistSchemaResponseMock(), list_index: undefined }; + httpMock.fetch.mockResolvedValue(badResponse); + + await expect( + readListIndex({ + http: httpMock, + signal: abortCtrl.signal, + }) + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "list_index"')); + }); + }); + + describe('createListIndex', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getAcknowledgeSchemaResponseMock()); + }); + + it('GETs the list index', async () => { + const abortCtrl = new AbortController(); + await createListIndex({ + http: httpMock, + signal: abortCtrl.signal, + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/index', + expect.objectContaining({ + method: 'POST', + }) + ); + }); + + it('returns the response when valid', async () => { + const abortCtrl = new AbortController(); + const result = await createListIndex({ + http: httpMock, + signal: abortCtrl.signal, + }); + + expect(result).toEqual(getAcknowledgeSchemaResponseMock()); + }); + + it('rejects with an error if response payload is invalid', async () => { + const abortCtrl = new AbortController(); + const badResponse = { acknowledged: undefined }; + httpMock.fetch.mockResolvedValue(badResponse); + + await expect( + createListIndex({ + http: httpMock, + signal: abortCtrl.signal, + }) + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "acknowledged"')); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/index.ts new file mode 100644 index 000000000000..93160ca739e2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/index.ts @@ -0,0 +1,342 @@ +/* + * 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 { chain, fromEither, map, tryCatch } from 'fp-ts/lib/TaskEither'; +import { flow } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { validateEither } from '@kbn/securitysolution-io-ts-utils'; +import { + AcknowledgeSchema, + DeleteListSchemaEncoded, + ExportListItemQuerySchemaEncoded, + FindListSchemaEncoded, + FoundListSchema, + ImportListItemQuerySchemaEncoded, + ImportListItemSchemaEncoded, + ListItemIndexExistSchema, + ListSchema, + ReadListSchema, + acknowledgeSchema, + deleteListSchema, + readListSchema, + exportListItemQuerySchema, + findListSchema, + foundListSchema, + importListItemQuerySchema, + importListItemSchema, + listItemIndexExistSchema, + listSchema, + foundListsBySizeSchema, + FoundListsBySizeSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { + LIST_INDEX, + LIST_ITEM_URL, + LIST_PRIVILEGES_URL, + LIST_URL, + INTERNAL_FIND_LISTS_BY_SIZE, +} from '@kbn/securitysolution-list-constants'; +import { toError, toPromise } from '../fp_utils'; + +import { + ApiParams, + DeleteListParams, + ExportListParams, + FindListsParams, + ImportListParams, + GetListByIdParams, +} from '../types'; + +export type { + ApiParams, + DeleteListParams, + ExportListParams, + FindListsParams, + ImportListParams, +} from '../types'; + +const version = '2023-10-31'; + +const findLists = async ({ + http, + cursor, + page, + // eslint-disable-next-line @typescript-eslint/naming-convention + per_page, + signal, + // eslint-disable-next-line @typescript-eslint/naming-convention + sort_field, + // eslint-disable-next-line @typescript-eslint/naming-convention + sort_order, +}: ApiParams & FindListSchemaEncoded): Promise<FoundListSchema> => { + return http.fetch(`${LIST_URL}/_find`, { + method: 'GET', + query: { + cursor, + page, + per_page, + sort_field, + sort_order, + }, + signal, + version, + }); +}; + +const findListsWithValidation = async ({ + cursor, + http, + pageIndex, + pageSize, + signal, + sortField, + sortOrder, +}: FindListsParams): Promise<FoundListSchema> => + pipe( + { + cursor: cursor != null ? cursor.toString() : undefined, + page: pageIndex != null ? pageIndex.toString() : undefined, + per_page: pageSize != null ? pageSize.toString() : undefined, + sort_field: sortField != null ? sortField.toString() : undefined, + sort_order: sortOrder, + }, + (payload) => fromEither(validateEither(findListSchema, payload)), + chain((payload) => tryCatch(() => findLists({ http, signal, ...payload }), toError)), + chain((response) => fromEither(validateEither(foundListSchema, response))), + flow(toPromise) + ); + +export { findListsWithValidation as findLists }; + +const findListsBySize = async ({ + http, + cursor, + page, + // eslint-disable-next-line @typescript-eslint/naming-convention + per_page, + signal, +}: ApiParams & FindListSchemaEncoded): Promise<FoundListsBySizeSchema> => { + return http.fetch(`${INTERNAL_FIND_LISTS_BY_SIZE}`, { + method: 'GET', + version: '1', + query: { + cursor, + page, + per_page, + }, + signal, + }); +}; + +const findListsBySizeWithValidation = async ({ + cursor, + http, + pageIndex, + pageSize, + signal, +}: FindListsParams): Promise<FoundListsBySizeSchema> => + pipe( + { + cursor: cursor != null ? cursor.toString() : undefined, + page: pageIndex != null ? pageIndex.toString() : undefined, + per_page: pageSize != null ? pageSize.toString() : undefined, + }, + (payload) => fromEither(validateEither(findListSchema, payload)), + chain((payload) => tryCatch(() => findListsBySize({ http, signal, ...payload }), toError)), + chain((response) => fromEither(validateEither(foundListsBySizeSchema, response))), + flow(toPromise) + ); + +export { findListsBySizeWithValidation as findListsBySize }; + +const importList = async ({ + file, + http, + // eslint-disable-next-line @typescript-eslint/naming-convention + list_id, + type, + signal, + refresh, +}: ApiParams & + ImportListItemSchemaEncoded & + ImportListItemQuerySchemaEncoded): Promise<ListSchema> => { + const formData = new FormData(); + formData.append('file', file as Blob); + + return http.fetch<ListSchema>(`${LIST_ITEM_URL}/_import`, { + body: formData, + headers: { 'Content-Type': undefined }, + method: 'POST', + query: { list_id, type, refresh }, + signal, + version, + }); +}; + +const importListWithValidation = async ({ + file, + http, + listId, + type, + signal, + refresh, +}: ImportListParams): Promise<ListSchema> => + pipe( + { + list_id: listId, + type, + refresh, + }, + (query) => fromEither(validateEither(importListItemQuerySchema, query)), + chain((query) => + pipe( + fromEither(validateEither(importListItemSchema, { file })), + map((body) => ({ ...body, ...query })) + ) + ), + chain((payload) => tryCatch(() => importList({ http, signal, ...payload }), toError)), + chain((response) => fromEither(validateEither(listSchema, response))), + toPromise + ); + +export { importListWithValidation as importList }; + +const deleteList = async ({ + deleteReferences = false, + http, + id, + ignoreReferences = false, + signal, +}: ApiParams & DeleteListSchemaEncoded): Promise<ListSchema> => + http.fetch<ListSchema>(LIST_URL, { + method: 'DELETE', + query: { deleteReferences, id, ignoreReferences }, + signal, + version, + }); + +const deleteListWithValidation = async ({ + deleteReferences, + http, + id, + ignoreReferences, + signal, +}: DeleteListParams): Promise<ListSchema> => + pipe( + { deleteReferences, id, ignoreReferences }, + (payload) => fromEither(validateEither(deleteListSchema, payload)), + chain((payload) => tryCatch(() => deleteList({ http, signal, ...payload }), toError)), + chain((response) => fromEither(validateEither(listSchema, response))), + flow(toPromise) + ); + +export { deleteListWithValidation as deleteList }; + +const exportList = async ({ + http, + // eslint-disable-next-line @typescript-eslint/naming-convention + list_id, + signal, +}: ApiParams & ExportListItemQuerySchemaEncoded): Promise<Blob> => + http.fetch<Blob>(`${LIST_ITEM_URL}/_export`, { + method: 'POST', + query: { list_id }, + signal, + version, + }); + +const exportListWithValidation = async ({ + http, + listId, + signal, +}: ExportListParams): Promise<Blob> => + pipe( + { list_id: listId }, + (payload) => fromEither(validateEither(exportListItemQuerySchema, payload)), + chain((payload) => tryCatch(() => exportList({ http, signal, ...payload }), toError)), + flow(toPromise) + ); + +export { exportListWithValidation as exportList }; + +const readListIndex = async ({ http, signal }: ApiParams): Promise<ListItemIndexExistSchema> => + http.fetch<ListItemIndexExistSchema>(LIST_INDEX, { + method: 'GET', + signal, + version, + }); + +const readListIndexWithValidation = async ({ + http, + signal, +}: ApiParams): Promise<ListItemIndexExistSchema> => + flow( + () => tryCatch(() => readListIndex({ http, signal }), toError), + chain((response) => fromEither(validateEither(listItemIndexExistSchema, response))), + flow(toPromise) + )(); + +export { readListIndexWithValidation as readListIndex }; + +// TODO add types and validation +export const readListPrivileges = async ({ http, signal }: ApiParams): Promise<unknown> => + http.fetch<unknown>(LIST_PRIVILEGES_URL, { + method: 'GET', + signal, + version, + }); + +const createListIndex = async ({ http, signal }: ApiParams): Promise<AcknowledgeSchema> => + http.fetch<AcknowledgeSchema>(LIST_INDEX, { + method: 'POST', + signal, + version, + }); + +const createListIndexWithValidation = async ({ + http, + signal, +}: ApiParams): Promise<AcknowledgeSchema> => + flow( + () => tryCatch(() => createListIndex({ http, signal }), toError), + chain((response) => fromEither(validateEither(acknowledgeSchema, response))), + flow(toPromise) + )(); + +export { createListIndexWithValidation as createListIndex }; + +const getListById = async ({ + http, + signal, + id, +}: ApiParams & ReadListSchema): Promise<ListSchema> => { + return http.fetch(`${LIST_URL}`, { + method: 'GET', + query: { + id, + }, + signal, + version, + }); +}; + +const getListByIdWithValidation = async ({ + http, + signal, + id, +}: GetListByIdParams): Promise<ListSchema> => + pipe( + { + id, + }, + (payload) => fromEither(validateEither(readListSchema, payload)), + chain((payload) => tryCatch(() => getListById({ http, signal, ...payload }), toError)), + chain((response) => fromEither(validateEither(listSchema, response))), + flow(toPromise) + ); + +export { getListByIdWithValidation as getListById }; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/constants.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/constants.mock.ts new file mode 100644 index 000000000000..b3ce62d03f48 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/constants.mock.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const DATE_NOW = '2020-04-20T15:25:31.830Z'; +export const USER = 'some user'; +export const ELASTIC_USER = 'elastic'; +export const NAME = 'some name'; +export const DESCRIPTION = 'some description'; +export const LIST_ID = 'some-list-id'; +export const TIE_BREAKER = '6a76b69d-80df-4ab2-8c3e-85f466b06a0e'; + +export const META = {}; +export const TYPE = 'ip'; + +export const VERSION = 1; +export const IMMUTABLE = false; diff --git a/x-pack/plugins/lists/common/schemas/response/acknowledge_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/acknowledge_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/acknowledge_schema.mock.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/acknowledge_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/found_list_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/found_list_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/found_list_schema.mock.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/found_list_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/list_item_index_exist_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_item_index_exist_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/list_item_index_exist_schema.mock.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_item_index_exist_schema.mock.ts diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_schema.mock.ts new file mode 100644 index 000000000000..b2b66ad4636c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_api/mocks/response/list_schema.mock.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { + DATE_NOW, + DESCRIPTION, + ELASTIC_USER, + IMMUTABLE, + LIST_ID, + META, + NAME, + TIE_BREAKER, + TYPE, + USER, + VERSION, +} from '../constants.mock'; + +export const getListResponseMock = (): ListSchema => ({ + '@timestamp': DATE_NOW, + _version: undefined, + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, + deserializer: undefined, + id: LIST_ID, + immutable: IMMUTABLE, + meta: META, + name: NAME, + serializer: undefined, + tie_breaker_id: TIE_BREAKER, + type: TYPE, + updated_at: DATE_NOW, + updated_by: USER, + version: VERSION, +}); + +/** + * This is useful for end to end tests where we remove the auto generated parts for comparisons + * such as created_at, updated_at, and id. + */ +export const getListResponseMockWithoutAutoGeneratedValues = (): Partial<ListSchema> => ({ + created_by: ELASTIC_USER, + description: DESCRIPTION, + immutable: IMMUTABLE, + name: NAME, + type: TYPE, + updated_by: ELASTIC_USER, + version: VERSION, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/index.test.ts new file mode 100644 index 000000000000..651edfe25cb1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/index.test.ts @@ -0,0 +1,171 @@ +/* + * 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 { createListItem, deleteListItem, findListItems, patchListItem } from '.'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { + getFoundListSchemaMock, + getCreateListItemResponseMock, + getUpdatedListItemResponseMock, + getDeletedListItemResponseMock, +} from './mocks/response'; + +describe('Value list item API', () => { + let httpMock: ReturnType<typeof httpServiceMock.createStartContract>; + beforeEach(() => { + httpMock = httpServiceMock.createStartContract(); + }); + + describe('findListItems', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getFoundListSchemaMock()); + }); + + it('GETs from the lists endpoint with query params', async () => { + const abortCtrl = new AbortController(); + await findListItems({ + http: httpMock, + pageIndex: 1, + pageSize: 10, + signal: abortCtrl.signal, + filter: '*:*', + listId: 'list_id', + sortField: 'updated_at', + sortOrder: 'asc', + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/items/_find', + expect.objectContaining({ + method: 'GET', + query: { + cursor: undefined, + filter: '*:*', + list_id: 'list_id', + page: 1, + per_page: 10, + sort_field: 'updated_at', + sort_order: 'asc', + }, + }) + ); + }); + }); + + describe('createListItem', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getCreateListItemResponseMock()); + }); + + it('POSTs to the lists endpoint with the list item', async () => { + const abortCtrl = new AbortController(); + await createListItem({ + http: httpMock, + signal: abortCtrl.signal, + value: '123', + listId: 'list_id', + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/items', + expect.objectContaining({ + method: 'POST', + body: JSON.stringify({ + value: '123', + list_id: 'list_id', + }), + }) + ); + }); + + it('returns the created list item', async () => { + const abortCtrl = new AbortController(); + const result = await createListItem({ + http: httpMock, + signal: abortCtrl.signal, + value: '123', + listId: 'list_id', + }); + + expect(result).toEqual(getCreateListItemResponseMock()); + }); + }); + + describe('patchListItem', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getUpdatedListItemResponseMock()); + }); + + it('PATCH to the lists endpoint with the list item', async () => { + const abortCtrl = new AbortController(); + await patchListItem({ + http: httpMock, + signal: abortCtrl.signal, + id: 'item_id', + value: '123', + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/items', + expect.objectContaining({ + method: 'PATCH', + body: JSON.stringify({ + id: 'item_id', + value: '123', + }), + }) + ); + }); + + it('returns the updated list item', async () => { + const abortCtrl = new AbortController(); + const result = await patchListItem({ + http: httpMock, + signal: abortCtrl.signal, + id: 'item_id', + value: '123', + }); + + expect(result).toEqual(getUpdatedListItemResponseMock()); + }); + }); + + describe('deleteListItem', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getCreateListItemResponseMock()); + }); + + it('DELETE to the lists endpoint with the list item', async () => { + const abortCtrl = new AbortController(); + await deleteListItem({ + http: httpMock, + signal: abortCtrl.signal, + id: 'item_id', + refresh: 'true', + }); + + expect(httpMock.fetch).toHaveBeenCalledWith( + '/api/lists/items', + expect.objectContaining({ + method: 'DELETE', + query: { id: 'item_id', refresh: 'true' }, + }) + ); + }); + + it('returns the deleted list item', async () => { + const abortCtrl = new AbortController(); + const result = await deleteListItem({ + http: httpMock, + signal: abortCtrl.signal, + id: 'item_id', + }); + + expect(result).toEqual(getDeletedListItemResponseMock()); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/index.ts new file mode 100644 index 000000000000..1605b68ee04c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/index.ts @@ -0,0 +1,225 @@ +/* + * 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 { + FindListItemSchema, + ListItemSchema, + deleteListItemSchema, + patchListItemSchema, + createListItemSchema, + findListItemSchema, + foundListItemSchema, + listItemSchema, + FoundListItemSchema, + DeleteListItemSchema, + PatchListItemSchema, + CreateListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { chain, fromEither, tryCatch } from 'fp-ts/lib/TaskEither'; +import { flow } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { validateEither } from '@kbn/securitysolution-io-ts-utils'; + +import { LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; +import { + ApiParams, + FindListItemsParams, + DeleteListItemParams, + PatchListItemParams, + CreateListItemParams, +} from '../types'; +import { toError, toPromise } from '../fp_utils'; + +const version = '2023-10-31'; + +/** + * Fetch list items + */ +const findListItems = async ({ + http, + cursor, + page, + // eslint-disable-next-line @typescript-eslint/naming-convention + list_id, + // eslint-disable-next-line @typescript-eslint/naming-convention + per_page, + signal, + // eslint-disable-next-line @typescript-eslint/naming-convention + sort_field, + // eslint-disable-next-line @typescript-eslint/naming-convention + sort_order, + filter, +}: ApiParams & FindListItemSchema): Promise<FoundListItemSchema> => { + return http.fetch(`${LIST_ITEM_URL}/_find`, { + method: 'GET', + query: { + cursor, + page, + per_page, + sort_field, + sort_order, + list_id, + filter, + }, + signal, + version, + }); +}; + +const findListItemsWithValidation = async ({ + cursor, + http, + pageIndex, + pageSize, + signal, + sortField, + sortOrder, + filter, + listId, +}: FindListItemsParams): Promise<FoundListItemSchema> => + pipe( + { + cursor: cursor != null ? cursor.toString() : undefined, + page: pageIndex != null ? pageIndex.toString() : undefined, + per_page: pageSize != null ? pageSize.toString() : undefined, + sort_field: sortField != null ? sortField.toString() : undefined, + filter: filter != null ? filter.toString() : undefined, + sort_order: sortOrder, + list_id: listId, + }, + (payload) => fromEither(validateEither(findListItemSchema, payload)), + chain((payload) => tryCatch(() => findListItems({ http, signal, ...payload }), toError)), + chain((response) => fromEither(validateEither(foundListItemSchema, response))), + flow(toPromise) + ); + +export { findListItemsWithValidation as findListItems }; + +const deleteListItem = async ({ + http, + id, + signal, + refresh, +}: ApiParams & DeleteListItemSchema): Promise<ListItemSchema> => + http.fetch<ListItemSchema>(LIST_ITEM_URL, { + method: 'DELETE', + query: { id, refresh }, + signal, + version, + }); + +const deleteListItemWithValidation = async ({ + http, + id, + signal, + refresh, +}: DeleteListItemParams): Promise<ListItemSchema> => + pipe( + { id, refresh }, + (payload) => fromEither(validateEither(deleteListItemSchema, payload)), + chain((payload) => + tryCatch( + () => + deleteListItem({ + http, + signal, + ...payload, + value: undefined, + list_id: undefined, + }), + toError + ) + ), + chain((response) => fromEither(validateEither(listItemSchema, response))), + flow(toPromise) + ); + +export { deleteListItemWithValidation as deleteListItem }; + +const patchListItem = async ({ + http, + id, + signal, + value, + _version, +}: ApiParams & PatchListItemSchema): Promise<ListItemSchema> => + http.fetch<ListItemSchema>(LIST_ITEM_URL, { + method: 'PATCH', + body: JSON.stringify({ id, value, _version }), + signal, + version, + }); + +const patchListItemWithValidation = async ({ + http, + id, + signal, + value, + refresh, + _version, +}: PatchListItemParams): Promise<ListItemSchema> => + pipe( + { id, value, _version, refresh }, + (payload) => fromEither(validateEither(patchListItemSchema, payload)), + chain((payload) => + tryCatch( + () => + patchListItem({ + http, + signal, + ...payload, + }), + toError + ) + ), + chain((response) => fromEither(validateEither(listItemSchema, response))), + flow(toPromise) + ); + +export { patchListItemWithValidation as patchListItem }; + +const createListItem = async ({ + http, + signal, + value, + // eslint-disable-next-line @typescript-eslint/naming-convention + list_id, + refresh, +}: ApiParams & CreateListItemSchema): Promise<ListItemSchema> => + http.fetch<ListItemSchema>(LIST_ITEM_URL, { + method: 'POST', + body: JSON.stringify({ value, list_id, refresh }), + signal, + version, + }); + +const createListItemWithValidation = async ({ + http, + signal, + value, + refresh, + listId, +}: CreateListItemParams): Promise<ListItemSchema> => + pipe( + { list_id: listId, value, refresh }, + (payload) => fromEither(validateEither(createListItemSchema, payload)), + chain((payload) => + tryCatch( + () => + createListItem({ + http, + signal, + ...payload, + }), + toError + ) + ), + chain((response) => fromEither(validateEither(listItemSchema, response))), + flow(toPromise) + ); + +export { createListItemWithValidation as createListItem }; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/index.ts new file mode 100644 index 000000000000..2dddd844c5f2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FoundListItemSchema, ListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { getListItemResponseMock } from './list_item_schema.mock'; + +export const getFoundListSchemaMock = (): FoundListItemSchema => ({ + cursor: '123', + data: [getListItemResponseMock()], + page: 1, + per_page: 1, + total: 1, +}); + +export const getCreateListItemResponseMock = (): ListItemSchema => getListItemResponseMock(); +export const getUpdatedListItemResponseMock = (): ListItemSchema => getListItemResponseMock(); +export const getDeletedListItemResponseMock = (): ListItemSchema => getListItemResponseMock(); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/list_item_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/list_item_schema.mock.ts new file mode 100644 index 000000000000..726f49e9d0d3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/list_item_api/mocks/response/list_item_schema.mock.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +export const getListItemResponseMock = (): ListItemSchema => ({ + _version: '1', + '@timestamp': '2020-08-11T11:22:13.670Z', + created_at: '2020-08-11T11:22:13.670Z', + created_by: 'elastic', + deserializer: 'some deserializer', + id: 'bpdB3XMBx7pemMHopQ6M', + list_id: 'list_id', + meta: {}, + serializer: 'some serializer', + tie_breaker_id: '17d3befb-dc22-4b3c-a286-b5504c4fbeeb', + type: 'keyword', + updated_at: '2020-08-11T11:22:13.670Z', + updated_by: 'elastic', + value: 'some keyword', +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/types.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/types.ts new file mode 100644 index 000000000000..54aa89936d73 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/src/types.ts @@ -0,0 +1,85 @@ +/* + * 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 { + SortFieldOrUndefined, + SortOrderOrUndefined, + Type, + Refresh, + RefreshWithWaitFor, +} from '@kbn/securitysolution-io-ts-list-types'; + +// TODO: Replace these with kbn packaged versions once we have those available to us +// These originally came from this location below before moving them to this hacked "any" types: +// import { HttpStart, NotificationsStart } from '../../../../../../../../src/core/public'; +interface HttpStart { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fetch: <T>(...args: any) => any; +} + +export interface ApiParams { + http: HttpStart; + signal: AbortSignal; +} +export type ApiPayload<T extends ApiParams> = Omit<T, 'http' | 'signal'>; + +export interface FindListsParams extends ApiParams { + cursor?: string | undefined; + pageSize: number | undefined; + pageIndex: number | undefined; + sortOrder?: SortOrderOrUndefined; + sortField?: SortFieldOrUndefined; +} + +export interface FindListItemsParams extends ApiParams { + cursor?: string | undefined; + pageSize: number | undefined; + pageIndex: number | undefined; + sortOrder?: SortOrderOrUndefined; + sortField?: SortFieldOrUndefined; + filter: string | undefined; + listId: string; +} + +export interface ImportListParams extends ApiParams { + file: File; + listId: string | undefined; + type: Type | undefined; + refresh?: RefreshWithWaitFor; +} + +export interface DeleteListParams extends ApiParams { + deleteReferences?: boolean; + id: string; + ignoreReferences?: boolean; +} + +export interface DeleteListItemParams extends ApiParams { + refresh?: Refresh; + id: string; +} + +export interface PatchListItemParams extends ApiParams { + refresh?: Refresh; + id: string; + value: string; + _version?: string; +} + +export interface CreateListItemParams extends ApiParams { + refresh?: RefreshWithWaitFor; + value: string; + listId: string; +} + +export interface ExportListParams extends ApiParams { + listId: string; +} + +export interface GetListByIdParams extends ApiParams { + id: string; +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-api/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/tsconfig.json new file mode 100644 index 000000000000..10dbca803000 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-api/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "kbn_references": [ + "@kbn/securitysolution-io-ts-list-types", + "@kbn/securitysolution-io-ts-utils", + "@kbn/securitysolution-list-constants", + "@kbn/core-http-browser", + "@kbn/core-http-browser-mocks", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-securitysolution-list-constants/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/README.md similarity index 100% rename from packages/kbn-securitysolution-list-constants/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-list-constants/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/index.ts new file mode 100644 index 000000000000..746294c8908f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/index.ts @@ -0,0 +1,146 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { deepFreeze } from '@kbn/std'; + +/** + * Value list routes + */ +export const LIST_URL = '/api/lists'; +export const LIST_INDEX = `${LIST_URL}/index`; +export const LIST_ITEM_URL = `${LIST_URL}/items`; +export const LIST_PRIVILEGES_URL = `${LIST_URL}/privileges`; + +/** + * Internal value list routes + */ +export const INTERNAL_LIST_URL = '/internal/lists'; +export const INTERNAL_FIND_LISTS_BY_SIZE = `${INTERNAL_LIST_URL}/_find_lists_by_size` as const; +export const INTERNAL_EXCEPTION_FILTER = `${INTERNAL_LIST_URL}/_create_filter` as const; + +/** + * Exception list routes + */ +export const EXCEPTION_LIST_URL = '/api/exception_lists'; +export const EXCEPTION_LIST_ITEM_URL = '/api/exception_lists/items'; + +/** + * Internal exception list routes + */ +export const INTERNAL_EXCEPTION_LIST_URL = `/internal${EXCEPTION_LIST_URL}`; +export const INTERNAL_EXCEPTIONS_LIST_ENSURE_CREATED_URL = `${INTERNAL_EXCEPTION_LIST_URL}/_create`; + +/** + * Exception list spaces + */ +export const EXCEPTION_LIST_NAMESPACE_AGNOSTIC = 'exception-list-agnostic'; +export const EXCEPTION_LIST_NAMESPACE = 'exception-list'; + +/** + * Specific routes for the single global space agnostic endpoint list + */ +export const ENDPOINT_LIST_URL = '/api/endpoint_list'; + +/** + * Specific routes for the single global space agnostic endpoint list. These are convenience + * routes where they are going to try and create the global space agnostic endpoint list if it + * does not exist yet or if it was deleted at some point and re-create it before adding items to + * the list + */ +export const ENDPOINT_LIST_ITEM_URL = '/api/endpoint_list/items'; + +/** + * This ID is used for _both_ the Saved Object ID and for the list_id + * for the single global space agnostic endpoint list + */ +export const ENDPOINT_LIST_ID = 'endpoint_list'; + +/** The name of the single global space agnostic endpoint list */ +export const ENDPOINT_LIST_NAME = 'Endpoint Security Exception List'; + +/** The description of the single global space agnostic endpoint list */ +export const ENDPOINT_LIST_DESCRIPTION = 'Endpoint Security Exception List'; + +export const MAX_EXCEPTION_LIST_SIZE = 10000; + +export const MAXIMUM_SMALL_VALUE_LIST_SIZE = 65536; + +export const MAXIMUM_SMALL_IP_RANGE_VALUE_LIST_DASH_SIZE = 200; + +/** + * List definitions for Endpoint Artifact + */ +export const ENDPOINT_ARTIFACT_LISTS = deepFreeze({ + trustedApps: { + id: 'endpoint_trusted_apps', + name: 'Endpoint Security Trusted Apps List', + description: 'Endpoint Security Trusted Apps List', + }, + eventFilters: { + id: 'endpoint_event_filters', + name: 'Endpoint Security Event Filters List', + description: 'Endpoint Security Event Filters List', + }, + hostIsolationExceptions: { + id: 'endpoint_host_isolation_exceptions', + name: 'Endpoint Security Host isolation exceptions List', + description: 'Endpoint Security Host isolation exceptions List', + }, + blocklists: { + id: 'endpoint_blocklists', + name: 'Endpoint Security Blocklists List', + description: 'Endpoint Security Blocklists List', + }, +}); + +/** + * The IDs of all Endpoint artifact lists + */ +export const ENDPOINT_ARTIFACT_LIST_IDS = Object.freeze( + Object.values(ENDPOINT_ARTIFACT_LISTS).map(({ id }) => id) +); + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_TRUSTED_APPS_LIST_ID = ENDPOINT_ARTIFACT_LISTS.trustedApps.id; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_TRUSTED_APPS_LIST_NAME = ENDPOINT_ARTIFACT_LISTS.trustedApps.name; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION = + ENDPOINT_ARTIFACT_LISTS.trustedApps.description; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_EVENT_FILTERS_LIST_ID = ENDPOINT_ARTIFACT_LISTS.eventFilters.id; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_EVENT_FILTERS_LIST_NAME = ENDPOINT_ARTIFACT_LISTS.eventFilters.name; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION = + ENDPOINT_ARTIFACT_LISTS.eventFilters.description; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID = + ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.id; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_NAME = + ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.name; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_DESCRIPTION = + ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.description; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_BLOCKLISTS_LIST_ID = ENDPOINT_ARTIFACT_LISTS.blocklists.id; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_BLOCKLISTS_LIST_NAME = ENDPOINT_ARTIFACT_LISTS.blocklists.name; + +/** @deprecated Use `ENDPOINT_ARTIFACT_LISTS` instead */ +export const ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION = ENDPOINT_ARTIFACT_LISTS.blocklists.description; diff --git a/packages/kbn-securitysolution-list-constants/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-list-constants/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-list-constants/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/package.json new file mode 100644 index 000000000000..f57bdd68a2cc --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/securitysolution-list-constants", + "version": "1.0.0", + "description": "security solution list constants to use across plugins such lists, security_solution, cases, etc...", + "license": "Elastic License 2.0", + "private": true +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/tsconfig.json new file mode 100644 index 000000000000..0d3f96d6a17b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-constants/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "kbn_references": [ + "@kbn/std" + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-securitysolution-list-hooks/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/README.md similarity index 100% rename from packages/kbn-securitysolution-list-hooks/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/index.ts new file mode 100644 index 000000000000..8e5fdecab186 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './src/transforms'; +export * from './src/use_api'; +export * from './src/use_create_list_index'; +export * from './src/use_cursor'; +export * from './src/use_delete_list'; +export * from './src/use_exception_lists'; +export * from './src/use_export_list'; +export * from './src/use_find_lists'; +export * from './src/use_find_lists_by_size'; +export * from './src/use_import_list'; +export * from './src/use_persist_exception_item'; +export * from './src/use_persist_exception_list'; +export * from './src/use_read_list_index'; +export * from './src/use_read_list_privileges'; +export * from './src/use_find_list_items'; +export * from './src/use_create_list_item'; +export * from './src/use_delete_list_item'; +export * from './src/use_patch_list_item'; +export * from './src/use_get_list_by_id'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/jest.config.js b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/jest.config.js new file mode 100644 index 000000000000..7b0430c9bf09 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks'], +}; diff --git a/packages/kbn-securitysolution-list-hooks/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-list-hooks/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/package.json new file mode 100644 index 000000000000..8a2ead4291bd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/securitysolution-list-hooks", + "version": "1.0.0", + "description": "Security solution list ReactJS hooks", + "license": "Elastic License 2.0", + "private": true, + "sideEffects": false +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/constants.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/constants.ts new file mode 100644 index 000000000000..377c980531a4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/constants.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const READ_INDEX_QUERY_KEY = ['detectionEngine', 'listIndex']; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/index.ts new file mode 100644 index 000000000000..dd0e180eba26 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './transforms'; +export * from './use_api'; +export * from './use_create_list_index'; +export * from './use_cursor'; +export * from './use_delete_list'; +export * from './use_exception_lists'; +export * from './use_export_list'; +export * from './use_find_lists'; +export * from './use_import_list'; +export * from './use_persist_exception_item'; +export * from './use_persist_exception_list'; +export * from './use_read_list_index'; +export * from './use_read_list_privileges'; +export * from './use_find_list_items'; +export * from './use_create_list_item'; +export * from './use_delete_list_item'; +export * from './use_patch_list_item'; +export * from './use_get_list_by_id'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/constants.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/constants.mock.ts new file mode 100644 index 000000000000..762d1eb243cf --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/constants.mock.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + CommentsArray, + EntriesArray, + Entry, + EntryMatch, + EntryNested, + OsTypeArray, +} from '@kbn/securitysolution-io-ts-list-types'; + +export const DATE_NOW = '2020-04-20T15:25:31.830Z'; +export const USER = 'some user'; +export const ELASTIC_USER = 'elastic'; +export const NAME = 'some name'; +export const DESCRIPTION = 'some description'; +export const LIST_ID = 'some-list-id'; +export const TIE_BREAKER = '6a76b69d-80df-4ab2-8c3e-85f466b06a0e'; + +export const META = {}; +export const TYPE = 'ip'; + +export const VERSION = 1; +export const IMMUTABLE = false; +// Exception List specific +export const ID = 'uuid_here'; +export const NAMESPACE_TYPE = 'single'; +export const OS_TYPES: OsTypeArray = ['windows']; +export const TAGS = []; +export const UPDATED_COMMENTS = [ + { + comment: 'old comment', + id: 'old_created_id', + }, + { + comment: 'new comment', + id: 'new_id', + }, +]; +export const COMMENTS = []; +export const ENTRIES: EntriesArray = [ + { + entries: [{ field: 'nested.field', operator: 'included', type: 'match', value: 'some value' }], + field: 'some.parentField', + type: 'nested', + }, + { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, +]; +export const ITEM_ID = 'some-list-item-id'; +export const ITEM_TYPE = 'simple'; +export const LIST_ITEM_ID = 'some-list-item-id'; +// ENTRIES_WITH_IDS should only be used to mock out functionality of a collection of transforms +// that are UI specific and useful for UI concerns that are inserted between the +// API and the actual user interface. In some ways these might be viewed as +// technical debt or to compensate for the differences and preferences +// of how ReactJS might prefer data vs. how we want to model data. +export const ENTRIES_WITH_IDS: EntriesArray = [ + { + entries: [ + { + field: 'nested.field', + id: '123', + operator: 'included', + type: 'match', + value: 'some value', + } as EntryMatch & { id: string }, + ], + field: 'some.parentField', + id: '123', + type: 'nested', + } as EntryNested & { id: string }, + { + field: 'some.not.nested.field', + id: '123', + operator: 'included', + type: 'match', + value: 'some value', + } as Entry & { id: string }, +]; + +export const COMMENTS_WITH_CREATEDAT_CREATEDBY: CommentsArray = [ + { + comment: 'old comment', + id: 'old_created_id', + created_at: '2022-01-08T15:25:31.830Z', + created_by: 'elastic', + }, + { + comment: 'new comment', + id: 'new_id', + created_at: '2022-05-14T15:25:31.830Z', + created_by: 'elastic', + }, +]; diff --git a/packages/kbn-securitysolution-list-hooks/src/mocks/request/create_exception_list_item_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/request/create_exception_list_item_schema.mock.ts similarity index 76% rename from packages/kbn-securitysolution-list-hooks/src/mocks/request/create_exception_list_item_schema.mock.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/request/create_exception_list_item_schema.mock.ts index 5727a271a9bf..ff9789a7995b 100644 --- a/packages/kbn-securitysolution-list-hooks/src/mocks/request/create_exception_list_item_schema.mock.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/request/create_exception_list_item_schema.mock.ts @@ -1,10 +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", 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". + * 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 { CreateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/request/update_exception_list_item_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/request/update_exception_list_item_schema.mock.ts new file mode 100644 index 000000000000..47b0030c316a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/request/update_exception_list_item_schema.mock.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { UpdateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { + DESCRIPTION, + ENTRIES, + ID, + ITEM_ID, + ITEM_TYPE, + LIST_ITEM_ID, + META, + NAME, + NAMESPACE_TYPE, + OS_TYPES, + TAGS, + UPDATED_COMMENTS, +} from '../constants.mock'; + +export const getUpdateExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ + _version: undefined, + comments: UPDATED_COMMENTS, + description: DESCRIPTION, + entries: ENTRIES, + id: ID, + item_id: LIST_ITEM_ID, + meta: META, + name: NAME, + namespace_type: NAMESPACE_TYPE, + os_types: ['linux'], + tags: TAGS, + type: ITEM_TYPE, +}); + +/** + * Useful for end to end tests and other mechanisms which want to fill in the values + * after doing a get of the structure. + */ +export const getUpdateMinimalExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ + description: DESCRIPTION, + entries: ENTRIES, + item_id: ITEM_ID, + name: NAME, + os_types: OS_TYPES, + type: ITEM_TYPE, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/response/exception_list_item_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/response/exception_list_item_schema.mock.ts new file mode 100644 index 000000000000..acb7204da9b7 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/response/exception_list_item_schema.mock.ts @@ -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 type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { + COMMENTS, + DATE_NOW, + DESCRIPTION, + ELASTIC_USER, + ENTRIES, + ITEM_ID, + ITEM_TYPE, + LIST_ID, + META, + NAME, + NAMESPACE_TYPE, + OS_TYPES, + TIE_BREAKER, + USER, +} from '../constants.mock'; + +export const getExceptionListItemSchemaMock = ( + overrides?: Partial<ExceptionListItemSchema> +): ExceptionListItemSchema => ({ + _version: undefined, + comments: COMMENTS, + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, + entries: ENTRIES, + expire_time: undefined, + id: '1', + item_id: 'endpoint_list_item', + list_id: 'endpoint_list_id', + meta: META, + name: NAME, + namespace_type: NAMESPACE_TYPE, + os_types: [], + tags: ['user added string for a tag', 'malware'], + tie_breaker_id: TIE_BREAKER, + type: ITEM_TYPE, + updated_at: DATE_NOW, + updated_by: USER, + ...(overrides || {}), +}); + +/** + * This is useful for end to end tests where we remove the auto generated parts for comparisons + * such as created_at, updated_at, and id. + */ +export const getExceptionListItemResponseMockWithoutAutoGeneratedValues = + (): Partial<ExceptionListItemSchema> => ({ + comments: [], + created_by: ELASTIC_USER, + description: DESCRIPTION, + entries: ENTRIES, + item_id: ITEM_ID, + list_id: LIST_ID, + name: NAME, + namespace_type: 'single', + os_types: OS_TYPES, + tags: [], + type: ITEM_TYPE, + updated_by: ELASTIC_USER, + }); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/response/found_list_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/response/found_list_schema.mock.ts new file mode 100644 index 000000000000..1691221842d3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/response/found_list_schema.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FoundListSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { getListResponseMock } from './list_schema.mock'; + +export const getFoundListSchemaMock = (): FoundListSchema => ({ + cursor: '123', + data: [getListResponseMock()], + page: 1, + per_page: 1, + total: 1, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/response/list_schema.mock.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/response/list_schema.mock.ts new file mode 100644 index 000000000000..b2b66ad4636c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/mocks/response/list_schema.mock.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { + DATE_NOW, + DESCRIPTION, + ELASTIC_USER, + IMMUTABLE, + LIST_ID, + META, + NAME, + TIE_BREAKER, + TYPE, + USER, + VERSION, +} from '../constants.mock'; + +export const getListResponseMock = (): ListSchema => ({ + '@timestamp': DATE_NOW, + _version: undefined, + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, + deserializer: undefined, + id: LIST_ID, + immutable: IMMUTABLE, + meta: META, + name: NAME, + serializer: undefined, + tie_breaker_id: TIE_BREAKER, + type: TYPE, + updated_at: DATE_NOW, + updated_by: USER, + version: VERSION, +}); + +/** + * This is useful for end to end tests where we remove the auto generated parts for comparisons + * such as created_at, updated_at, and id. + */ +export const getListResponseMockWithoutAutoGeneratedValues = (): Partial<ListSchema> => ({ + created_by: ELASTIC_USER, + description: DESCRIPTION, + immutable: IMMUTABLE, + name: NAME, + type: TYPE, + updated_by: ELASTIC_USER, + version: VERSION, +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/transforms/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/transforms/index.test.ts new file mode 100644 index 000000000000..dc94fcddd849 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/transforms/index.test.ts @@ -0,0 +1,297 @@ +/* + * 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 { + CreateExceptionListItemSchema, + Entry, + EntryMatch, + EntryNested, + ExceptionListItemSchema, + UpdateExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { + addIdToExceptionItemEntries, + removeIdFromExceptionItemsEntries, + transformInput, + transformOutput, +} from '../..'; + +import { getCreateExceptionListItemSchemaMock } from '../mocks/request/create_exception_list_item_schema.mock'; +import { getUpdateExceptionListItemSchemaMock } from '../mocks/request/update_exception_list_item_schema.mock'; +import { getExceptionListItemSchemaMock } from '../mocks/response/exception_list_item_schema.mock'; +import { COMMENTS_WITH_CREATEDAT_CREATEDBY, ENTRIES_WITH_IDS } from '../mocks/constants.mock'; + +jest.mock('uuid', () => ({ + v4: jest.fn().mockReturnValue('123'), +})); + +describe('Exceptions transforms', () => { + describe('transformOutput', () => { + it('should return same output as input with stripped ids per entry - CreateExceptionListItemSchema', () => { + const mockCreateExceptionItem = { + ...getCreateExceptionListItemSchemaMock(), + entries: ENTRIES_WITH_IDS, + }; + const output = transformOutput(mockCreateExceptionItem); + const expectedOutput: CreateExceptionListItemSchema = getCreateExceptionListItemSchemaMock(); + + expect(output).toEqual(expectedOutput); + }); + + it('should return same output as input with stripped ids per entry - UpdateExceptionListItemSchema', () => { + const mockUpdateExceptionItem = { + ...getUpdateExceptionListItemSchemaMock(), + entries: ENTRIES_WITH_IDS, + }; + const output = transformOutput(mockUpdateExceptionItem); + const expectedOutput: UpdateExceptionListItemSchema = getUpdateExceptionListItemSchemaMock(); + + expect(output).toEqual(expectedOutput); + }); + it('should return output as input with stripped createdAt and createdBy per entry - UpdateExceptionListItemSchema', () => { + const mockUpdateExceptionItem = { + ...getUpdateExceptionListItemSchemaMock(), + entries: ENTRIES_WITH_IDS, + comments: COMMENTS_WITH_CREATEDAT_CREATEDBY, + }; + const output = transformOutput(mockUpdateExceptionItem); + const expectedOutput: UpdateExceptionListItemSchema = getUpdateExceptionListItemSchemaMock(); + + expect(output).toEqual(expectedOutput); + }); + }); + + describe('transformInput', () => { + it('should return same output as input with added ids per entry', () => { + const mockExceptionItem = getExceptionListItemSchemaMock(); + const output = transformInput(mockExceptionItem); + const expectedOutput: ExceptionListItemSchema = { + ...getExceptionListItemSchemaMock(), + entries: ENTRIES_WITH_IDS, + }; + + expect(output).toEqual(expectedOutput); + }); + }); + + describe('addIdToExceptionItemEntries', () => { + it('should return same output as input with added ids per entry', () => { + const mockExceptionItem: ExceptionListItemSchema = { + ...getExceptionListItemSchemaMock(), + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + }; + const output = addIdToExceptionItemEntries(mockExceptionItem); + const expectedOutput: ExceptionListItemSchema = { + ...getExceptionListItemSchemaMock(), + entries: [ + { + field: 'some.not.nested.field', + id: '123', + operator: 'included', + type: 'match', + value: 'some value', + } as Entry & { id: string }, + ], + }; + + expect(output).toEqual(expectedOutput); + }); + + it('should return same output as input with added ids per nested entry', () => { + const mockExceptionItem: ExceptionListItemSchema = { + ...getExceptionListItemSchemaMock(), + entries: [ + { + entries: [ + { + field: 'nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + field: 'some.parentField', + type: 'nested', + }, + ], + }; + const output = addIdToExceptionItemEntries(mockExceptionItem); + const expectedOutput: ExceptionListItemSchema = { + ...getExceptionListItemSchemaMock(), + entries: [ + { + entries: [ + { + field: 'nested.field', + id: '123', + operator: 'included', + type: 'match', + value: 'some value', + } as EntryMatch & { id: string }, + ], + field: 'some.parentField', + id: '123', + type: 'nested', + } as EntryNested & { id: string }, + ], + }; + + expect(output).toEqual(expectedOutput); + }); + }); + + describe('removeIdFromExceptionItemsEntries', () => { + it('should return same output as input with stripped ids per entry - CreateExceptionListItemSchema', () => { + const mockCreateExceptionItem = { + ...getCreateExceptionListItemSchemaMock(), + entries: [ + { + field: 'some.not.nested.field', + id: '123', + operator: 'included', + type: 'match', + value: 'some value', + } as Entry & { id: string }, + ], + }; + const output = removeIdFromExceptionItemsEntries(mockCreateExceptionItem); + const expectedOutput: CreateExceptionListItemSchema = { + ...getCreateExceptionListItemSchemaMock(), + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + }; + + expect(output).toEqual(expectedOutput); + }); + + it('should return same output as input with stripped ids per nested entry - CreateExceptionListItemSchema', () => { + const mockCreateExceptionItem = { + ...getCreateExceptionListItemSchemaMock(), + entries: [ + { + entries: [ + { + field: 'nested.field', + id: '123', + operator: 'included', + type: 'match', + value: 'some value', + } as EntryMatch & { id: string }, + ], + field: 'some.parentField', + id: '123', + type: 'nested', + } as EntryNested & { id: string }, + ], + }; + const output = removeIdFromExceptionItemsEntries(mockCreateExceptionItem); + const expectedOutput: CreateExceptionListItemSchema = { + ...getCreateExceptionListItemSchemaMock(), + entries: [ + { + entries: [ + { + field: 'nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + field: 'some.parentField', + type: 'nested', + }, + ], + }; + + expect(output).toEqual(expectedOutput); + }); + + it('should return same output as input with stripped ids per entry - UpdateExceptionListItemSchema', () => { + const mockUpdateExceptionItem = { + ...getUpdateExceptionListItemSchemaMock(), + entries: [ + { + field: 'some.not.nested.field', + id: '123', + operator: 'included', + type: 'match', + value: 'some value', + } as Entry & { id: string }, + ], + }; + const output = removeIdFromExceptionItemsEntries(mockUpdateExceptionItem); + const expectedOutput: UpdateExceptionListItemSchema = { + ...getUpdateExceptionListItemSchemaMock(), + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + }; + + expect(output).toEqual(expectedOutput); + }); + + it('should return same output as input with stripped ids per nested entry - UpdateExceptionListItemSchema', () => { + const mockUpdateExceptionItem = { + ...getUpdateExceptionListItemSchemaMock(), + entries: [ + { + entries: [ + { + field: 'nested.field', + id: '123', + operator: 'included', + type: 'match', + value: 'some value', + } as EntryMatch & { id: string }, + ], + field: 'some.parentField', + id: '123', + type: 'nested', + } as EntryNested & { id: string }, + ], + }; + const output = removeIdFromExceptionItemsEntries(mockUpdateExceptionItem); + const expectedOutput: UpdateExceptionListItemSchema = { + ...getUpdateExceptionListItemSchemaMock(), + entries: [ + { + entries: [ + { + field: 'nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + field: 'some.parentField', + type: 'nested', + }, + ], + }; + + expect(output).toEqual(expectedOutput); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/transforms/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/transforms/index.ts new file mode 100644 index 000000000000..276c52b000af --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/transforms/index.ts @@ -0,0 +1,136 @@ +/* + * 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 { flow } from 'fp-ts/lib/function'; +import { addIdToItem, removeIdFromItem } from '@kbn/securitysolution-utils'; +import type { + CreateExceptionListItemSchema, + EntriesArray, + Entry, + ExceptionListItemSchema, + UpdateExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; + +// These are a collection of transforms that are UI specific and useful for UI concerns +// that are inserted between the API and the actual user interface. In some ways these +// might be viewed as technical debt or to compensate for the differences and preferences +// of how ReactJS might prefer data vs. how we want to model data. Each function should have +// a description giving context around the transform. + +/** + * Transforms the output of exception items to compensate for technical debt or UI concerns such as + * ReactJS preferences for having ids within arrays if the data is not modeled that way. + * + * If you add a new transform of the output called "myNewTransform" do it + * in the form of: + * flow(removeIdFromExceptionItemsEntries, myNewTransform)(exceptionItem) + * + * @param exceptionItem The exceptionItem to transform the output of + * @returns The exceptionItem transformed from the output + */ +export const transformOutput = ( + exceptionItem: UpdateExceptionListItemSchema | ExceptionListItemSchema +): UpdateExceptionListItemSchema | ExceptionListItemSchema => + flow( + removeCreatedAtCreatedByFromCommentsOnUpdate, + removeIdFromExceptionItemsEntries + )(exceptionItem); + +export const transformNewItemOutput = ( + exceptionItem: CreateExceptionListItemSchema +): CreateExceptionListItemSchema => flow(removeIdFromExceptionItemsEntries)(exceptionItem); + +/** + * Transforms the output of rules to compensate for technical debt or UI concerns such as + * ReactJS preferences for having ids within arrays if the data is not modeled that way. + * + * If you add a new transform of the input called "myNewTransform" do it + * in the form of: + * flow(addIdToExceptionItemEntries, myNewTransform)(exceptionItem) + * + * @param exceptionItem The exceptionItem to transform the output of + * @returns The exceptionItem transformed from the output + */ +export const transformInput = (exceptionItem: ExceptionListItemSchema): ExceptionListItemSchema => + flow(addIdToExceptionItemEntries)(exceptionItem); + +/** + * This adds an id to the incoming exception item entries as ReactJS prefers to have + * an id added to them for use as a stable id. Later if we decide to change the data + * model to have id's within the array then this code should be removed. If not, then + * this code should stay as an adapter for ReactJS. + * + * This does break the type system slightly as we are lying a bit to the type system as we return + * the same exceptionItem as we have previously but are augmenting the arrays with an id which TypeScript + * doesn't mind us doing here. However, downstream you will notice that you have an id when the type + * does not indicate it. In that case use (ExceptionItem & { id: string }) temporarily if you're using the id. If you're not, + * you can ignore the id and just use the normal TypeScript with ReactJS. + * + * @param exceptionItem The exceptionItem to add an id to the threat matches. + * @returns exceptionItem The exceptionItem but with id added to the exception item entries + */ +export const addIdToExceptionItemEntries = ( + exceptionItem: ExceptionListItemSchema +): ExceptionListItemSchema => { + const entries = exceptionItem.entries.map((entry) => { + if (entry.type === 'nested') { + return addIdToItem({ + ...entry, + entries: entry.entries.map((nestedEntry) => addIdToItem(nestedEntry)), + }); + } else { + return addIdToItem(entry); + } + }); + return { ...exceptionItem, entries }; +}; + +/** + * This removes an id from the exceptionItem entries as ReactJS prefers to have + * an id added to them for use as a stable id. Later if we decide to change the data + * model to have id's within the array then this code should be removed. If not, then + * this code should stay as an adapter for ReactJS. + * + * @param exceptionItem The exceptionItem to remove an id from the entries. + * @returns exceptionItem The exceptionItem but with id removed from the entries + */ +export const removeIdFromExceptionItemsEntries = <T extends { entries: EntriesArray }>( + exceptionItem: T +): T => { + const { entries } = exceptionItem; + const entriesNoId = entries.map((entry) => { + if (entry.type === 'nested') { + return removeIdFromItem({ + ...entry, + entries: entry.entries.map((nestedEntry) => removeIdFromItem(nestedEntry)), + }); + } else { + return removeIdFromItem<Entry>(entry); + } + }); + return { ...exceptionItem, entries: entriesNoId }; +}; + +/** + * This removes createdAt, createdBy from the exceptionItem if a comment was added to + * the Exception item, and return the comment message with id to prevent creating the commet + * twice + * @param exceptionItem The exceptionItem to remove createdAt, createdBy from the comments array. + * @returns exceptionItem The exceptionItem with comments do not have createdAt, createdBy. + */ +export const removeCreatedAtCreatedByFromCommentsOnUpdate = ( + exceptionItem: UpdateExceptionListItemSchema | ExceptionListItemSchema +) => { + const { comments } = exceptionItem; + if (!comments || !comments.length) return exceptionItem; + + const entriesNoCreatedAtAndBy = comments.map(({ comment, id }) => ({ + comment, + id, + })); + return { ...exceptionItem, comments: entriesNoCreatedAtAndBy }; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts new file mode 100644 index 000000000000..673eaff90b1a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts @@ -0,0 +1,315 @@ +/* + * 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 type { + CreateExceptionListItemSchema, + ExceptionListItemSchema, + ExceptionListSchema, + UpdateExceptionListItemSchema, + ApiCallFindListsItemsMemoProps, + ApiCallMemoProps, + ApiListExportProps, + ApiCallGetExceptionFilterFromIdsMemoProps, + ApiCallGetExceptionFilterFromExceptionsMemoProps, + ApiListDuplicateProps, +} from '@kbn/securitysolution-io-ts-list-types'; +import * as Api from '@kbn/securitysolution-list-api'; +import { getIdsAndNamespaces } from '@kbn/securitysolution-list-utils'; +import type { HttpStart } from '@kbn/core-http-browser'; + +import { transformInput, transformNewItemOutput, transformOutput } from '../transforms'; + +export interface ExceptionsApi { + addExceptionListItem: (arg: { + listItem: CreateExceptionListItemSchema; + }) => Promise<ExceptionListItemSchema>; + updateExceptionListItem: (arg: { + listItem: UpdateExceptionListItemSchema; + }) => Promise<ExceptionListItemSchema>; + deleteExceptionItem: (arg: ApiCallMemoProps) => Promise<void>; + deleteExceptionList: (arg: ApiCallMemoProps) => Promise<void>; + duplicateExceptionList: (arg: ApiListDuplicateProps) => Promise<void>; + getExceptionItem: ( + arg: ApiCallMemoProps & { onSuccess: (arg: ExceptionListItemSchema) => void } + ) => Promise<void>; + getExceptionList: ( + arg: ApiCallMemoProps & { onSuccess: (arg: ExceptionListSchema) => void } + ) => Promise<void>; + getExceptionListsItems: (arg: ApiCallFindListsItemsMemoProps) => Promise<void>; + getExceptionFilterFromIds: (arg: ApiCallGetExceptionFilterFromIdsMemoProps) => Promise<void>; + getExceptionFilterFromExceptions: ( + arg: ApiCallGetExceptionFilterFromExceptionsMemoProps + ) => Promise<void>; + exportExceptionList: (arg: ApiListExportProps) => Promise<void>; +} + +export const useApi = (http: HttpStart): ExceptionsApi => { + return useMemo( + (): ExceptionsApi => ({ + async addExceptionListItem({ + listItem, + }: { + listItem: CreateExceptionListItemSchema; + }): Promise<ExceptionListItemSchema> { + const abortCtrl = new AbortController(); + const sanitizedItem: CreateExceptionListItemSchema = transformNewItemOutput(listItem); + + return Api.addExceptionListItem({ + http, + listItem: sanitizedItem, + signal: abortCtrl.signal, + }); + }, + async deleteExceptionItem({ + id, + namespaceType, + onSuccess, + onError, + }: ApiCallMemoProps): Promise<void> { + const abortCtrl = new AbortController(); + + try { + await Api.deleteExceptionListItemById({ + http, + id, + namespaceType, + signal: abortCtrl.signal, + }); + onSuccess(); + } catch (error) { + onError(error); + } + }, + async deleteExceptionList({ + id, + namespaceType, + onSuccess, + onError, + }: ApiCallMemoProps): Promise<void> { + const abortCtrl = new AbortController(); + + try { + await Api.deleteExceptionListById({ + http, + id, + namespaceType, + signal: abortCtrl.signal, + }); + onSuccess(); + } catch (error) { + onError(error); + } + }, + async duplicateExceptionList({ + includeExpiredExceptions, + listId, + namespaceType, + onError, + onSuccess, + }: ApiListDuplicateProps): Promise<void> { + const abortCtrl = new AbortController(); + + try { + const newList = await Api.duplicateExceptionList({ + http, + includeExpiredExceptions, + listId, + namespaceType, + signal: abortCtrl.signal, + }); + onSuccess(newList); + } catch (error) { + onError(error); + } + }, + async exportExceptionList({ + id, + includeExpiredExceptions, + listId, + namespaceType, + onError, + onSuccess, + }: ApiListExportProps): Promise<void> { + const abortCtrl = new AbortController(); + + try { + const blob = await Api.exportExceptionList({ + http, + id, + includeExpiredExceptions, + listId, + namespaceType, + signal: abortCtrl.signal, + }); + onSuccess(blob); + } catch (error) { + onError(error); + } + }, + async getExceptionItem({ + id, + namespaceType, + onSuccess, + onError, + }: ApiCallMemoProps & { onSuccess: (arg: ExceptionListItemSchema) => void }): Promise<void> { + const abortCtrl = new AbortController(); + + try { + const item = transformInput( + await Api.fetchExceptionListItemById({ + http, + id, + namespaceType, + signal: abortCtrl.signal, + }) + ); + onSuccess(item); + } catch (error) { + onError(error); + } + }, + async getExceptionList({ + id, + namespaceType, + onSuccess, + onError, + }: ApiCallMemoProps & { onSuccess: (arg: ExceptionListSchema) => void }): Promise<void> { + const abortCtrl = new AbortController(); + + try { + const list = await Api.fetchExceptionListById({ + http, + id, + namespaceType, + signal: abortCtrl.signal, + }); + onSuccess(list); + } catch (error) { + onError(error); + } + }, + async getExceptionListsItems({ + lists, + filter, + pagination, + showDetectionsListsOnly, + showEndpointListsOnly, + onSuccess, + onError, + }: ApiCallFindListsItemsMemoProps): Promise<void> { + const abortCtrl = new AbortController(); + const { ids, namespaces } = getIdsAndNamespaces({ + lists, + showDetection: showDetectionsListsOnly, + showEndpoint: showEndpointListsOnly, + }); + + try { + if (ids.length > 0 && namespaces.length > 0) { + const { + data, + page, + per_page: perPage, + total, + } = await Api.fetchExceptionListsItemsByListIds({ + filter, + http, + listIds: ids, + namespaceTypes: namespaces, + pagination, + signal: abortCtrl.signal, + }); + onSuccess({ + // This data transform is UI specific and useful for UI concerns + // to compensate for the differences and preferences of how ReactJS might prefer + // data vs. how we want to model data. View `transformInput` for more details + exceptions: data.map((item) => transformInput(item)), + pagination: { + page, + perPage, + total, + }, + }); + } else { + onSuccess({ + exceptions: [], + pagination: { + page: 0, + perPage: pagination.perPage != null ? pagination.perPage : 0, + total: 0, + }, + }); + } + } catch (error) { + onError(error); + } + }, + async getExceptionFilterFromIds({ + exceptionListIds, + chunkSize, + alias, + excludeExceptions, + onSuccess, + onError, + }: ApiCallGetExceptionFilterFromIdsMemoProps): Promise<void> { + const abortCtrl = new AbortController(); + try { + const { filter } = await Api.getExceptionFilterFromExceptionListIds({ + http, + exceptionListIds, + signal: abortCtrl.signal, + chunkSize, + alias, + excludeExceptions, + }); + onSuccess(filter); + } catch (error) { + onError(error); + } + }, + async getExceptionFilterFromExceptions({ + exceptions, + chunkSize, + alias, + excludeExceptions, + onSuccess, + onError, + }: ApiCallGetExceptionFilterFromExceptionsMemoProps): Promise<void> { + const abortCtrl = new AbortController(); + try { + const { filter } = await Api.getExceptionFilterFromExceptions({ + http, + exceptions, + signal: abortCtrl.signal, + chunkSize, + alias, + excludeExceptions, + }); + onSuccess(filter); + } catch (error) { + onError(error); + } + }, + async updateExceptionListItem({ + listItem, + }: { + listItem: UpdateExceptionListItemSchema; + }): Promise<ExceptionListItemSchema> { + const abortCtrl = new AbortController(); + const sanitizedItem: UpdateExceptionListItemSchema = transformOutput(listItem); + + return Api.updateExceptionListItem({ + http, + listItem: sanitizedItem, + signal: abortCtrl.signal, + }); + }, + }), + [http] + ); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.ts new file mode 100644 index 000000000000..9cfaf30a9e2a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { createListIndex, ApiParams } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; + +import { READ_INDEX_QUERY_KEY } from '../constants'; + +const createListIndexWithOptionalSignal = withOptionalSignal(createListIndex); + +export const useCreateListIndex = ({ + http, + onError, +}: { + http: ApiParams['http']; + onError?: (err: unknown) => void; +}) => { + const queryClient = useQueryClient(); + + const { mutate, isLoading, error } = useMutation( + () => createListIndexWithOptionalSignal({ http }), + { + onSuccess: () => { + queryClient.invalidateQueries(READ_INDEX_QUERY_KEY); + }, + onError, + } + ); + + return { + start: mutate, + loading: isLoading, + error, + }; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_create_list_item/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_create_list_item/index.ts new file mode 100644 index 000000000000..eaafc36d8733 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_create_list_item/index.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { UseMutationOptions } from '@tanstack/react-query'; +import type { ListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { useMutation } from '@tanstack/react-query'; +import { createListItem } from '@kbn/securitysolution-list-api'; +import type { CreateListItemParams } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; + +import { useInvalidateListItemQuery } from '../use_find_list_items'; + +const createListItemWithOptionalSignal = withOptionalSignal(createListItem); + +export const CREATE_LIST_ITEM_MUTATION_KEY = ['POST', 'LIST_ITEM_CREATE']; +type CreateListMutationParams = Omit<CreateListItemParams, 'refresh' | 'signal'>; + +export const useCreateListItemMutation = ( + options?: UseMutationOptions<ListItemSchema, IHttpFetchError<Error>, CreateListMutationParams> +) => { + const invalidateListItemQuery = useInvalidateListItemQuery(); + return useMutation<ListItemSchema, IHttpFetchError<Error>, CreateListMutationParams>( + ({ listId, value, http }) => + createListItemWithOptionalSignal({ listId, value, http, refresh: 'wait_for' }), + { + ...options, + mutationKey: CREATE_LIST_ITEM_MUTATION_KEY, + onSettled: (...args) => { + invalidateListItemQuery(); + if (options?.onSettled) { + options.onSettled(...args); + } + }, + } + ); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.test.ts new file mode 100644 index 000000000000..714c310f6d5e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.test.ts @@ -0,0 +1,119 @@ +/* + * 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 { renderHook, act } from '@testing-library/react'; + +import { UseCursorProps, useCursor } from '.'; + +describe('useCursor', () => { + it('returns undefined cursor if no values have been set', () => { + const { result } = renderHook((props: UseCursorProps) => useCursor(props), { + initialProps: { pageIndex: 0, pageSize: 0 }, + }); + + expect(result.current[0]).toBeUndefined(); + }); + + it('retrieves a cursor for the next page of a given page size', () => { + const { rerender, result } = renderHook((props: UseCursorProps) => useCursor(props), { + initialProps: { pageIndex: 0, pageSize: 0 }, + }); + rerender({ pageIndex: 1, pageSize: 1 }); + act(() => { + result.current[1]('new_cursor'); + }); + + expect(result.current[0]).toBeUndefined(); + + rerender({ pageIndex: 2, pageSize: 1 }); + expect(result.current[0]).toEqual('new_cursor'); + }); + + it('returns undefined cursor for an unknown search', () => { + const { rerender, result } = renderHook((props: UseCursorProps) => useCursor(props), { + initialProps: { pageIndex: 0, pageSize: 0 }, + }); + act(() => { + result.current[1]('new_cursor'); + }); + + rerender({ pageIndex: 1, pageSize: 2 }); + expect(result.current[0]).toBeUndefined(); + }); + + it('remembers cursor through rerenders', () => { + const { rerender, result } = renderHook((props: UseCursorProps) => useCursor(props), { + initialProps: { pageIndex: 0, pageSize: 0 }, + }); + + rerender({ pageIndex: 1, pageSize: 1 }); + act(() => { + result.current[1]('new_cursor'); + }); + + rerender({ pageIndex: 2, pageSize: 1 }); + expect(result.current[0]).toEqual('new_cursor'); + + rerender({ pageIndex: 0, pageSize: 0 }); + expect(result.current[0]).toBeUndefined(); + + rerender({ pageIndex: 2, pageSize: 1 }); + expect(result.current[0]).toEqual('new_cursor'); + }); + + it('remembers multiple cursors', () => { + const { rerender, result } = renderHook((props: UseCursorProps) => useCursor(props), { + initialProps: { pageIndex: 0, pageSize: 0 }, + }); + + rerender({ pageIndex: 1, pageSize: 1 }); + act(() => { + result.current[1]('new_cursor'); + }); + rerender({ pageIndex: 2, pageSize: 2 }); + act(() => { + result.current[1]('another_cursor'); + }); + + rerender({ pageIndex: 2, pageSize: 1 }); + expect(result.current[0]).toEqual('new_cursor'); + + rerender({ pageIndex: 3, pageSize: 2 }); + expect(result.current[0]).toEqual('another_cursor'); + }); + + it('returns the "nearest" cursor for the given page size', () => { + const { rerender, result } = renderHook((props: UseCursorProps) => useCursor(props), { + initialProps: { pageIndex: 0, pageSize: 0 }, + }); + + rerender({ pageIndex: 1, pageSize: 2 }); + act(() => { + result.current[1]('cursor1'); + }); + rerender({ pageIndex: 2, pageSize: 2 }); + act(() => { + result.current[1]('cursor2'); + }); + rerender({ pageIndex: 3, pageSize: 2 }); + act(() => { + result.current[1]('cursor3'); + }); + + rerender({ pageIndex: 2, pageSize: 2 }); + expect(result.current[0]).toEqual('cursor1'); + + rerender({ pageIndex: 3, pageSize: 2 }); + expect(result.current[0]).toEqual('cursor2'); + + rerender({ pageIndex: 4, pageSize: 2 }); + expect(result.current[0]).toEqual('cursor3'); + + rerender({ pageIndex: 6, pageSize: 2 }); + expect(result.current[0]).toEqual('cursor3'); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.ts new file mode 100644 index 000000000000..1bc85c63363c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.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 { useCallback, useState } from 'react'; + +export interface UseCursorProps { + pageIndex: number; + pageSize: number; +} +type Cursor = string | undefined; +type SetCursor = (cursor: Cursor) => void; +type UseCursor = (props: UseCursorProps) => [Cursor, SetCursor]; + +const hash = (props: UseCursorProps): string => JSON.stringify(props); + +export const useCursor: UseCursor = ({ pageIndex, pageSize }) => { + const [cache, setCache] = useState<Record<string, Cursor>>({}); + + const setCursor = useCallback<SetCursor>( + (cursor) => { + setCache({ + ...cache, + [hash({ pageIndex: pageIndex + 1, pageSize })]: cursor, + }); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [pageIndex, pageSize] + ); + + let cursor: Cursor; + for (let i = pageIndex; i >= 0; i--) { + const currentProps = { pageIndex: i, pageSize }; + cursor = cache[hash(currentProps)]; + if (cursor) { + break; + } + } + + return [cursor, setCursor]; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.ts new file mode 100644 index 000000000000..ae427947ab66 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.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. + */ + +import { deleteList } from '@kbn/securitysolution-list-api'; +import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; + +const deleteListWithOptionalSignal = withOptionalSignal(deleteList); + +export const useDeleteList = () => useAsync(deleteListWithOptionalSignal); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_delete_list_item/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_delete_list_item/index.ts new file mode 100644 index 000000000000..04c608f9c841 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_delete_list_item/index.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { UseMutationOptions } from '@tanstack/react-query'; +import type { ListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { useMutation } from '@tanstack/react-query'; +import { deleteListItem } from '@kbn/securitysolution-list-api'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import type { DeleteListItemParams } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; +import { useInvalidateListItemQuery } from '../use_find_list_items'; + +const deleteListItemWithOptionalSignal = withOptionalSignal(deleteListItem); + +export const DELETE_LIST_ITEM_MUTATION_KEY = ['POST', ' DELETE_LIST_ITEM_MUTATION']; +type DeleteListMutationParams = Omit<DeleteListItemParams, 'refresh' | 'signal'>; + +export const useDeleteListItemMutation = ( + options?: UseMutationOptions<ListItemSchema, IHttpFetchError<Error>, DeleteListMutationParams> +) => { + const invalidateListItemQuery = useInvalidateListItemQuery(); + return useMutation<ListItemSchema, IHttpFetchError<Error>, DeleteListMutationParams>( + ({ id, http }) => deleteListItemWithOptionalSignal({ id, http }), + { + ...options, + mutationKey: DELETE_LIST_ITEM_MUTATION_KEY, + onSettled: (...args) => { + invalidateListItemQuery(); + if (options?.onSettled) { + options.onSettled(...args); + } + }, + } + ); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.ts new file mode 100644 index 000000000000..8a8e2aadf258 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import type { + ExceptionListSchema, + UseExceptionListsProps, + Pagination, + Sort, +} from '@kbn/securitysolution-io-ts-list-types'; +import { fetchExceptionLists } from '@kbn/securitysolution-list-api'; + +import { getFilters } from '@kbn/securitysolution-list-utils'; + +export type Func = () => void; +export type ReturnExceptionLists = [ + loading: boolean, + exceptionLists: ExceptionListSchema[], + pagination: Pagination, + setPagination: React.Dispatch<React.SetStateAction<Pagination>>, + fetchLists: Func | null, + sort: Sort, + setSort: React.Dispatch<React.SetStateAction<Sort>> +]; + +const DEFAULT_PAGINATION = { + page: 1, + perPage: 20, + total: 0, +}; + +const DEFAULT_SORT = { + field: 'created_at', + order: 'desc', +}; + +/** + * Hook for fetching ExceptionLists + * + * @param http Kibana http service + * @param errorMessage message shown to user if error occurs + * @param filterOptions filter by certain fields + * @param namespaceTypes spaces to be searched + * @param notifications kibana service for displaying toasters + * @param hideLists a list of listIds we don't want to query + * @param initialPagination + * + */ +export const useExceptionLists = ({ + errorMessage, + http, + initialPagination = DEFAULT_PAGINATION, + filterOptions = {}, + namespaceTypes, + notifications, + hideLists = [], + initialSort = DEFAULT_SORT, +}: UseExceptionListsProps): ReturnExceptionLists => { + const [exceptionLists, setExceptionLists] = useState<ExceptionListSchema[]>([]); + const [pagination, setPagination] = useState<Pagination>(initialPagination); + const [sort, setSort] = useState<Sort>(initialSort); + const [loading, setLoading] = useState(true); + const abortCtrlRef = useRef<AbortController>(); + + const namespaceTypesAsString = useMemo(() => namespaceTypes.join(','), [namespaceTypes]); + const filters = useMemo( + (): string => + getFilters({ + filters: filterOptions, + namespaceTypes, + hideLists, + }), + [namespaceTypes, filterOptions, hideLists] + ); + + const fetchData = useCallback(async (): Promise<void> => { + try { + setLoading(true); + + abortCtrlRef.current = new AbortController(); + + const { + page, + per_page: perPage, + total, + data, + } = await fetchExceptionLists({ + filters, + http, + namespaceTypes: namespaceTypesAsString, + pagination: { + page: pagination.page, + perPage: pagination.perPage, + }, + sort, + signal: abortCtrlRef.current.signal, + }); + + setPagination({ + page, + perPage, + total, + }); + setExceptionLists(data); + setLoading(false); + } catch (error) { + if (error.name !== 'AbortError') { + notifications.toasts.addError(error, { + title: errorMessage, + }); + setExceptionLists([]); + setPagination(DEFAULT_PAGINATION); + setLoading(false); + } + } + }, [ + errorMessage, + filters, + http, + namespaceTypesAsString, + notifications.toasts, + pagination.page, + pagination.perPage, + sort, + ]); + + useEffect(() => { + fetchData(); + + return (): void => { + abortCtrlRef.current?.abort(); + }; + }, [fetchData]); + + return [loading, exceptionLists, pagination, setPagination, fetchData, sort, setSort]; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.ts new file mode 100644 index 000000000000..bdaec5e50361 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.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. + */ + +import { exportList } from '@kbn/securitysolution-list-api'; +import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; + +const exportListWithOptionalSignal = withOptionalSignal(exportList); + +export const useExportList = () => useAsync(exportListWithOptionalSignal); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_list_items/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_list_items/index.ts new file mode 100644 index 000000000000..86ed9892fa95 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_list_items/index.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback } from 'react'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { findListItems, ApiParams } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; +import { useCursor } from '../use_cursor'; + +const findListItemsWithOptionalSignal = withOptionalSignal(findListItems); + +const FIND_LIST_ITEMS_QUERY_KEY = 'FIND_LIST_ITEMS'; + +export const useInvalidateListItemQuery = () => { + const queryClient = useQueryClient(); + + return useCallback(() => { + queryClient.invalidateQueries([FIND_LIST_ITEMS_QUERY_KEY], { + refetchType: 'active', + }); + }, [queryClient]); +}; + +export const useFindListItems = ({ + pageIndex, + pageSize, + sortField, + sortOrder, + listId, + filter, + http, +}: { + pageIndex: number; + pageSize: number; + sortField: string; + sortOrder: 'asc' | 'desc'; + listId: string; + filter: string; + http: ApiParams['http']; +}) => { + const [cursor, setCursor] = useCursor({ pageIndex, pageSize }); + return useQuery( + [FIND_LIST_ITEMS_QUERY_KEY, pageIndex, pageSize, sortField, sortOrder, listId, filter], + async ({ signal }) => { + const response = await findListItemsWithOptionalSignal({ + http, + signal, + pageIndex, + pageSize, + sortField, + sortOrder, + listId, + cursor, + filter, + }); + return response; + }, + { + keepPreviousData: true, + refetchOnWindowFocus: false, + retry: false, + onSuccess: (data) => { + if (data?.cursor) { + setCursor(data?.cursor); + } + }, + } + ); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.test.ts new file mode 100644 index 000000000000..0c60b3ebebb3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.test.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { waitFor, renderHook, act } from '@testing-library/react'; + +import { useFindLists } from '.'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import * as Api from '@kbn/securitysolution-list-api'; + +import { getFoundListSchemaMock } from '../mocks/response/found_list_schema.mock'; + +jest.mock('@kbn/securitysolution-list-api'); + +describe('useFindLists', () => { + let httpMock: ReturnType<typeof httpServiceMock.createStartContract>; + + beforeEach(() => { + httpMock = httpServiceMock.createStartContract(); + (Api.findLists as jest.Mock).mockResolvedValue(getFoundListSchemaMock()); + }); + + it('invokes Api.findLists', async () => { + const { result } = renderHook(() => useFindLists()); + act(() => { + result.current.start({ http: httpMock, pageIndex: 1, pageSize: 10 }); + }); + await waitFor(() => + expect(Api.findLists).toHaveBeenCalledWith( + expect.objectContaining({ http: httpMock, pageIndex: 1, pageSize: 10 }) + ) + ); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.ts new file mode 100644 index 000000000000..0ad30728be4a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.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. + */ + +import { findLists } from '@kbn/securitysolution-list-api'; +import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; + +const findListsWithOptionalSignal = withOptionalSignal(findLists); + +export const useFindLists = () => useAsync(findListsWithOptionalSignal); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_lists_by_size/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_lists_by_size/index.ts new file mode 100644 index 000000000000..e1132bdaab29 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_find_lists_by_size/index.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. + */ + +import { findListsBySize } from '@kbn/securitysolution-list-api'; +import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; + +const findListsBySizeWithOptionalSignal = withOptionalSignal(findListsBySize); + +export const useFindListsBySize = () => useAsync(findListsBySizeWithOptionalSignal); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_get_list_by_id/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_get_list_by_id/index.ts new file mode 100644 index 000000000000..394b48352a32 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_get_list_by_id/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useQuery } from '@tanstack/react-query'; +import { getListById, ApiParams } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; + +const getListByIdWithOptionalSignal = withOptionalSignal(getListById); + +const GET_LIST_BY_ID_QUERY_KEY = 'GET_LIST_BY_ID'; +export const useGetListById = ({ http, id }: { http: ApiParams['http']; id: string }) => { + return useQuery( + [GET_LIST_BY_ID_QUERY_KEY, id], + async ({ signal }) => { + const respone = await getListByIdWithOptionalSignal({ http, signal, id }); + return respone; + }, + { + refetchOnWindowFocus: false, + } + ); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts new file mode 100644 index 000000000000..4499b7220128 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.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. + */ + +import { importList } from '@kbn/securitysolution-list-api'; +import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; + +const importListWithOptionalSignal = withOptionalSignal(importList); + +export const useImportList = () => useAsync(importListWithOptionalSignal); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_patch_list_item/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_patch_list_item/index.ts new file mode 100644 index 000000000000..e321006a0b68 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_patch_list_item/index.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { UseMutationOptions } from '@tanstack/react-query'; +import type { ListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { useMutation } from '@tanstack/react-query'; +import type { PatchListItemParams } from '@kbn/securitysolution-list-api'; +import { patchListItem } from '@kbn/securitysolution-list-api'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; +import { useInvalidateListItemQuery } from '../use_find_list_items'; + +const patchListItemWithOptionalSignal = withOptionalSignal(patchListItem); + +export const PATCH_LIST_ITEM_MUTATION_KEY = ['PATCH', 'LIST_ITEM_MUTATION']; +type PatchListMutationParams = Omit<PatchListItemParams, 'refresh' | 'signal'>; + +export const usePatchListItemMutation = ( + options?: UseMutationOptions<ListItemSchema, IHttpFetchError<Error>, PatchListMutationParams> +) => { + const invalidateListItemQuery = useInvalidateListItemQuery(); + return useMutation<ListItemSchema, IHttpFetchError<Error>, PatchListMutationParams>( + ({ id, value, _version, http }: PatchListMutationParams) => + patchListItemWithOptionalSignal({ id, value, http, refresh: 'true', _version }), + { + ...options, + mutationKey: PATCH_LIST_ITEM_MUTATION_KEY, + onSettled: (...args) => { + invalidateListItemQuery(); + if (options?.onSettled) { + options.onSettled(...args); + } + }, + } + ); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.ts new file mode 100644 index 000000000000..ad239016ed05 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.ts @@ -0,0 +1,109 @@ +/* + * 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 { Dispatch, useEffect, useRef, useState } from 'react'; +import type { + CreateExceptionListItemSchema, + PersistHookProps, + UpdateExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { addExceptionListItem, updateExceptionListItem } from '@kbn/securitysolution-list-api'; + +import { transformNewItemOutput, transformOutput } from '../transforms'; + +interface PersistReturnExceptionItem { + isLoading: boolean; + isSaved: boolean; +} + +export type ReturnPersistExceptionItem = [ + PersistReturnExceptionItem, + Dispatch<CreateExceptionListItemSchema | UpdateExceptionListItemSchema | null> +]; + +// TODO: Add this to @kbn/securitysolution-list-hooks + +/** + * Hook for creating or updating ExceptionListItem + * + * @param http Kibana http service + * @param onError error callback + * + */ +export const usePersistExceptionItem = ({ + http, + onError, +}: PersistHookProps): ReturnPersistExceptionItem => { + const [exceptionListItem, setExceptionItem] = useState< + CreateExceptionListItemSchema | UpdateExceptionListItemSchema | null + >(null); + const [isSaved, setIsSaved] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const isUpdateExceptionItem = (item: unknown): item is UpdateExceptionListItemSchema => + Boolean(item && (item as UpdateExceptionListItemSchema).id != null); + const isSubscribed = useRef(false); + + useEffect(() => { + let abortCtrl: AbortController | null = null; + isSubscribed.current = true; + setIsSaved(false); + + const saveExceptionItem = async (): Promise<void> => { + if (exceptionListItem === null) { + return; + } + + try { + abortCtrl = new AbortController(); + setIsLoading(true); + + if (isUpdateExceptionItem(exceptionListItem)) { + // Please see `x-pack/solutions/security/plugins/lists/public/exceptions/transforms.ts` doc notes + // for context around the temporary `id` + const transformedList = transformOutput(exceptionListItem); + + await updateExceptionListItem({ + http, + listItem: transformedList, + signal: abortCtrl.signal, + }); + } else { + // Please see `x-pack/solutions/security/plugins/lists/public/exceptions/transforms.ts` doc notes + // for context around the temporary `id` + const transformedList = transformNewItemOutput(exceptionListItem); + + await addExceptionListItem({ + http, + listItem: transformedList, + signal: abortCtrl.signal, + }); + } + + if (isSubscribed.current) { + setIsSaved(true); + } + } catch (error) { + if (isSubscribed.current) { + onError(error); + } + } finally { + if (isSubscribed.current) { + setIsLoading(false); + } + } + }; + + saveExceptionItem(); + + return (): void => { + isSubscribed.current = false; + abortCtrl?.abort(); + }; + }, [http, exceptionListItem, onError]); + + return [{ isLoading, isSaved }, setExceptionItem]; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.ts new file mode 100644 index 000000000000..6814a19679c5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.ts @@ -0,0 +1,79 @@ +/* + * 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 { Dispatch, useEffect, useState } from 'react'; +import type { + AddExceptionList, + PersistHookProps, + UpdateExceptionListSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { addExceptionList, updateExceptionList } from '@kbn/securitysolution-list-api'; + +interface PersistReturnExceptionList { + isLoading: boolean; + isSaved: boolean; +} + +export type ReturnPersistExceptionList = [ + PersistReturnExceptionList, + Dispatch<AddExceptionList | null> +]; + +/** + * Hook for creating or updating ExceptionList + * + * @param http Kibana http service + * @param onError error callback + * + */ +export const usePersistExceptionList = ({ + http, + onError, +}: PersistHookProps): ReturnPersistExceptionList => { + const [exceptionList, setExceptionList] = useState<AddExceptionList | null>(null); + const [isSaved, setIsSaved] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const isUpdateExceptionList = (item: unknown): item is UpdateExceptionListSchema => + Boolean(item && (item as UpdateExceptionListSchema).id != null); + + useEffect(() => { + let isSubscribed = true; + const abortCtrl = new AbortController(); + setIsSaved(false); + + const saveExceptionList = async (): Promise<void> => { + if (exceptionList != null) { + try { + setIsLoading(true); + if (isUpdateExceptionList(exceptionList)) { + await updateExceptionList({ http, list: exceptionList, signal: abortCtrl.signal }); + } else { + await addExceptionList({ http, list: exceptionList, signal: abortCtrl.signal }); + } + if (isSubscribed) { + setIsSaved(true); + } + } catch (error) { + if (isSubscribed) { + onError(error); + } + } + if (isSubscribed) { + setIsLoading(false); + } + } + }; + + saveExceptionList(); + return (): void => { + isSubscribed = false; + abortCtrl.abort(); + }; + }, [http, exceptionList, onError]); + + return [{ isLoading, isSaved }, setExceptionList]; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.ts new file mode 100644 index 000000000000..2bb2a7018857 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.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 { useQuery } from '@tanstack/react-query'; + +import { readListIndex, ApiParams } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '@kbn/securitysolution-hook-utils'; + +import { READ_INDEX_QUERY_KEY } from '../constants'; + +const readListIndexWithOptionalSignal = withOptionalSignal(readListIndex); + +export const useReadListIndex = ({ + http, + isEnabled, + onError, +}: { + isEnabled: boolean; + http: ApiParams['http']; + onError?: (err: unknown) => void; +}) => { + const query = useQuery( + READ_INDEX_QUERY_KEY, + async ({ signal }) => { + if (!isEnabled) { + return null; + } + + return readListIndexWithOptionalSignal({ http, signal }); + }, + { + onError, + retry: false, + refetchOnWindowFocus: false, + enabled: isEnabled, + staleTime: Infinity, + } + ); + + return { + result: query.data, + loading: query.isFetching, + error: query.error, + }; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_read_list_privileges/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_read_list_privileges/index.ts new file mode 100644 index 000000000000..1e96118228b5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_read_list_privileges/index.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. + */ + +import { readListPrivileges } from '@kbn/securitysolution-list-api'; +import { useAsync, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; + +const readListPrivilegesWithOptionalSignal = withOptionalSignal(readListPrivileges); + +export const useReadListPrivileges = () => useAsync(readListPrivilegesWithOptionalSignal); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/tsconfig.json new file mode 100644 index 000000000000..f2583861bced --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "kbn_references": [ + "@kbn/securitysolution-hook-utils", + "@kbn/securitysolution-io-ts-list-types", + "@kbn/securitysolution-list-api", + "@kbn/securitysolution-list-utils", + "@kbn/securitysolution-utils", + "@kbn/core-http-browser-mocks", + "@kbn/core-http-browser", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-securitysolution-list-utils/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/README.md similarity index 100% rename from packages/kbn-securitysolution-list-utils/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-list-utils/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/index.ts new file mode 100644 index 000000000000..cd6928dd89e3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './src/autocomplete_operators'; +export * from './src/get_exception_list_type'; +export * from './src/get_filters'; +export * from './src/get_general_filters'; +export * from './src/get_ids_and_namespaces'; +export * from './src/get_saved_object_type'; +export * from './src/get_saved_object_types'; +export * from './src/has_large_value_list'; +export * from './src/helpers'; +export * from './src/types'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/jest.config.js b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/jest.config.js new file mode 100644 index 000000000000..e3fc2abb342a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-list-utils'], +}; diff --git a/packages/kbn-securitysolution-list-utils/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-list-utils/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-list-utils/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/package.json new file mode 100644 index 000000000000..585acd8e3d70 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/securitysolution-list-utils", + "version": "1.0.0", + "description": "security solution list utilities", + "license": "Elastic License 2.0", + "private": true, + "sideEffects": false +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts new file mode 100644 index 000000000000..430a0dbc3a1d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts @@ -0,0 +1,158 @@ +/* + * 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 { + ListOperatorEnum as OperatorEnum, + ListOperatorTypeEnum as OperatorTypeEnum, +} from '@kbn/securitysolution-io-ts-list-types'; +import { OperatorOption } from '../types'; + +export const isOperator: OperatorOption = { + message: i18n.translate('lists.exceptions.isOperatorLabel', { + defaultMessage: 'is', + }), + operator: OperatorEnum.INCLUDED, + type: OperatorTypeEnum.MATCH, + value: 'is', +}; + +export const isNotOperator: OperatorOption = { + message: i18n.translate('lists.exceptions.isNotOperatorLabel', { + defaultMessage: 'is not', + }), + operator: OperatorEnum.EXCLUDED, + type: OperatorTypeEnum.MATCH, + value: 'is_not', +}; + +export const isOneOfOperator: OperatorOption = { + message: i18n.translate('lists.exceptions.isOneOfOperatorLabel', { + defaultMessage: 'is one of', + }), + operator: OperatorEnum.INCLUDED, + type: OperatorTypeEnum.MATCH_ANY, + value: 'is_one_of', +}; + +export const isNotOneOfOperator: OperatorOption = { + message: i18n.translate('lists.exceptions.isNotOneOfOperatorLabel', { + defaultMessage: 'is not one of', + }), + operator: OperatorEnum.EXCLUDED, + type: OperatorTypeEnum.MATCH_ANY, + value: 'is_not_one_of', +}; + +export const existsOperator: OperatorOption = { + message: i18n.translate('lists.exceptions.existsOperatorLabel', { + defaultMessage: 'exists', + }), + operator: OperatorEnum.INCLUDED, + type: OperatorTypeEnum.EXISTS, + value: 'exists', +}; + +export const doesNotExistOperator: OperatorOption = { + message: i18n.translate('lists.exceptions.doesNotExistOperatorLabel', { + defaultMessage: 'does not exist', + }), + operator: OperatorEnum.EXCLUDED, + type: OperatorTypeEnum.EXISTS, + value: 'does_not_exist', +}; + +export const isInListOperator: OperatorOption = { + message: i18n.translate('lists.exceptions.isInListOperatorLabel', { + defaultMessage: 'is in list', + }), + operator: OperatorEnum.INCLUDED, + type: OperatorTypeEnum.LIST, + value: 'is_in_list', +}; + +export const isNotInListOperator: OperatorOption = { + message: i18n.translate('lists.exceptions.isNotInListOperatorLabel', { + defaultMessage: 'is not in list', + }), + operator: OperatorEnum.EXCLUDED, + type: OperatorTypeEnum.LIST, + value: 'is_not_in_list', +}; + +export const matchesOperator: OperatorOption = { + message: i18n.translate('lists.exceptions.matchesOperatorLabel', { + defaultMessage: 'matches', + }), + operator: OperatorEnum.INCLUDED, + type: OperatorTypeEnum.WILDCARD, + value: 'matches', +}; + +export const doesNotMatchOperator: OperatorOption = { + message: i18n.translate('lists.exceptions.doesNotMatchOperatorLabel', { + defaultMessage: 'does not match', + }), + operator: OperatorEnum.EXCLUDED, + type: OperatorTypeEnum.WILDCARD, + value: 'does_not_match', +}; + +export const EVENT_FILTERS_OPERATORS: OperatorOption[] = [ + isOperator, + isNotOperator, + isOneOfOperator, + isNotOneOfOperator, + matchesOperator, + doesNotMatchOperator, +]; + +/* + * !IMPORTANT! - Please only add to this list if it is an operator + * supported by the detection engine. + */ +export const DETECTION_ENGINE_EXCEPTION_OPERATORS: OperatorOption[] = [ + isOperator, + isNotOperator, + isOneOfOperator, + isNotOneOfOperator, + existsOperator, + doesNotExistOperator, + isInListOperator, + isNotInListOperator, + matchesOperator, + doesNotMatchOperator, +]; + +export const ALL_OPERATORS: OperatorOption[] = [ + isOperator, + isNotOperator, + isOneOfOperator, + isNotOneOfOperator, + existsOperator, + doesNotExistOperator, + isInListOperator, + isNotInListOperator, + matchesOperator, + doesNotMatchOperator, +]; + +export const EXCEPTION_OPERATORS_SANS_LISTS: OperatorOption[] = [ + isOperator, + isNotOperator, + isOneOfOperator, + isNotOneOfOperator, + existsOperator, + doesNotExistOperator, + matchesOperator, + doesNotMatchOperator, +]; + +export const EXCEPTION_OPERATORS_ONLY_LISTS: OperatorOption[] = [ + isInListOperator, + isNotInListOperator, +]; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_exception_list_type/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_exception_list_type/index.ts new file mode 100644 index 000000000000..29dba94f7676 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_exception_list_type/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; + +import { exceptionListAgnosticSavedObjectType } from '../types'; + +export const getExceptionListType = ({ + savedObjectType, +}: { + savedObjectType: string; +}): NamespaceType => { + if (savedObjectType === exceptionListAgnosticSavedObjectType) { + return 'agnostic'; + } else { + return 'single'; + } +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_filters/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_filters/index.test.ts new file mode 100644 index 000000000000..a88a78479cf5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_filters/index.test.ts @@ -0,0 +1,207 @@ +/* + * 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 { getFilters } from '.'; + +describe('getFilters', () => { + describe('single', () => { + test('it properly formats when no filters and hide lists contains few list ids', () => { + const filter = getFilters({ + filters: {}, + namespaceTypes: ['single'], + hideLists: ['listId-1', 'listId-2', 'listId-3'], + }); + + expect(filter).toEqual( + '(not exception-list.attributes.list_id: listId-1*) AND (not exception-list.attributes.list_id: listId-2*) AND (not exception-list.attributes.list_id: listId-3*)' + ); + }); + test('it properly formats when no filters and hide lists contains one list id', () => { + const filter = getFilters({ + filters: {}, + namespaceTypes: ['single'], + hideLists: ['listId-1'], + }); + + expect(filter).toEqual('(not exception-list.attributes.list_id: listId-1*)'); + }); + test('it properly formats when no filters and no hide lists', () => { + const filter = getFilters({ + filters: {}, + namespaceTypes: ['single'], + hideLists: [], + }); + + expect(filter).toEqual(''); + }); + test('it properly formats when filters passed and hide lists contains few list ids', () => { + const filter = getFilters({ + filters: { created_by: 'moi', name: 'Sample' }, + namespaceTypes: ['single'], + hideLists: ['listId-1', 'listId-2', 'listId-3'], + }); + + expect(filter).toEqual( + '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample) AND (not exception-list.attributes.list_id: listId-1*) AND (not exception-list.attributes.list_id: listId-2*) AND (not exception-list.attributes.list_id: listId-3*)' + ); + }); + test('it properly formats when filters passed and hide lists contains one list id', () => { + const filter = getFilters({ + filters: { created_by: 'moi', name: 'Sample' }, + namespaceTypes: ['single'], + hideLists: ['listId-1'], + }); + + expect(filter).toEqual( + '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample) AND (not exception-list.attributes.list_id: listId-1*)' + ); + }); + test('it properly formats when filters passed and no hide lists', () => { + const filter = getFilters({ + filters: { created_by: 'moi', name: 'Sample' }, + namespaceTypes: ['single'], + hideLists: [], + }); + + expect(filter).toEqual( + '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample)' + ); + }); + }); + + describe('agnostic', () => { + test('it properly formats when no filters and hide lists contains few list ids', () => { + const filter = getFilters({ + filters: {}, + namespaceTypes: ['agnostic'], + hideLists: ['listId-1', 'listId-2', 'listId-3'], + }); + + expect(filter).toEqual( + '(not exception-list-agnostic.attributes.list_id: listId-1*) AND (not exception-list-agnostic.attributes.list_id: listId-2*) AND (not exception-list-agnostic.attributes.list_id: listId-3*)' + ); + }); + test('it properly formats when no filters and hide lists contains one list id', () => { + const filter = getFilters({ + filters: {}, + namespaceTypes: ['agnostic'], + hideLists: ['listId-1'], + }); + + expect(filter).toEqual('(not exception-list-agnostic.attributes.list_id: listId-1*)'); + }); + test('it properly formats when no filters and no hide lists', () => { + const filter = getFilters({ + filters: {}, + namespaceTypes: ['agnostic'], + hideLists: [], + }); + + expect(filter).toEqual(''); + }); + test('it properly formats when filters passed and hide lists contains few list ids', () => { + const filter = getFilters({ + filters: { created_by: 'moi', name: 'Sample' }, + namespaceTypes: ['agnostic'], + hideLists: ['listId-1', 'listId-2', 'listId-3'], + }); + + expect(filter).toEqual( + '(exception-list-agnostic.attributes.created_by:moi) AND (exception-list-agnostic.attributes.name.text:Sample) AND (not exception-list-agnostic.attributes.list_id: listId-1*) AND (not exception-list-agnostic.attributes.list_id: listId-2*) AND (not exception-list-agnostic.attributes.list_id: listId-3*)' + ); + }); + test('it properly formats when filters passed and hide lists contains one list id', () => { + const filter = getFilters({ + filters: { created_by: 'moi', name: 'Sample' }, + namespaceTypes: ['agnostic'], + hideLists: ['listId-1'], + }); + + expect(filter).toEqual( + '(exception-list-agnostic.attributes.created_by:moi) AND (exception-list-agnostic.attributes.name.text:Sample) AND (not exception-list-agnostic.attributes.list_id: listId-1*)' + ); + }); + test('it properly formats when filters passed and no hide lists', () => { + const filter = getFilters({ + filters: { created_by: 'moi', name: 'Sample' }, + namespaceTypes: ['agnostic'], + hideLists: [], + }); + + expect(filter).toEqual( + '(exception-list-agnostic.attributes.created_by:moi) AND (exception-list-agnostic.attributes.name.text:Sample)' + ); + }); + }); + + describe('single, agnostic', () => { + test('it properly formats when no filters and hide lists contains few list ids', () => { + const filter = getFilters({ + filters: {}, + namespaceTypes: ['single', 'agnostic'], + hideLists: ['listId-1', 'listId-2', 'listId-3'], + }); + + expect(filter).toEqual( + '(not exception-list.attributes.list_id: listId-1* AND not exception-list-agnostic.attributes.list_id: listId-1*) AND (not exception-list.attributes.list_id: listId-2* AND not exception-list-agnostic.attributes.list_id: listId-2*) AND (not exception-list.attributes.list_id: listId-3* AND not exception-list-agnostic.attributes.list_id: listId-3*)' + ); + }); + test('it properly formats when no filters and hide lists contains one list id', () => { + const filter = getFilters({ + filters: {}, + namespaceTypes: ['single', 'agnostic'], + hideLists: ['listId-1'], + }); + + expect(filter).toEqual( + '(not exception-list.attributes.list_id: listId-1* AND not exception-list-agnostic.attributes.list_id: listId-1*)' + ); + }); + test('it properly formats when no filters and no hide lists', () => { + const filter = getFilters({ + filters: {}, + namespaceTypes: ['single', 'agnostic'], + hideLists: [], + }); + + expect(filter).toEqual(''); + }); + test('it properly formats when filters passed and hide lists contains few list ids', () => { + const filter = getFilters({ + filters: { created_by: 'moi', name: 'Sample' }, + namespaceTypes: ['single', 'agnostic'], + hideLists: ['listId-1', 'listId-2', 'listId-3'], + }); + + expect(filter).toEqual( + '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample) AND (not exception-list.attributes.list_id: listId-1* AND not exception-list-agnostic.attributes.list_id: listId-1*) AND (not exception-list.attributes.list_id: listId-2* AND not exception-list-agnostic.attributes.list_id: listId-2*) AND (not exception-list.attributes.list_id: listId-3* AND not exception-list-agnostic.attributes.list_id: listId-3*)' + ); + }); + test('it properly formats when filters passed and hide lists contains one list id', () => { + const filter = getFilters({ + filters: { created_by: 'moi', name: 'Sample' }, + namespaceTypes: ['single', 'agnostic'], + hideLists: ['listId-1'], + }); + + expect(filter).toEqual( + '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample) AND (not exception-list.attributes.list_id: listId-1* AND not exception-list-agnostic.attributes.list_id: listId-1*)' + ); + }); + test('it properly formats when filters passed and no hide lists', () => { + const filter = getFilters({ + filters: { created_by: 'moi', name: 'Sample' }, + namespaceTypes: ['single', 'agnostic'], + hideLists: [], + }); + + expect(filter).toEqual( + '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample)' + ); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_filters/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_filters/index.ts new file mode 100644 index 000000000000..2bc7960de792 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_filters/index.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 { ExceptionListFilter, NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; +import { getGeneralFilters } from '../get_general_filters'; +import { getSavedObjectTypes } from '../get_saved_object_types'; +export interface GetFiltersParams { + filters: ExceptionListFilter; + namespaceTypes: NamespaceType[]; + hideLists: readonly string[]; +} + +export const getFilters = ({ filters, namespaceTypes, hideLists }: GetFiltersParams): string => { + const namespaces = getSavedObjectTypes({ namespaceType: namespaceTypes }); + const generalFilters = getGeneralFilters(filters, namespaces); + const hideListsFilters = hideLists.map((listId) => { + const filtersByNamespace = namespaces.map((namespace) => { + return `not ${namespace}.attributes.list_id: ${listId}*`; + }); + return `(${filtersByNamespace.join(' AND ')})`; + }); + + return [generalFilters, ...hideListsFilters] + .filter((filter) => filter.trim() !== '') + .join(' AND '); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.test.ts new file mode 100644 index 000000000000..026202349763 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.test.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { getGeneralFilters } from '.'; + +describe('getGeneralFilters', () => { + test('it returns empty string if no filters', () => { + const filters = getGeneralFilters({}, ['exception-list']); + + expect(filters).toEqual(''); + }); + + test('it properly formats filters when one namespace type passed in', () => { + const filters = getGeneralFilters({ created_by: 'moi', name: 'Sample' }, ['exception-list']); + + expect(filters).toEqual( + '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample)' + ); + }); + + test('it properly formats filters when two namespace types passed in', () => { + const filters = getGeneralFilters({ created_by: 'moi', name: 'Sample' }, [ + 'exception-list', + 'exception-list-agnostic', + ]); + + expect(filters).toEqual( + '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample)' + ); + }); + + test('it properly formats filters when two types are passed in', () => { + const filters = getGeneralFilters( + { + created_by: 'moi', + name: 'Sample', + types: [ExceptionListTypeEnum.DETECTION, ExceptionListTypeEnum.RULE_DEFAULT], + }, + ['exception-list', 'exception-list-agnostic'] + ); + + expect(filters).toEqual( + '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample) AND (exception-list.attributes.type:detection OR exception-list.attributes.type:rule_default OR exception-list-agnostic.attributes.type:detection OR exception-list-agnostic.attributes.type:rule_default)' + ); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.ts new file mode 100644 index 000000000000..7960617fad48 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ExceptionListFilter } from '@kbn/securitysolution-io-ts-list-types'; +import { isArray } from 'lodash'; +import { get } from 'lodash/fp'; +import { SavedObjectType } from '../types'; + +export const getGeneralFilters = ( + filters: ExceptionListFilter, + namespaceTypes: SavedObjectType[] +): string => { + return Object.keys(filters) + .map((filterKey) => { + const value = get(filterKey, filters); + if (isArray(value) || (value != null && value.trim() !== '')) { + const filtersByNamespace = namespaceTypes + .map((namespace) => { + const fieldToSearch = + filterKey === 'name' ? 'name.text' : filterKey === 'types' ? 'type' : filterKey; + return isArray(value) + ? value.map((val) => `${namespace}.attributes.${fieldToSearch}:${val}`).join(' OR ') + : `${namespace}.attributes.${fieldToSearch}:${value}`; + }) + .join(' OR '); + return `(${filtersByNamespace})`; + } else return null; + }) + .filter((item) => item != null) + .join(' AND '); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.test.ts new file mode 100644 index 000000000000..b76bc2df94ff --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getIdsAndNamespaces } from '.'; + +describe('getIdsAndNamespaces', () => { + test('it returns empty arrays if no lists found', async () => { + const output = getIdsAndNamespaces({ + lists: [], + showDetection: false, + showEndpoint: false, + }); + + expect(output).toEqual({ ids: [], namespaces: [] }); + }); + + test('it returns all lists if "showDetection" and "showEndpoint" are "false"', async () => { + const output = getIdsAndNamespaces({ + lists: [ + { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, + { + id: 'myListIdEndpoint', + listId: 'list_id_endpoint', + namespaceType: 'agnostic', + type: 'endpoint', + }, + ], + showDetection: false, + showEndpoint: false, + }); + + expect(output).toEqual({ + ids: ['list_id', 'list_id_endpoint'], + namespaces: ['single', 'agnostic'], + }); + }); + + test('it returns only detections lists if "showDetection" is "true"', async () => { + const output = getIdsAndNamespaces({ + lists: [ + { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, + { + id: 'myListIdEndpoint', + listId: 'list_id_endpoint', + namespaceType: 'agnostic', + type: 'endpoint', + }, + ], + showDetection: true, + showEndpoint: false, + }); + + expect(output).toEqual({ + ids: ['list_id'], + namespaces: ['single'], + }); + }); + + test('it returns only endpoint lists if "showEndpoint" is "true"', async () => { + const output = getIdsAndNamespaces({ + lists: [ + { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, + { + id: 'myListIdEndpoint', + listId: 'list_id_endpoint', + namespaceType: 'agnostic', + type: 'endpoint', + }, + ], + showDetection: false, + showEndpoint: true, + }); + + expect(output).toEqual({ + ids: ['list_id_endpoint'], + namespaces: ['agnostic'], + }); + }); + + test('it returns only detection lists if both "showEndpoint" and "showDetection" are "true"', async () => { + const output = getIdsAndNamespaces({ + lists: [ + { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, + { + id: 'myListIdEndpoint', + listId: 'list_id_endpoint', + namespaceType: 'agnostic', + type: 'endpoint', + }, + ], + showDetection: true, + showEndpoint: true, + }); + + expect(output).toEqual({ + ids: ['list_id'], + namespaces: ['single'], + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.ts new file mode 100644 index 000000000000..25a7ae1931e1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ExceptionListIdentifiers, NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; + +export const getIdsAndNamespaces = ({ + lists, + showDetection, + showEndpoint, +}: { + lists: ExceptionListIdentifiers[]; + showDetection: boolean; + showEndpoint: boolean; +}): { ids: string[]; namespaces: NamespaceType[] } => + lists + .filter((list) => { + if (showDetection) { + return list.type === 'detection'; + } else if (showEndpoint) { + return list.type === 'endpoint'; + } else { + return true; + } + }) + .reduce<{ ids: string[]; namespaces: NamespaceType[] }>( + (acc, { listId, namespaceType }) => ({ + ids: [...acc.ids, listId], + namespaces: [...acc.namespaces, namespaceType], + }), + { ids: [], namespaces: [] } + ); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_saved_object_type/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_saved_object_type/index.ts new file mode 100644 index 000000000000..0ee1b134333c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_saved_object_type/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; + +import { + exceptionListAgnosticSavedObjectType, + exceptionListSavedObjectType, + SavedObjectType, +} from '../types'; + +export const getSavedObjectType = ({ + namespaceType, +}: { + namespaceType: NamespaceType; +}): SavedObjectType => { + if (namespaceType === 'agnostic') { + return exceptionListAgnosticSavedObjectType; + } else { + return exceptionListSavedObjectType; + } +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_saved_object_types/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_saved_object_types/index.ts new file mode 100644 index 000000000000..92b6ff4a9263 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/get_saved_object_types/index.ts @@ -0,0 +1,21 @@ +/* + * 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 { NamespaceTypeArray } from '@kbn/securitysolution-io-ts-list-types'; + +import { SavedObjectType } from '../types'; +import { getSavedObjectType } from '../get_saved_object_type'; + +export const getSavedObjectTypes = ({ + namespaceType, +}: { + namespaceType: NamespaceTypeArray; +}): SavedObjectType[] => { + return namespaceType.map((singleNamespaceType) => + getSavedObjectType({ namespaceType: singleNamespaceType }) + ); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.ts new file mode 100644 index 000000000000..350cb581153b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.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. + */ + +import type { EntriesArray } from '@kbn/securitysolution-io-ts-list-types'; + +export const hasLargeValueList = (entries: EntriesArray): boolean => { + const found = entries.filter(({ type }) => type === 'list'); + return found.length > 0; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts new file mode 100644 index 000000000000..fe75ad88e012 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts @@ -0,0 +1,375 @@ +/* + * 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 { + getMappingConflictsInfo, + fieldSupportsMatches, + hasWrongOperatorWithWildcard, + hasPartialCodeSignatureEntry, +} from '.'; + +describe('Helpers', () => { + describe('getMappingConflictsInfo', () => { + test('it return null if there are not conflicts', () => { + const field = { + name: 'field1', + type: 'string', + }; + const conflictsInfo = getMappingConflictsInfo(field); + + expect(conflictsInfo).toBeNull(); + }); + test('it groups ".ds-" data stream indices', () => { + const field = { + name: 'field1', + type: 'conflict', + conflictDescriptions: { + text: [ + '.ds-logs-default-2023.01.18-000001', + '.ds-logs-default-2023.01.18-000002', + '.ds-logs-tortilla.process-default-2022.11.20-000011', + '.ds-logs-tortilla.process-default-2022.11.20-000012', + '.ds-logs-tortilla.process-default-2022.11.20-000016', + ], + long: [ + '.ds-logs-default-2023.01.18-000004', + '.ds-logs-default-2023.01.18-000005', + 'partial-.ds-logs-gcp.audit-2021.12.22-000240', + 'partial-.ds-logs-gcp.audit-2021.12.22-000242', + ], + }, + }; + const conflictsInfo = getMappingConflictsInfo(field); + + expect(conflictsInfo).toEqual([ + { + type: 'text', + totalIndexCount: 5, + groupedIndices: [ + { name: 'logs-tortilla.process-default', count: 3 }, + { name: 'logs-default', count: 2 }, + ], + }, + { + type: 'long', + totalIndexCount: 4, + groupedIndices: [ + { name: 'logs-default', count: 2 }, + { name: 'logs-gcp.audit', count: 2 }, + ], + }, + ]); + }); + test('it groups old ".siem-" indices', () => { + const field = { + name: 'field1', + type: 'conflict', + conflictDescriptions: { + text: [ + '.siem-signals-default-000001', + '.siem-signals-default-000002', + '.siem-signals-default-000011', + '.siem-signals-default-000012', + ], + unmapped: [ + '.siem-signals-default-000004', + '.siem-signals-default-000005', + '.siem-signals-default-000240', + ], + }, + }; + const conflictsInfo = getMappingConflictsInfo(field); + + expect(conflictsInfo).toEqual([ + { + type: 'text', + totalIndexCount: 4, + groupedIndices: [{ name: '.siem-signals-default', count: 4 }], + }, + { + type: 'unmapped', + totalIndexCount: 3, + groupedIndices: [{ name: '.siem-signals-default', count: 3 }], + }, + ]); + }); + test('it groups mixed indices', () => { + const field = { + name: 'field1', + type: 'conflict', + conflictDescriptions: { + boolean: [ + '.ds-logs-default-2023.01.18-000001', + '.ds-logs-tortilla.process-default-2022.11.20-000011', + '.ds-logs-tortilla.process-default-2022.11.20-000012', + '.ds-logs-tortilla.process-default-2022.11.20-000016', + '.siem-signals-default-000001', + '.siem-signals-default-000002', + '.siem-signals-default-000012', + 'my-own-index-1', + 'my-own-index-2', + ], + unmapped: [ + '.siem-signals-default-000004', + 'partial-.ds-logs-gcp.audit-2021.12.22-000240', + 'partial-.ds-logs-gcp.audit-2021.12.22-000242', + 'my-own-index-3', + ], + }, + }; + const conflictsInfo = getMappingConflictsInfo(field); + + expect(conflictsInfo).toEqual([ + { + type: 'boolean', + totalIndexCount: 9, + groupedIndices: [ + { name: 'logs-tortilla.process-default', count: 3 }, + { name: '.siem-signals-default', count: 3 }, + { name: 'logs-default', count: 1 }, + { name: 'my-own-index-1', count: 1 }, + { name: 'my-own-index-2', count: 1 }, + ], + }, + { + type: 'unmapped', + totalIndexCount: 4, + groupedIndices: [ + { name: 'logs-gcp.audit', count: 2 }, + { name: '.siem-signals-default', count: 1 }, + { name: 'my-own-index-3', count: 1 }, + ], + }, + ]); + }); + }); + + describe('fieldSupportsMatches', () => { + test('it returns true if esTypes is keyword', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword'] }) + ).toBeTruthy(); + }); + + test('it returns true if one of the esTypes is kibana type string and another is not', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword', 'object'] }) + ).toBeTruthy(); + }); + + test('it returns true if one of the esTypes is keyword', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword', 'unmapped'] }) + ).toBeTruthy(); + }); + + test('it returns true if one of the esTypes is text', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['text', 'unmapped'] }) + ).toBeTruthy(); + }); + + test('it returns true if all of the esTypes is map to kibana type string', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['text', 'keyword'] }) + ).toBeTruthy(); + }); + + test('it returns false if none of the esTypes map to kibana type string', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['bool', 'unmapped'] }) + ).toBeFalsy(); + }); + }); + describe('hasWrongOperatorWithWildcard', () => { + test('it returns true if there is at least one exception entry with a wildcard and the wrong operator', () => { + expect( + hasWrongOperatorWithWildcard([ + { + description: '', + name: '', + type: 'simple', + entries: [{ type: 'match', value: 'withwildcard*', field: '', operator: 'included' }], + }, + ]) + ).toBeTruthy(); + expect( + hasWrongOperatorWithWildcard([ + { + description: '', + name: '', + type: 'simple', + entries: [{ type: 'match', value: 'withwildcard?', field: '', operator: 'included' }], + }, + ]) + ).toBeTruthy(); + }); + + test('it returns true if there are entries joined with an OR that have a wildcard and the wrong operator', () => { + expect( + hasWrongOperatorWithWildcard([ + { + description: '', + name: '', + type: 'simple', + entries: [{ type: 'match', value: 'withwildcard?', field: '', operator: 'included' }], + }, + { + description: '', + name: '', + type: 'simple', + entries: [{ type: 'match', value: 'withwildcard?*', field: '', operator: 'included' }], + }, + ]) + ).toBeTruthy(); + }); + + test('it returns false if there are no exception entries with a wildcard and the wrong operator', () => { + expect( + hasWrongOperatorWithWildcard([ + { + description: '', + name: '', + type: 'simple', + entries: [ + { type: 'match', value: 'nowildcard', field: '', operator: 'excluded' }, + { type: 'wildcard', value: 'withwildcard*?', field: '', operator: 'included' }, + ], + }, + ]) + ).toBeFalsy(); + }); + + test('it returns true if there are nested entries with a wildcard and the wrong operator', () => { + expect( + hasWrongOperatorWithWildcard([ + { + description: '', + name: '', + type: 'simple', + entries: [ + { type: 'match', value: 'nowildcard', field: '', operator: 'excluded' }, + { + field: '', + type: 'nested', + entries: [{ type: 'match', value: 'wildcard?', field: '', operator: 'excluded' }], + }, + ], + }, + ]) + ).toBeTruthy(); + }); + }); + + describe('hasPartialCodeSignatureEntry', () => { + it('returns false if the entry has neither code signature subject name nor trusted field', () => { + expect( + hasPartialCodeSignatureEntry([ + { + description: '', + name: '', + type: 'simple', + os_types: ['windows'], + entries: [{ type: 'match', value: 'asdf', field: 'someField', operator: 'excluded' }], + }, + ]) + ).toBeFalsy(); + }); + it('returns true if the entry has code signature subject name but not trusted field', () => { + expect( + hasPartialCodeSignatureEntry([ + { + description: '', + name: '', + type: 'simple', + os_types: ['windows'], + entries: [ + { + type: 'match', + value: 'asdf', + field: 'process.code_signature.subject_name', + operator: 'excluded', + }, + ], + }, + ]) + ).toBeTruthy(); + }); + it('returns true if the entry has code signature trusted but not the subject name field', () => { + expect( + hasPartialCodeSignatureEntry([ + { + description: '', + name: '', + type: 'simple', + os_types: ['windows'], + entries: [ + { + type: 'match', + value: 'asdf', + field: 'process.code_signature.trusted', + operator: 'excluded', + }, + ], + }, + ]) + ).toBeTruthy(); + }); + it('returns false if the entry has both code signature subject name and trusted field', () => { + expect( + hasPartialCodeSignatureEntry([ + { + description: '', + name: '', + type: 'simple', + os_types: ['windows'], + entries: [ + { + type: 'match', + value: 'asdf', + field: 'process.code_signature.subject_name', + operator: 'excluded', + }, + { + type: 'match', + value: 'true', + field: 'process.code_signature.trusted', + operator: 'excluded', + }, + ], + }, + ]) + ).toBeFalsy(); + }); + it('returns false if the entry has both code signature team_id and trusted fields for mac os', () => { + expect( + hasPartialCodeSignatureEntry([ + { + description: '', + name: '', + type: 'simple', + os_types: ['macos'], + entries: [ + { + type: 'match', + value: 'asdf', + field: 'process.code_signature.team_id', + operator: 'excluded', + }, + { + type: 'match', + value: 'true', + field: 'process.code_signature.trusted', + operator: 'excluded', + }, + ], + }, + ]) + ).toBeFalsy(); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/helpers/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/helpers/index.ts new file mode 100644 index 000000000000..6cbdbe55cfeb --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/helpers/index.ts @@ -0,0 +1,1088 @@ +/* + * 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 { v4 as uuidv4 } from 'uuid'; +import { + addIdToItem, + removeIdFromItem, + validateHasWildcardWithWrongOperator, +} from '@kbn/securitysolution-utils'; +import { validate } from '@kbn/securitysolution-io-ts-utils'; +import { + CreateExceptionListItemSchema, + EntriesArray, + Entry, + EntryNested, + ExceptionListType, + ListSchema, + NamespaceType, + ListOperatorEnum as OperatorEnum, + ListOperatorTypeEnum as OperatorTypeEnum, + createExceptionListItemSchema, + entriesList, + entriesNested, + entry, + exceptionListItemSchema, + nestedEntryItem, + CreateRuleExceptionListItemSchema, + createRuleExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { + DataViewBase, + DataViewFieldBase, + getDataViewFieldSubtypeNested, + isDataViewFieldSubtypeNested, +} from '@kbn/es-query'; +import { castEsToKbnFieldTypeName, KBN_FIELD_TYPES } from '@kbn/field-types'; + +import { + ALL_OPERATORS, + EXCEPTION_OPERATORS_SANS_LISTS, + doesNotExistOperator, + existsOperator, + isNotOperator, + isOneOfOperator, + isOperator, + DETECTION_ENGINE_EXCEPTION_OPERATORS, + isNotOneOfOperator, + isInListOperator, + isNotInListOperator, + matchesOperator, + doesNotMatchOperator, +} from '../autocomplete_operators'; + +import { + BuilderEntry, + CreateExceptionListItemBuilderSchema, + DataViewField, + EmptyEntry, + EmptyNestedEntry, + ExceptionsBuilderExceptionItem, + ExceptionsBuilderReturnExceptionItem, + FormattedBuilderEntry, + OperatorOption, + SavedObjectType, +} from '../types'; + +export const isEntryNested = (item: BuilderEntry): item is EntryNested => { + return (item as EntryNested).entries != null; +}; + +export const filterExceptionItems = ( + exceptions: ExceptionsBuilderExceptionItem[] +): ExceptionsBuilderReturnExceptionItem[] => { + return exceptions.reduce<ExceptionsBuilderReturnExceptionItem[]>((acc, exception) => { + const entries = exception.entries.reduce<BuilderEntry[]>((nestedAcc, singleEntry) => { + const strippedSingleEntry = removeIdFromItem(singleEntry); + if (entriesNested.is(strippedSingleEntry)) { + const nestedEntriesArray = strippedSingleEntry.entries.filter((singleNestedEntry) => { + const noIdSingleNestedEntry = removeIdFromItem(singleNestedEntry); + const [validatedNestedEntry] = validate(noIdSingleNestedEntry, nestedEntryItem); + return validatedNestedEntry != null; + }); + const noIdNestedEntries = nestedEntriesArray.map((singleNestedEntry) => + removeIdFromItem(singleNestedEntry) + ); + + const [validatedNestedEntry] = validate( + { ...strippedSingleEntry, entries: noIdNestedEntries }, + entriesNested + ); + + if (validatedNestedEntry != null) { + return [...nestedAcc, { ...singleEntry, entries: nestedEntriesArray }]; + } + return nestedAcc; + } else { + const [validatedEntry] = validate(strippedSingleEntry, entry); + if (validatedEntry != null) { + return [...nestedAcc, singleEntry]; + } + return nestedAcc; + } + }, []); + + if (entries.length === 0) { + return acc; + } + + const item = { ...exception, entries }; + + if (exceptionListItemSchema.is(item)) { + return [...acc, item]; + } else if ( + createExceptionListItemSchema.is(item) || + createRuleExceptionListItemSchema.is(item) + ) { + const { meta, ...rest } = item; + const itemSansMetaId: CreateExceptionListItemSchema | CreateRuleExceptionListItemSchema = { + ...rest, + meta: undefined, + }; + return [...acc, itemSansMetaId]; + } else { + return acc; + } + }, []); +}; + +export const addIdToEntries = (entries: EntriesArray): EntriesArray => { + return entries.map((singleEntry) => { + if (singleEntry.type === 'nested') { + return addIdToItem({ + ...singleEntry, + entries: singleEntry.entries.map((nestedEntry) => addIdToItem(nestedEntry)), + }); + } else { + return addIdToItem(singleEntry); + } + }); +}; + +export const getNewExceptionItem = ({ + listId, + namespaceType, + name, +}: { + listId: string | undefined; + namespaceType: NamespaceType | undefined; + name: string; +}): CreateExceptionListItemBuilderSchema => { + return { + comments: [], + description: `Exception list item`, + entries: addIdToEntries([ + { + field: '', + operator: 'included', + type: 'match', + value: '', + }, + ]), + item_id: undefined, + list_id: listId, + meta: { + temporaryUuid: uuidv4(), + }, + name, + namespace_type: namespaceType, + tags: [], + type: 'simple', + }; +}; + +/** + * Returns the operator type, may not need this if using io-ts types + * + * @param item a single ExceptionItem entry + */ +export const getOperatorType = (item: BuilderEntry): OperatorTypeEnum => { + switch (item.type) { + case 'match': + return OperatorTypeEnum.MATCH; + case 'match_any': + return OperatorTypeEnum.MATCH_ANY; + case 'wildcard': + return OperatorTypeEnum.WILDCARD; + case 'list': + return OperatorTypeEnum.LIST; + default: + return OperatorTypeEnum.EXISTS; + } +}; + +/** + * Determines operator selection (is/is not/is one of, etc.) + * Default operator is "is" + * + * @param item a single ExceptionItem entry + */ +export const getExceptionOperatorSelect = (item: BuilderEntry): OperatorOption => { + if (item.type === 'nested') { + return isOperator; + } else { + const operatorType = getOperatorType(item); + const foundOperator = ALL_OPERATORS.find((operatorOption) => { + return item.operator === operatorOption.operator && operatorType === operatorOption.type; + }); + + return foundOperator != null ? foundOperator : isOperator; + } +}; + +/** + * Returns the fields corresponding value for an entry + * + * @param item a single ExceptionItem entry + */ +export const getEntryValue = (item: BuilderEntry): string | string[] | undefined => { + switch (item.type) { + case OperatorTypeEnum.MATCH: + case OperatorTypeEnum.MATCH_ANY: + case OperatorTypeEnum.WILDCARD: + return item.value; + case OperatorTypeEnum.EXISTS: + return undefined; + case OperatorTypeEnum.LIST: + return item.list.id; + default: + return undefined; + } +}; + +/** + * Determines whether an entire entry, exception item, or entry within a nested + * entry needs to be removed + * + * @param exceptionItem + * @param entryIndex index of given entry, for nested entries, this will correspond + * to their parent index + * @param nestedEntryIndex index of nested entry + * + */ +export const getUpdatedEntriesOnDelete = ( + exceptionItem: ExceptionsBuilderExceptionItem, + entryIndex: number, + nestedParentIndex: number | null +): ExceptionsBuilderExceptionItem => { + const itemOfInterest: BuilderEntry = + exceptionItem.entries[nestedParentIndex != null ? nestedParentIndex : entryIndex]; + + if (nestedParentIndex != null && itemOfInterest.type === OperatorTypeEnum.NESTED) { + const updatedEntryEntries = [ + ...itemOfInterest.entries.slice(0, entryIndex), + ...itemOfInterest.entries.slice(entryIndex + 1), + ]; + + if (updatedEntryEntries.length === 0) { + return { + ...exceptionItem, + entries: [ + ...exceptionItem.entries.slice(0, nestedParentIndex), + ...exceptionItem.entries.slice(nestedParentIndex + 1), + ], + }; + } else { + const { field } = itemOfInterest; + const updatedItemOfInterest: EntryNested | EmptyNestedEntry = { + entries: updatedEntryEntries, + field, + id: itemOfInterest.id != null ? itemOfInterest.id : `${entryIndex}`, + type: OperatorTypeEnum.NESTED, + }; + + return { + ...exceptionItem, + entries: [ + ...exceptionItem.entries.slice(0, nestedParentIndex), + updatedItemOfInterest, + ...exceptionItem.entries.slice(nestedParentIndex + 1), + ], + }; + } + } else { + return { + ...exceptionItem, + entries: [ + ...exceptionItem.entries.slice(0, entryIndex), + ...exceptionItem.entries.slice(entryIndex + 1), + ], + }; + } +}; + +/** + * Returns filtered index patterns based on the field - if a user selects to + * add nested entry, should only show nested fields, if item is the parent + * field of a nested entry, we only display the parent field + * + * @param patterns DataViewBase containing available fields on rule index + * @param item exception item entry + * set to add a nested field + */ +export const getFilteredIndexPatterns = ( + patterns: DataViewBase, + item: FormattedBuilderEntry +): DataViewBase => { + if (item.nested === 'child' && item.parent != null) { + // when user has selected a nested entry, only fields with the common parent are shown + return { + ...patterns, + fields: patterns.fields + .filter((indexField) => { + const subTypeNested = getDataViewFieldSubtypeNested(indexField); + const fieldHasCommonParentPath = + subTypeNested && + item.parent != null && + subTypeNested.nested.path === item.parent.parent.field; + + return fieldHasCommonParentPath; + }) + .map((f) => { + const [fieldNameWithoutParentPath] = f.name.split('.').slice(-1); + return { ...f, name: fieldNameWithoutParentPath }; + }), + }; + } else if (item.nested === 'parent' && item.field != null) { + // when user has selected a nested entry, right above it we show the common parent + return { ...patterns, fields: [item.field] }; + } else if (item.nested === 'parent' && item.field == null) { + // when user selects to add a nested entry, only nested fields are shown as options + return { + ...patterns, + fields: patterns.fields.filter((field) => isDataViewFieldSubtypeNested(field)), + }; + } else { + return patterns; + } +}; + +/** + * Determines proper entry update when user selects new field + * + * @param item - current exception item entry values + * @param newField - newly selected field + * + */ +export const getEntryOnFieldChange = ( + item: FormattedBuilderEntry, + newField: DataViewFieldBase +): { index: number; updatedEntry: BuilderEntry } => { + const { parent, entryIndex, nested } = item; + const newChildFieldValue = newField != null ? newField.name.split('.').slice(-1)[0] : ''; + + if (nested === 'parent') { + // For nested entries, when user first selects to add a nested + // entry, they first see a row similar to what is shown for when + // a user selects "exists", as soon as they make a selection + // we can now identify the 'parent' and 'child' this is where + // we first convert the entry into type "nested" + const subTypeNested = getDataViewFieldSubtypeNested(newField); + const newParentFieldValue = subTypeNested?.nested.path || ''; + + return { + index: entryIndex, + updatedEntry: { + entries: [ + addIdToItem({ + field: newChildFieldValue != null ? newChildFieldValue : '', + operator: isOperator.operator, + type: OperatorTypeEnum.MATCH, + value: '', + }), + ], + field: newParentFieldValue, + id: item.id, + type: OperatorTypeEnum.NESTED, + }, + }; + } else if (nested === 'child' && parent != null) { + return { + index: parent.parentIndex, + updatedEntry: { + ...parent.parent, + entries: [ + ...parent.parent.entries.slice(0, entryIndex), + { + field: newChildFieldValue != null ? newChildFieldValue : '', + id: item.id, + operator: isOperator.operator, + type: OperatorTypeEnum.MATCH, + value: '', + }, + ...parent.parent.entries.slice(entryIndex + 1), + ], + }, + }; + } else { + return { + index: entryIndex, + updatedEntry: { + field: newField != null ? newField.name : '', + id: item.id, + operator: isOperator.operator, + type: OperatorTypeEnum.MATCH, + value: '', + }, + }; + } +}; + +/** + * Determines proper entry update when user updates value + * when operator is of type "list" + * + * @param item - current exception item entry values + * @param newField - newly selected list + * + */ +export const getEntryOnListChange = ( + item: FormattedBuilderEntry, + newField: ListSchema +): { index: number; updatedEntry: BuilderEntry } => { + const { entryIndex, field, operator } = item; + const { id, type } = newField; + + return { + index: entryIndex, + updatedEntry: { + field: field != null ? field.name : '', + id: item.id, + list: { id, type }, + operator: operator.operator, + type: OperatorTypeEnum.LIST, + }, + }; +}; + +/** + * Determines proper entry update when user updates value + * when operator is of type "match_any" + * + * @param item - current exception item entry values + * @param newField - newly entered value + * + */ +export const getEntryOnMatchAnyChange = ( + item: FormattedBuilderEntry, + newField: string[] +): { index: number; updatedEntry: BuilderEntry } => { + const { nested, parent, entryIndex, field, operator } = item; + + if (nested != null && parent != null) { + const fieldName = field != null ? field.name.split('.').slice(-1)[0] : ''; + + return { + index: parent.parentIndex, + updatedEntry: { + ...parent.parent, + entries: [ + ...parent.parent.entries.slice(0, entryIndex), + { + field: fieldName, + id: item.id, + operator: operator.operator, + type: OperatorTypeEnum.MATCH_ANY, + value: newField, + }, + ...parent.parent.entries.slice(entryIndex + 1), + ], + }, + }; + } else { + return { + index: entryIndex, + updatedEntry: { + field: field != null ? field.name : '', + id: item.id, + operator: operator.operator, + type: OperatorTypeEnum.MATCH_ANY, + value: newField, + }, + }; + } +}; + +/** + * Determines proper entry update when user updates value + * when operator is of type "match" + * + * @param item - current exception item entry values + * @param newField - newly entered value + * + */ +export const getEntryOnMatchChange = ( + item: FormattedBuilderEntry, + newField: string +): { index: number; updatedEntry: BuilderEntry } => { + const { nested, parent, entryIndex, field, operator } = item; + + if (nested != null && parent != null) { + const fieldName = field != null ? field.name.split('.').slice(-1)[0] : ''; + + return { + index: parent.parentIndex, + updatedEntry: { + ...parent.parent, + entries: [ + ...parent.parent.entries.slice(0, entryIndex), + { + field: fieldName, + id: item.id, + operator: operator.operator, + type: OperatorTypeEnum.MATCH, + value: newField, + }, + ...parent.parent.entries.slice(entryIndex + 1), + ], + }, + }; + } else { + return { + index: entryIndex, + updatedEntry: { + field: field != null ? field.name : '', + id: item.id, + operator: operator.operator, + type: OperatorTypeEnum.MATCH, + value: newField, + }, + }; + } +}; + +/** + * Determines proper entry update when user updates value + * when operator is of type "wildcard" + * + * @param item - current exception item entry values + * @param newField - newly entered value + * + */ +export const getEntryOnWildcardChange = ( + item: FormattedBuilderEntry, + newField: string +): { index: number; updatedEntry: BuilderEntry } => { + const { nested, parent, entryIndex, field, operator } = item; + + if (nested != null && parent != null) { + const fieldName = field != null ? field.name.split('.').slice(-1)[0] : ''; + + return { + index: parent.parentIndex, + updatedEntry: { + ...parent.parent, + entries: [ + ...parent.parent.entries.slice(0, entryIndex), + { + field: fieldName, + id: item.id, + operator: operator.operator, + type: OperatorTypeEnum.WILDCARD, + value: newField, + }, + ...parent.parent.entries.slice(entryIndex + 1), + ], + }, + }; + } else { + return { + index: entryIndex, + updatedEntry: { + field: field != null ? field.name : '', + id: item.id, + operator: operator.operator, + type: OperatorTypeEnum.WILDCARD, + value: newField, + }, + }; + } +}; + +/** + * On operator change, determines whether value needs to be cleared or not + * + * @param field + * @param selectedOperator + * @param currentEntry + * + */ +export const getEntryFromOperator = ( + selectedOperator: OperatorOption, + currentEntry: FormattedBuilderEntry +): Entry & { id?: string } => { + const isSameOperatorType = currentEntry.operator.type === selectedOperator.type; + const fieldValue = currentEntry.field != null ? currentEntry.field.name : ''; + switch (selectedOperator.type) { + case 'match': + return { + field: fieldValue, + id: currentEntry.id, + operator: selectedOperator.operator, + type: OperatorTypeEnum.MATCH, + value: + isSameOperatorType && typeof currentEntry.value === 'string' ? currentEntry.value : '', + }; + case 'match_any': + return { + field: fieldValue, + id: currentEntry.id, + operator: selectedOperator.operator, + type: OperatorTypeEnum.MATCH_ANY, + value: isSameOperatorType && Array.isArray(currentEntry.value) ? currentEntry.value : [], + }; + case 'list': + return { + field: fieldValue, + id: currentEntry.id, + list: { id: '', type: 'ip' }, + operator: selectedOperator.operator, + type: OperatorTypeEnum.LIST, + }; + case 'wildcard': + return { + field: fieldValue, + id: currentEntry.id, + operator: selectedOperator.operator, + type: OperatorTypeEnum.WILDCARD, + value: + isSameOperatorType && typeof currentEntry.value === 'string' ? currentEntry.value : '', + }; + default: + return { + field: fieldValue, + id: currentEntry.id, + operator: selectedOperator.operator, + type: OperatorTypeEnum.EXISTS, + }; + } +}; + +/** + * Determines proper entry update when user selects new operator + * + * @param item - current exception item entry values + * @param newOperator - newly selected operator + * + */ +export const getEntryOnOperatorChange = ( + item: FormattedBuilderEntry, + newOperator: OperatorOption +): { updatedEntry: BuilderEntry; index: number } => { + const { parent, entryIndex, field, nested } = item; + const newEntry = getEntryFromOperator(newOperator, item); + + if (!entriesList.is(newEntry) && nested != null && parent != null) { + return { + index: parent.parentIndex, + updatedEntry: { + ...parent.parent, + entries: [ + ...parent.parent.entries.slice(0, entryIndex), + { + ...newEntry, + field: field != null ? field.name.split('.').slice(-1)[0] : '', + }, + ...parent.parent.entries.slice(entryIndex + 1), + ], + }, + }; + } else { + return { index: entryIndex, updatedEntry: newEntry }; + } +}; + +export const isKibanaStringType = (type: string) => { + const kbnFieldType = castEsToKbnFieldTypeName(type); + return kbnFieldType === KBN_FIELD_TYPES.STRING; +}; + +export const fieldSupportsMatches = (field: DataViewFieldBase) => { + return field.esTypes?.some(isKibanaStringType); +}; + +/** + * Determines which operators to make available + * + * @param item + * @param listType + * @param isBoolean + * @param includeValueListOperators whether or not to include the 'is in list' and 'is not in list' operators + */ +export const getOperatorOptions = ( + item: FormattedBuilderEntry, + listType: ExceptionListType, + isBoolean: boolean, + includeValueListOperators = true +): OperatorOption[] => { + if (item.nested === 'parent' || item.field == null) { + return [isOperator]; + } else if (listType === 'endpoint') { + if (isBoolean) { + return [isOperator]; + } else { + return fieldSupportsMatches(item.field) + ? [isOperator, isOneOfOperator, matchesOperator, doesNotMatchOperator] + : [isOperator, isOneOfOperator]; + } + } else if (item.nested != null && listType === 'detection') { + return isBoolean ? [isOperator, existsOperator] : [isOperator, isOneOfOperator, existsOperator]; + } else if (isBoolean) { + return [isOperator, isNotOperator, existsOperator, doesNotExistOperator]; + } else if (!includeValueListOperators) { + return fieldSupportsMatches(item.field) + ? EXCEPTION_OPERATORS_SANS_LISTS + : [ + isOperator, + isNotOperator, + isOneOfOperator, + isNotOneOfOperator, + existsOperator, + doesNotExistOperator, + ]; + } else { + return listType === 'detection' + ? fieldSupportsMatches(item.field) + ? DETECTION_ENGINE_EXCEPTION_OPERATORS + : [ + isOperator, + isNotOperator, + isOneOfOperator, + isNotOneOfOperator, + existsOperator, + doesNotExistOperator, + isInListOperator, + isNotInListOperator, + ] + : ALL_OPERATORS; + } +}; + +/** + * Fields of type 'text' do not generate autocomplete values, we want + * to find it's corresponding keyword type (if available) which does + * generate autocomplete values + * + * @param fields DataViewFieldBase fields + * @param selectedField the field name that was selected + * @param isTextType we only want a corresponding keyword field if + * the selected field is of type 'text' + * + */ +export const getCorrespondingKeywordField = ({ + fields, + selectedField, +}: { + fields: DataViewFieldBase[]; + selectedField: string | undefined; +}): DataViewFieldBase | undefined => { + const selectedFieldBits = + selectedField != null && selectedField !== '' ? selectedField.split('.') : []; + const selectedFieldIsTextType = selectedFieldBits.slice(-1)[0] === 'text'; + + if (selectedFieldIsTextType && selectedFieldBits.length > 0) { + const keywordField = selectedFieldBits.slice(0, selectedFieldBits.length - 1).join('.'); + const [foundKeywordField] = fields.filter( + ({ name }) => keywordField !== '' && keywordField === name + ); + return foundKeywordField; + } + + return undefined; +}; + +/** + * Formats the entry into one that is easily usable for the UI, most of the + * complexity was introduced with nested fields + * + * @param patterns DataViewBase containing available fields on rule index + * @param item exception item entry + * @param itemIndex entry index + * @param parent nested entries hold copy of their parent for use in various logic + * @param parentIndex corresponds to the entry index, this might seem obvious, but + * was added to ensure that nested items could be identified with their parent entry + * @param allowCustomFieldOptions determines if field must be found to match in indexPattern or not + */ +export const getFormattedBuilderEntry = ( + indexPattern: DataViewBase, + item: BuilderEntry, + itemIndex: number, + parent: EntryNested | undefined, + parentIndex: number | undefined, + allowCustomFieldOptions: boolean +): FormattedBuilderEntry => { + const { fields } = indexPattern; + const field = parent != null ? `${parent.field}.${item.field}` : item.field; + const [foundField] = fields.filter(({ name }) => field != null && field === name); + const correspondingKeywordField = getCorrespondingKeywordField({ + fields, + selectedField: field, + }); + + if (parent != null && parentIndex != null) { + return { + correspondingKeywordField, + entryIndex: itemIndex, + field: + foundField != null + ? { ...foundField, name: foundField.name.split('.').slice(-1)[0] } + : foundField, + id: item.id != null ? item.id : `${itemIndex}`, + nested: 'child', + operator: getExceptionOperatorSelect(item), + parent: { parent, parentIndex }, + value: getEntryValue(item), + }; + } else { + const fieldToUse = allowCustomFieldOptions + ? foundField ?? { name: item.field, type: 'keyword' } + : foundField; + + return { + correspondingKeywordField, + entryIndex: itemIndex, + field: fieldToUse, + id: item.id != null ? item.id : `${itemIndex}`, + nested: undefined, + operator: getExceptionOperatorSelect(item), + parent: undefined, + value: getEntryValue(item), + }; + } +}; + +/** + * Formats the entries to be easily usable for the UI, most of the + * complexity was introduced with nested fields + * + * @param patterns DataViewBase containing available fields on rule index + * @param entries exception item entries + * @param allowCustomFieldOptions determines if field must be found to match in indexPattern or not + * @param parent nested entries hold copy of their parent for use in various logic + * @param parentIndex corresponds to the entry index, this might seem obvious, but + * was added to ensure that nested items could be identified with their parent entry + */ +export const getFormattedBuilderEntries = ( + indexPattern: DataViewBase, + entries: BuilderEntry[], + allowCustomFieldOptions: boolean, + parent?: EntryNested, + parentIndex?: number +): FormattedBuilderEntry[] => { + return entries.reduce<FormattedBuilderEntry[]>((acc, item, index) => { + const isNewNestedEntry = item.type === 'nested' && item.entries.length === 0; + if (item.type !== 'nested' && !isNewNestedEntry) { + const newItemEntry: FormattedBuilderEntry = getFormattedBuilderEntry( + indexPattern, + item, + index, + parent, + parentIndex, + allowCustomFieldOptions + ); + return [...acc, newItemEntry]; + } else { + const parentEntry: FormattedBuilderEntry = { + correspondingKeywordField: undefined, + entryIndex: index, + field: isNewNestedEntry + ? undefined + : // This type below is really a FieldSpec type from "src/plugins/data/common/index_patterns/fields/types.ts", we cast it here to keep using the DataViewFieldBase interface + ({ + aggregatable: false, + esTypes: ['nested'], + name: item.field != null ? item.field : '', + searchable: false, + type: 'string', + } as DataViewFieldBase), + id: item.id != null ? item.id : `${index}`, + nested: 'parent', + operator: isOperator, + parent: undefined, + value: undefined, + }; + + // User has selected to add a nested field, but not yet selected the field + if (isNewNestedEntry) { + return [...acc, parentEntry]; + } + + if (isEntryNested(item)) { + const nestedItems = getFormattedBuilderEntries( + indexPattern, + item.entries, + allowCustomFieldOptions, + item, + index + ); + + return [...acc, parentEntry, ...nestedItems]; + } + + return [...acc]; + } + }, []); +}; + +export const getDefaultEmptyEntry = (): EmptyEntry => ({ + field: '', + id: uuidv4(), + operator: OperatorEnum.INCLUDED, + type: OperatorTypeEnum.MATCH, + value: '', +}); + +export const getDefaultNestedEmptyEntry = (): EmptyNestedEntry => ({ + entries: [], + field: '', + id: uuidv4(), + type: OperatorTypeEnum.NESTED, +}); + +export const containsValueListEntry = (items: ExceptionsBuilderExceptionItem[]): boolean => + items.some((item) => item.entries.some(({ type }) => type === OperatorTypeEnum.LIST)); + +export const buildShowActiveExceptionsFilter = (savedObjectPrefix: SavedObjectType[]): string => { + const now = new Date().toISOString(); + const filters = savedObjectPrefix.map( + (prefix) => + `${prefix}.attributes.expire_time > "${now}" OR NOT ${prefix}.attributes.expire_time: *` + ); + return filters.join(','); +}; + +export const buildShowExpiredExceptionsFilter = (savedObjectPrefix: SavedObjectType[]): string => { + const now = new Date().toISOString(); + const filters = savedObjectPrefix.map((prefix) => `${prefix}.attributes.expire_time <= "${now}"`); + return filters.join(','); +}; + +const getIndexGroupName = (indexName: string): string => { + // Check whether it is a Data Stream index + const dataStreamExp = /.ds-(.*?)-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-[0-9]{6}/; + let result = indexName.match(dataStreamExp); + if (result && result.length === 2) { + return result[1]; + } + + // Check whether it is an old '.siem' index group + const siemSignalsExp = /.siem-(.*?)-[0-9]{6}/; + result = indexName.match(siemSignalsExp); + if (result && result.length === 2) { + return `.siem-${result[1]}`; + } + + // Otherwise return index name + return indexName; +}; + +export interface FieldConflictsInfo { + /** + * Kibana field type + */ + type: string; + /** + * Total count of the indices of this type + */ + totalIndexCount: number; + /** + * Grouped indices info + */ + groupedIndices: Array<{ + /** + * Index group name (like '.ds-...' or '.siem-signals-...') + */ + name: string; + /** + * Count of indices in the group + */ + count: number; + }>; +} + +export const getMappingConflictsInfo = (field: DataViewField): FieldConflictsInfo[] | null => { + if (!field.conflictDescriptions) { + return null; + } + const conflicts: FieldConflictsInfo[] = []; + for (const [key, value] of Object.entries(field.conflictDescriptions)) { + const groupedIndices: Array<{ + name: string; + count: number; + }> = []; + + // Group indices and calculate count of indices in each group + const groupedInfo: { [key: string]: number } = {}; + value.forEach((index) => { + const groupName = getIndexGroupName(index); + if (!groupedInfo[groupName]) { + groupedInfo[groupName] = 0; + } + groupedInfo[groupName]++; + }); + for (const [name, count] of Object.entries(groupedInfo)) { + groupedIndices.push({ + name, + count, + }); + } + + // Sort groups by the indices count + groupedIndices.sort((group1, group2) => { + return group2.count - group1.count; + }); + + conflicts.push({ + type: key, + totalIndexCount: value.length, + groupedIndices, + }); + } + return conflicts; +}; + +/** + * Given an exceptions list, determine if any entries have an "IS" operator with a wildcard value + */ +export const hasWrongOperatorWithWildcard = ( + items: ExceptionsBuilderReturnExceptionItem[] +): boolean => { + // flattens array of multiple entries added with OR + const multipleEntries = items.flatMap((item) => item.entries); + // flattens nested entries + const allEntries = multipleEntries.flatMap((item) => { + if (item.type === 'nested') { + return item.entries; + } + return item; + }); + + // eslint-disable-next-line array-callback-return + return allEntries.some((e) => { + if (e.type !== 'list' && 'value' in e) { + return validateHasWildcardWithWrongOperator({ + operator: e.type, + value: e.value, + }); + } + }); +}; + +/** + * Event filters helper where given an exceptions list, + * determine if both 'subject_name' and 'trusted' are + * included in an entry with 'code_signature' + */ +export const hasPartialCodeSignatureEntry = ( + items: ExceptionsBuilderReturnExceptionItem[] +): boolean => { + const { os_types: os = ['windows'], entries = [] } = items[0] || {}; + let name = false; + let trusted = false; + + for (const e of entries) { + if (e.type === 'nested' && e.field === 'process.Ext.code_signature') { + const includesNestedName = e.entries.some( + (nestedEntry) => nestedEntry.field === 'subject_name' + ); + const includesNestedTrusted = e.entries.some( + (nestedEntry) => nestedEntry.field === 'trusted' + ); + if (includesNestedName !== includesNestedTrusted) { + return true; + } + } else if ( + e.field === 'process.code_signature.subject_name' || + (os.includes('macos') && e.field === 'process.code_signature.team_id') + ) { + name = true; + } else if (e.field === 'process.code_signature.trusted') { + trusted = true; + } + } + return name !== trusted; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/types/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/types/index.ts new file mode 100644 index 000000000000..1730a0e9a829 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/src/types/index.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataViewFieldBase } from '@kbn/es-query'; +import type { + CreateExceptionListItemSchema, + CreateRuleExceptionListItemSchema, + Entry, + EntryExists, + EntryMatch, + EntryMatchAny, + EntryMatchWildcard, + EntryNested, + ExceptionListItemSchema, + ListOperatorEnum as OperatorEnum, + ListOperatorTypeEnum as OperatorTypeEnum, + NamespaceType, +} from '@kbn/securitysolution-io-ts-list-types'; +import { + EXCEPTION_LIST_NAMESPACE, + EXCEPTION_LIST_NAMESPACE_AGNOSTIC, +} from '@kbn/securitysolution-list-constants'; + +export interface DataViewField extends DataViewFieldBase { + conflictDescriptions?: Record<string, string[]>; +} + +export interface OperatorOption { + message: string; + value: string; + operator: OperatorEnum; + type: OperatorTypeEnum; +} + +export interface FormattedBuilderEntry { + id: string; + field: DataViewField | undefined; + operator: OperatorOption; + value: string | string[] | undefined; + nested: 'parent' | 'child' | undefined; + entryIndex: number; + parent: { parent: BuilderEntryNested; parentIndex: number } | undefined; + correspondingKeywordField: DataViewFieldBase | undefined; +} + +export interface EmptyEntry { + id: string; + field: string | undefined; + operator: OperatorEnum; + type: OperatorTypeEnum.MATCH | OperatorTypeEnum.MATCH_ANY | OperatorTypeEnum.WILDCARD; + value: string | string[] | undefined; +} + +export interface EmptyListEntry { + id: string; + field: string | undefined; + operator: OperatorEnum; + type: OperatorTypeEnum.LIST; + list: { id: string | undefined; type: string | undefined }; +} + +export interface EmptyNestedEntry { + id: string; + field: string | undefined; + type: OperatorTypeEnum.NESTED; + entries: Array< + | (EntryMatch & { id?: string }) + | (EntryMatchAny & { id?: string }) + | (EntryMatchWildcard & { id?: string }) + | (EntryExists & { id?: string }) + >; +} + +export type BuilderEntry = + | (Entry & { id?: string }) + | EmptyListEntry + | EmptyEntry + | BuilderEntryNested + | EmptyNestedEntry; + +export type BuilderEntryNested = Omit<EntryNested, 'entries'> & { + id?: string; + entries: Array< + | (EntryMatch & { id?: string }) + | (EntryMatchAny & { id?: string }) + | (EntryMatchWildcard & { id?: string }) + | (EntryExists & { id?: string }) + >; +}; + +export type ExceptionListItemBuilderSchema = Omit<ExceptionListItemSchema, 'entries'> & { + entries: BuilderEntry[]; +}; + +export type CreateExceptionListItemBuilderSchema = Omit< + CreateExceptionListItemSchema, + 'meta' | 'entries' | 'list_id' | 'namespace_type' +> & { + meta: { temporaryUuid: string }; + entries: BuilderEntry[]; + list_id: string | undefined; + namespace_type: NamespaceType | undefined; +}; + +export type ExceptionsBuilderExceptionItem = + | ExceptionListItemBuilderSchema + | CreateExceptionListItemBuilderSchema; + +export type ExceptionsBuilderReturnExceptionItem = + | ExceptionListItemSchema + | CreateExceptionListItemSchema + | CreateRuleExceptionListItemSchema; + +export const exceptionListSavedObjectType = EXCEPTION_LIST_NAMESPACE; +export const exceptionListAgnosticSavedObjectType = EXCEPTION_LIST_NAMESPACE_AGNOSTIC; +export type SavedObjectType = + | typeof EXCEPTION_LIST_NAMESPACE + | typeof EXCEPTION_LIST_NAMESPACE_AGNOSTIC; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/tsconfig.json new file mode 100644 index 000000000000..0b6eb353950d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-list-utils/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "kbn_references": [ + "@kbn/es-query", + "@kbn/i18n", + "@kbn/securitysolution-io-ts-list-types", + "@kbn/securitysolution-io-ts-utils", + "@kbn/securitysolution-list-constants", + "@kbn/securitysolution-utils", + "@kbn/field-types" + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-securitysolution-lists-common/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/README.md similarity index 100% rename from packages/kbn-securitysolution-lists-common/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list/create_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list/create_list.gen.ts new file mode 100644 index 000000000000..cf603f3ac3bd --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list/create_list.gen.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Create list API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { + ListId, + ListName, + ListDescription, + ListType, + ListMetadata, +} from '../model/list_common.gen'; +import { List } from '../model/list_schemas.gen'; + +export type CreateListRequestBody = z.infer<typeof CreateListRequestBody>; +export const CreateListRequestBody = z.object({ + id: ListId.optional(), + name: ListName, + description: ListDescription, + type: ListType, + serializer: z.string().optional(), + deserializer: z.string().optional(), + meta: ListMetadata.optional(), + version: z.number().int().min(1).optional().default(1), +}); +export type CreateListRequestBodyInput = z.input<typeof CreateListRequestBody>; + +export type CreateListResponse = z.infer<typeof CreateListResponse>; +export const CreateListResponse = List; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list/create_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list/create_list.schema.yaml new file mode 100644 index 000000000000..df3e6b35ef65 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list/create_list.schema.yaml @@ -0,0 +1,81 @@ +openapi: 3.0.0 +info: + title: Create list API endpoint + version: '2023-10-31' +paths: + /api/lists: + post: + x-labels: [serverless, ess] + operationId: CreateList + x-codegen-enabled: true + summary: Create a list + description: Create a new list. + requestBody: + description: List's properties + required: true + content: + application/json: + schema: + type: object + properties: + id: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + name: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListName' + description: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListDescription' + type: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListType' + serializer: + type: string + deserializer: + type: string + meta: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListMetadata' + version: + type: integer + minimum: 1 + default: 1 + required: + - name + - description + - type + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/list_schemas.schema.yaml#/components/schemas/List' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 409: + description: List already exists response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.gen.ts new file mode 100644 index 000000000000..7eb20ada7f32 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.gen.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Create list DS API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +export type CreateListIndexResponse = z.infer<typeof CreateListIndexResponse>; +export const CreateListIndexResponse = z.object({ + acknowledged: z.boolean(), +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.schema.yaml new file mode 100644 index 000000000000..8ff9ad6ab1b2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_index/create_list_index.schema.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + title: Create list DS API endpoint + version: '2023-10-31' +paths: + /api/lists/index: + post: + x-labels: [serverless, ess] + operationId: CreateListIndex + x-codegen-enabled: true + summary: Create list data streams + description: Create `.lists` and `.items` data streams in the relevant space. + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: object + properties: + acknowledged: + type: boolean + required: [acknowledged] + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 409: + description: List data stream exists response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.gen.ts new file mode 100644 index 000000000000..ce4744f3c441 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.gen.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Create list item API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { ListItemId, ListId, ListItemValue, ListItemMetadata } from '../model/list_common.gen'; +import { ListItem } from '../model/list_schemas.gen'; + +export type CreateListItemRequestBody = z.infer<typeof CreateListItemRequestBody>; +export const CreateListItemRequestBody = z.object({ + id: ListItemId.optional(), + list_id: ListId, + value: ListItemValue, + meta: ListItemMetadata.optional(), + /** + * Determines when changes made by the request are made visible to search + */ + refresh: z.enum(['true', 'false', 'wait_for']).optional(), +}); +export type CreateListItemRequestBodyInput = z.input<typeof CreateListItemRequestBody>; + +export type CreateListItemResponse = z.infer<typeof CreateListItemResponse>; +export const CreateListItemResponse = ListItem; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.schema.yaml new file mode 100644 index 000000000000..01d024f8b40d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/create_list_item/create_list_item.schema.yaml @@ -0,0 +1,82 @@ +openapi: 3.0.0 +info: + title: Create list item API endpoint + version: '2023-10-31' +paths: + /api/lists/items: + post: + x-labels: [serverless, ess] + operationId: CreateListItem + x-codegen-enabled: true + summary: Create a list item + description: | + Create a list item and associate it with the specified list. + + All list items in the same list must be the same type. For example, each list item in an `ip` list must define a specific IP address. + > info + > Before creating a list item, you must create a list. + requestBody: + description: List item's properties + required: true + content: + application/json: + schema: + type: object + properties: + id: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemId' + list_id: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + value: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemValue' + meta: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemMetadata' + refresh: + type: string + enum: + - 'true' + - 'false' + - wait_for + description: Determines when changes made by the request are made visible to search + required: + - list_id + - value + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 409: + description: List item already exists response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.gen.ts new file mode 100644 index 000000000000..3dd638be564e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Delete list API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; +import { BooleanFromString } from '@kbn/zod-helpers'; + +import { ListId } from '../model/list_common.gen'; +import { List } from '../model/list_schemas.gen'; + +export type DeleteListRequestQuery = z.infer<typeof DeleteListRequestQuery>; +export const DeleteListRequestQuery = z.object({ + /** + * List's `id` value + */ + id: ListId, + deleteReferences: BooleanFromString.optional().default(false), + ignoreReferences: BooleanFromString.optional().default(false), +}); +export type DeleteListRequestQueryInput = z.input<typeof DeleteListRequestQuery>; + +export type DeleteListResponse = z.infer<typeof DeleteListResponse>; +export const DeleteListResponse = List; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.schema.yaml new file mode 100644 index 000000000000..e8caef0eb2c6 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list/delete_list.schema.yaml @@ -0,0 +1,73 @@ +openapi: 3.0.0 +info: + title: Delete list API endpoint + version: '2023-10-31' +paths: + /api/lists: + delete: + x-labels: [serverless, ess] + operationId: DeleteList + x-codegen-enabled: true + summary: Delete a list + description: | + Delete a list using the list ID. + > info + > When you delete a list, all of its list items are also deleted. + parameters: + - name: id + in: query + required: true + description: List's `id` value + schema: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + - name: deleteReferences + in: query + required: false + schema: + type: boolean + default: false + - name: ignoreReferences + in: query + required: false + schema: + type: boolean + default: false + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/list_schemas.schema.yaml#/components/schemas/List' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.gen.ts new file mode 100644 index 000000000000..4ffd90f6fb8b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.gen.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Delete list DS API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +export type DeleteListIndexResponse = z.infer<typeof DeleteListIndexResponse>; +export const DeleteListIndexResponse = z.object({ + acknowledged: z.boolean(), +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.schema.yaml new file mode 100644 index 000000000000..ae43c58726d5 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_index/delete_list_index.schema.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + title: Delete list DS API endpoint + version: '2023-10-31' +paths: + /api/lists/index: + delete: + x-labels: [serverless, ess] + operationId: DeleteListIndex + x-codegen-enabled: true + summary: Delete list data streams + description: Delete the `.lists` and `.items` data streams. + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: object + properties: + acknowledged: + type: boolean + required: [acknowledged] + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List data stream not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.gen.ts similarity index 76% rename from packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.gen.ts index 178641cd8d6a..fe9f7bee0e68 100644 --- a/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.schema.yaml new file mode 100644 index 000000000000..b95afcdc1ed3 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/delete_list_item/delete_list_item.schema.yaml @@ -0,0 +1,82 @@ +openapi: 3.0.0 +info: + title: Delete list item API endpoint + version: '2023-10-31' +paths: + /api/lists/items: + delete: + x-labels: [serverless, ess] + operationId: DeleteListItem + x-codegen-enabled: true + summary: Delete a list item + description: Delete a list item using its `id`, or its `list_id` and `value` fields. + parameters: + - name: id + in: query + required: false + description: Required if `list_id` and `value` are not specified + schema: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + - name: list_id + in: query + required: false + description: Required if `id` is not specified + schema: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + - name: value + in: query + required: false + description: Required if `id` is not specified + schema: + type: string + - name: refresh + in: query + required: false + description: Determines when changes made by the request are made visible to search + schema: + type: string + enum: ['true', 'false', 'wait_for'] + default: 'false' + responses: + 200: + description: Successful response + content: + application/json: + schema: + oneOf: + - $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' + - type: array + items: + $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List item not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.gen.ts new file mode 100644 index 000000000000..87e05bd24e48 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.gen.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Export list items API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { ListId } from '../model/list_common.gen'; + +export type ExportListItemsRequestQuery = z.infer<typeof ExportListItemsRequestQuery>; +export const ExportListItemsRequestQuery = z.object({ + /** + * List's id to export + */ + list_id: ListId, +}); +export type ExportListItemsRequestQueryInput = z.input<typeof ExportListItemsRequestQuery>; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.schema.yaml new file mode 100644 index 000000000000..999eb4a0ae42 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/export_list_items/export_list_items.schema.yaml @@ -0,0 +1,60 @@ +openapi: 3.0.0 +info: + title: Export list items API endpoint + version: '2023-10-31' +paths: + /api/lists/items/_export: + post: + x-labels: [serverless, ess] + operationId: ExportListItems + x-codegen-enabled: true + summary: Export list items + description: Export list item values from the specified list. + parameters: + - name: list_id + in: query + required: true + description: List's id to export + schema: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + responses: + 200: + description: Successful response + content: + application/ndjson: + schema: + type: string + format: binary + description: A `.txt` file containing list items from the specified list + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.gen.ts similarity index 85% rename from packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.gen.ts index f693965e91e2..e40c6fe9e2fc 100644 --- a/packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.schema.yaml similarity index 77% rename from packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.schema.yaml index 21cf4ffd6184..746b3e9fdbe3 100644 --- a/packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_list_items/find_list_items.schema.yaml @@ -34,7 +34,7 @@ paths: required: false description: Determines which field is used to sort the results schema: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - name: sort_order in: query required: false @@ -94,31 +94,31 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' components: schemas: FindListItemsCursor: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' FindListItemsFilter: type: string diff --git a/packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.gen.ts similarity index 84% rename from packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.gen.ts index ec104f2e6c2b..74f8ba0217d6 100644 --- a/packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.schema.yaml similarity index 76% rename from packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.schema.yaml index 3bb55decacff..9b0012e8d696 100644 --- a/packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/find_lists/find_lists.schema.yaml @@ -28,7 +28,7 @@ paths: required: false description: Determines which field is used to sort the results schema: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' - name: sort_order in: query required: false @@ -88,31 +88,31 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' components: schemas: FindListsCursor: - $ref: '../../../kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' FindListsFilter: type: string diff --git a/packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.gen.ts similarity index 77% rename from packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.gen.ts index b71b05bd7595..baf9d9308a93 100644 --- a/packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.schema.yaml similarity index 75% rename from packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.schema.yaml index 520213e949c1..f3fae4515934 100644 --- a/packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/import_list_items/import_list_items.schema.yaml @@ -73,29 +73,29 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 409: description: List with specified list_id does not exist response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/index.ts new file mode 100644 index 000000000000..6ff15e6109c6 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './model/list_common.gen'; +export * from './model/list_schemas.gen'; +export * from './create_list_index/create_list_index.gen'; +export * from './create_list_item/create_list_item.gen'; +export * from './create_list/create_list.gen'; +export * from './delete_list_index/delete_list_index.gen'; +export * from './delete_list_item/delete_list_item.gen'; +export * from './delete_list/delete_list.gen'; +export * from './find_list_items/find_list_items.gen'; +export * from './find_lists/find_lists.gen'; +export * from './export_list_items/export_list_items.gen'; +export * from './import_list_items/import_list_items.gen'; +export * from './patch_list_item/patch_list_item.gen'; +export * from './patch_list/patch_list.gen'; +export * from './read_list_index/read_list_index.gen'; +export * from './read_list_item/read_list_item.gen'; +export * from './read_list/read_list.gen'; +export * from './update_list_item/update_list_item.gen'; +export * from './update_list/update_list.gen'; diff --git a/packages/kbn-securitysolution-lists-common/api/model/list_common.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_common.gen.ts similarity index 82% rename from packages/kbn-securitysolution-lists-common/api/model/list_common.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_common.gen.ts index 9a51bd0eeb5a..536e0b859e45 100644 --- a/packages/kbn-securitysolution-lists-common/api/model/list_common.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_common.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_common.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_common.schema.yaml new file mode 100644 index 000000000000..808e99c7b1e1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_common.schema.yaml @@ -0,0 +1,59 @@ +openapi: 3.0.0 +info: + title: Common List Attributes + version: 'not applicable' +paths: {} +components: + schemas: + ListId: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + + ListType: + type: string + enum: + - binary + - boolean + - byte + - date + - date_nanos + - date_range + - double + - double_range + - float + - float_range + - geo_point + - geo_shape + - half_float + - integer + - integer_range + - ip + - ip_range + - keyword + - long + - long_range + - shape + - short + - text + + ListName: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + + ListDescription: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + + ListMetadata: + type: object + additionalProperties: true + + ListItemId: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + + ListItemValue: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + + ListItemDescription: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/primitives.schema.yaml#/components/schemas/NonEmptyString' + + ListItemMetadata: + type: object + additionalProperties: true diff --git a/packages/kbn-securitysolution-lists-common/api/model/list_schemas.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_schemas.gen.ts similarity index 80% rename from packages/kbn-securitysolution-lists-common/api/model/list_schemas.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_schemas.gen.ts index 14d9547c35b5..cd95d20853c1 100644 --- a/packages/kbn-securitysolution-lists-common/api/model/list_schemas.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_schemas.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-lists-common/api/model/list_schemas.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_schemas.schema.yaml similarity index 100% rename from packages/kbn-securitysolution-lists-common/api/model/list_schemas.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/model/list_schemas.schema.yaml diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.gen.ts new file mode 100644 index 000000000000..f2a67181d396 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Patch list API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { ListId, ListName, ListDescription, ListMetadata } from '../model/list_common.gen'; +import { List } from '../model/list_schemas.gen'; + +export type PatchListRequestBody = z.infer<typeof PatchListRequestBody>; +export const PatchListRequestBody = z.object({ + id: ListId, + name: ListName.optional(), + description: ListDescription.optional(), + meta: ListMetadata.optional(), + version: z.number().int().min(1).optional(), + _version: z.string().optional(), +}); +export type PatchListRequestBodyInput = z.input<typeof PatchListRequestBody>; + +export type PatchListResponse = z.infer<typeof PatchListResponse>; +export const PatchListResponse = List; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.schema.yaml new file mode 100644 index 000000000000..6a61e668ced8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list/patch_list.schema.yaml @@ -0,0 +1,74 @@ +openapi: 3.0.0 +info: + title: Patch list API endpoint + version: '2023-10-31' +paths: + /api/lists: + patch: + x-labels: [serverless, ess] + operationId: PatchList + x-codegen-enabled: true + summary: Patch a list + description: Update specific fields of an existing list using the list ID. + requestBody: + description: List's properties + required: true + content: + application/json: + schema: + type: object + properties: + id: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + name: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListName' + description: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListDescription' + meta: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListMetadata' + version: + type: integer + minimum: 1 + _version: + type: string + required: + - id + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/list_schemas.schema.yaml#/components/schemas/List' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.gen.ts new file mode 100644 index 000000000000..e5c06ddd7c25 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.gen.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Patch list item API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { ListItemId, ListItemValue, ListItemMetadata } from '../model/list_common.gen'; +import { ListItem } from '../model/list_schemas.gen'; + +export type PatchListItemRequestBody = z.infer<typeof PatchListItemRequestBody>; +export const PatchListItemRequestBody = z.object({ + id: ListItemId, + value: ListItemValue.optional(), + meta: ListItemMetadata.optional(), + _version: z.string().optional(), + /** + * Determines when changes made by the request are made visible to search + */ + refresh: z.enum(['true', 'false', 'wait_for']).optional(), +}); +export type PatchListItemRequestBodyInput = z.input<typeof PatchListItemRequestBody>; + +export type PatchListItemResponse = z.infer<typeof PatchListItemResponse>; +export const PatchListItemResponse = ListItem; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.schema.yaml new file mode 100644 index 000000000000..fdd7a020d098 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/patch_list_item/patch_list_item.schema.yaml @@ -0,0 +1,76 @@ +openapi: 3.0.0 +info: + title: Patch list item API endpoint + version: '2023-10-31' +paths: + /api/lists/items: + patch: + x-labels: [serverless, ess] + operationId: PatchListItem + x-codegen-enabled: true + summary: Patch a list item + description: Update specific fields of an existing list item using the list item ID. + requestBody: + description: List item's properties + required: true + content: + application/json: + schema: + type: object + properties: + id: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemId' + value: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemValue' + meta: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemMetadata' + _version: + type: string + refresh: + type: string + enum: + - 'true' + - 'false' + - wait_for + description: Determines when changes made by the request are made visible to search + required: + - id + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List item not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/quickstart_client.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/quickstart_client.gen.ts new file mode 100644 index 000000000000..232f4b00540c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/quickstart_client.gen.ts @@ -0,0 +1,428 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Lists API client for quickstart + * version: Bundle (no version) + */ + +import type { KbnClient } from '@kbn/test'; +import { ToolingLog } from '@kbn/tooling-log'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { catchAxiosErrorFormatAndThrow } from '@kbn/securitysolution-utils'; + +import type { CreateListIndexResponse } from './create_list_index/create_list_index.gen'; +import type { + CreateListItemRequestBodyInput, + CreateListItemResponse, +} from './create_list_item/create_list_item.gen'; +import type { CreateListRequestBodyInput, CreateListResponse } from './create_list/create_list.gen'; +import type { DeleteListIndexResponse } from './delete_list_index/delete_list_index.gen'; +import type { + DeleteListItemRequestQueryInput, + DeleteListItemResponse, +} from './delete_list_item/delete_list_item.gen'; +import type { + DeleteListRequestQueryInput, + DeleteListResponse, +} from './delete_list/delete_list.gen'; +import type { ExportListItemsRequestQueryInput } from './export_list_items/export_list_items.gen'; +import type { + FindListItemsRequestQueryInput, + FindListItemsResponse, +} from './find_list_items/find_list_items.gen'; +import type { FindListsRequestQueryInput, FindListsResponse } from './find_lists/find_lists.gen'; +import type { + ImportListItemsRequestQueryInput, + ImportListItemsResponse, +} from './import_list_items/import_list_items.gen'; +import type { + PatchListItemRequestBodyInput, + PatchListItemResponse, +} from './patch_list_item/patch_list_item.gen'; +import type { PatchListRequestBodyInput, PatchListResponse } from './patch_list/patch_list.gen'; +import type { ReadListIndexResponse } from './read_list_index/read_list_index.gen'; +import type { + ReadListItemRequestQueryInput, + ReadListItemResponse, +} from './read_list_item/read_list_item.gen'; +import type { ReadListPrivilegesResponse } from './read_list_privileges/read_list_privileges.gen'; +import type { ReadListRequestQueryInput, ReadListResponse } from './read_list/read_list.gen'; +import type { + UpdateListItemRequestBodyInput, + UpdateListItemResponse, +} from './update_list_item/update_list_item.gen'; +import type { UpdateListRequestBodyInput, UpdateListResponse } from './update_list/update_list.gen'; + +export interface ClientOptions { + kbnClient: KbnClient; + log: ToolingLog; +} + +export class Client { + readonly kbnClient: KbnClient; + readonly log: ToolingLog; + + constructor(options: ClientOptions) { + this.kbnClient = options.kbnClient; + this.log = options.log; + } + /** + * Create a new list. + */ + async createList(props: CreateListProps) { + this.log.info(`${new Date().toISOString()} Calling API CreateList`); + return this.kbnClient + .request<CreateListResponse>({ + path: '/api/lists', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Create `.lists` and `.items` data streams in the relevant space. + */ + async createListIndex() { + this.log.info(`${new Date().toISOString()} Calling API CreateListIndex`); + return this.kbnClient + .request<CreateListIndexResponse>({ + path: '/api/lists/index', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Create a list item and associate it with the specified list. + +All list items in the same list must be the same type. For example, each list item in an `ip` list must define a specific IP address. +> info +> Before creating a list item, you must create a list. + + */ + async createListItem(props: CreateListItemProps) { + this.log.info(`${new Date().toISOString()} Calling API CreateListItem`); + return this.kbnClient + .request<CreateListItemResponse>({ + path: '/api/lists/items', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Delete a list using the list ID. +> info +> When you delete a list, all of its list items are also deleted. + + */ + async deleteList(props: DeleteListProps) { + this.log.info(`${new Date().toISOString()} Calling API DeleteList`); + return this.kbnClient + .request<DeleteListResponse>({ + path: '/api/lists', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'DELETE', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Delete the `.lists` and `.items` data streams. + */ + async deleteListIndex() { + this.log.info(`${new Date().toISOString()} Calling API DeleteListIndex`); + return this.kbnClient + .request<DeleteListIndexResponse>({ + path: '/api/lists/index', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'DELETE', + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Delete a list item using its `id`, or its `list_id` and `value` fields. + */ + async deleteListItem(props: DeleteListItemProps) { + this.log.info(`${new Date().toISOString()} Calling API DeleteListItem`); + return this.kbnClient + .request<DeleteListItemResponse>({ + path: '/api/lists/items', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'DELETE', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Export list item values from the specified list. + */ + async exportListItems(props: ExportListItemsProps) { + this.log.info(`${new Date().toISOString()} Calling API ExportListItems`); + return this.kbnClient + .request({ + path: '/api/lists/items/_export', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Get all list items in the specified list. + */ + async findListItems(props: FindListItemsProps) { + this.log.info(`${new Date().toISOString()} Calling API FindListItems`); + return this.kbnClient + .request<FindListItemsResponse>({ + path: '/api/lists/items/_find', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Get a paginated subset of lists. By default, the first page is returned, with 20 results per page. + */ + async findLists(props: FindListsProps) { + this.log.info(`${new Date().toISOString()} Calling API FindLists`); + return this.kbnClient + .request<FindListsResponse>({ + path: '/api/lists/_find', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Import list items from a TXT or CSV file. The maximum file size is 9 million bytes. + +You can import items to a new or existing list. + + */ + async importListItems(props: ImportListItemsProps) { + this.log.info(`${new Date().toISOString()} Calling API ImportListItems`); + return this.kbnClient + .request<ImportListItemsResponse>({ + path: '/api/lists/items/_import', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + body: props.attachment, + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Update specific fields of an existing list using the list ID. + */ + async patchList(props: PatchListProps) { + this.log.info(`${new Date().toISOString()} Calling API PatchList`); + return this.kbnClient + .request<PatchListResponse>({ + path: '/api/lists', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'PATCH', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Update specific fields of an existing list item using the list item ID. + */ + async patchListItem(props: PatchListItemProps) { + this.log.info(`${new Date().toISOString()} Calling API PatchListItem`); + return this.kbnClient + .request<PatchListItemResponse>({ + path: '/api/lists/items', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'PATCH', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Get the details of a list using the list ID. + */ + async readList(props: ReadListProps) { + this.log.info(`${new Date().toISOString()} Calling API ReadList`); + return this.kbnClient + .request<ReadListResponse>({ + path: '/api/lists', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Verify that `.lists` and `.items` data streams exist. + */ + async readListIndex() { + this.log.info(`${new Date().toISOString()} Calling API ReadListIndex`); + return this.kbnClient + .request<ReadListIndexResponse>({ + path: '/api/lists/index', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Get the details of a list item. + */ + async readListItem(props: ReadListItemProps) { + this.log.info(`${new Date().toISOString()} Calling API ReadListItem`); + return this.kbnClient + .request<ReadListItemResponse>({ + path: '/api/lists/items', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + async readListPrivileges() { + this.log.info(`${new Date().toISOString()} Calling API ReadListPrivileges`); + return this.kbnClient + .request<ReadListPrivilegesResponse>({ + path: '/api/lists/privileges', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Update a list using the list ID. The original list is replaced, and all unspecified fields are deleted. +> info +> You cannot modify the `id` value. + + */ + async updateList(props: UpdateListProps) { + this.log.info(`${new Date().toISOString()} Calling API UpdateList`); + return this.kbnClient + .request<UpdateListResponse>({ + path: '/api/lists', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'PUT', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } + /** + * Update a list item using the list item ID. The original list item is replaced, and all unspecified fields are deleted. +> info +> You cannot modify the `id` value. + + */ + async updateListItem(props: UpdateListItemProps) { + this.log.info(`${new Date().toISOString()} Calling API UpdateListItem`); + return this.kbnClient + .request<UpdateListItemResponse>({ + path: '/api/lists/items', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'PUT', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } +} + +export interface CreateListProps { + body: CreateListRequestBodyInput; +} +export interface CreateListItemProps { + body: CreateListItemRequestBodyInput; +} +export interface DeleteListProps { + query: DeleteListRequestQueryInput; +} +export interface DeleteListItemProps { + query: DeleteListItemRequestQueryInput; +} +export interface ExportListItemsProps { + query: ExportListItemsRequestQueryInput; +} +export interface FindListItemsProps { + query: FindListItemsRequestQueryInput; +} +export interface FindListsProps { + query: FindListsRequestQueryInput; +} +export interface ImportListItemsProps { + query: ImportListItemsRequestQueryInput; + attachment: FormData; +} +export interface PatchListProps { + body: PatchListRequestBodyInput; +} +export interface PatchListItemProps { + body: PatchListItemRequestBodyInput; +} +export interface ReadListProps { + query: ReadListRequestQueryInput; +} +export interface ReadListItemProps { + query: ReadListItemRequestQueryInput; +} +export interface UpdateListProps { + body: UpdateListRequestBodyInput; +} +export interface UpdateListItemProps { + body: UpdateListItemRequestBodyInput; +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list/read_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list/read_list.gen.ts new file mode 100644 index 000000000000..d2967d71d57e --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list/read_list.gen.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Read list API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { ListId } from '../model/list_common.gen'; +import { List } from '../model/list_schemas.gen'; + +export type ReadListRequestQuery = z.infer<typeof ReadListRequestQuery>; +export const ReadListRequestQuery = z.object({ + /** + * List's `id` value + */ + id: ListId, +}); +export type ReadListRequestQueryInput = z.input<typeof ReadListRequestQuery>; + +export type ReadListResponse = z.infer<typeof ReadListResponse>; +export const ReadListResponse = List; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list/read_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list/read_list.schema.yaml new file mode 100644 index 000000000000..280a6fdab754 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list/read_list.schema.yaml @@ -0,0 +1,58 @@ +openapi: 3.0.0 +info: + title: Read list API endpoint + version: '2023-10-31' +paths: + /api/lists: + get: + x-labels: [serverless, ess] + operationId: ReadList + x-codegen-enabled: true + summary: Get list details + description: Get the details of a list using the list ID. + parameters: + - name: id + in: query + required: true + description: List's `id` value + schema: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/list_schemas.schema.yaml#/components/schemas/List' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.gen.ts new file mode 100644 index 000000000000..bf0aec4dc6bc --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Read list DS existence status API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +export type ReadListIndexResponse = z.infer<typeof ReadListIndexResponse>; +export const ReadListIndexResponse = z.object({ + list_index: z.boolean(), + list_item_index: z.boolean(), +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.schema.yaml new file mode 100644 index 000000000000..40dbddf25e69 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_index/read_list_index.schema.yaml @@ -0,0 +1,57 @@ +openapi: 3.0.0 +info: + title: Read list DS existence status API endpoint + version: '2023-10-31' +paths: + /api/lists/index: + get: + x-labels: [serverless, ess] + operationId: ReadListIndex + x-codegen-enabled: true + summary: Get status of list data streams + description: Verify that `.lists` and `.items` data streams exist. + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: object + properties: + list_index: + type: boolean + list_item_index: + type: boolean + required: [list_index, list_item_index] + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List data stream(s) not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.gen.ts new file mode 100644 index 000000000000..d22ee4602226 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.gen.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Read list item API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { ListId } from '../model/list_common.gen'; +import { ListItem } from '../model/list_schemas.gen'; + +export type ReadListItemRequestQuery = z.infer<typeof ReadListItemRequestQuery>; +export const ReadListItemRequestQuery = z.object({ + /** + * Required if `list_id` and `value` are not specified + */ + id: ListId.optional(), + /** + * Required if `id` is not specified + */ + list_id: ListId.optional(), + /** + * Required if `id` is not specified + */ + value: z.string().optional(), +}); +export type ReadListItemRequestQueryInput = z.input<typeof ReadListItemRequestQuery>; + +export type ReadListItemResponse = z.infer<typeof ReadListItemResponse>; +export const ReadListItemResponse = z.union([ListItem, z.array(ListItem)]); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.schema.yaml new file mode 100644 index 000000000000..a41fb497f611 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_item/read_list_item.schema.yaml @@ -0,0 +1,74 @@ +openapi: 3.0.0 +info: + title: Read list item API endpoint + version: '2023-10-31' +paths: + /api/lists/items: + get: + x-labels: [serverless, ess] + operationId: ReadListItem + x-codegen-enabled: true + summary: Get a list item + description: Get the details of a list item. + parameters: + - name: id + in: query + required: false + description: Required if `list_id` and `value` are not specified + schema: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + - name: list_id + in: query + required: false + description: Required if `id` is not specified + schema: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + - name: value + in: query + required: false + description: Required if `id` is not specified + schema: + type: string + responses: + 200: + description: Successful response + content: + application/json: + schema: + oneOf: + - $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' + - type: array + items: + $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List item not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.gen.ts similarity index 76% rename from packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.gen.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.gen.ts index 356915ba03cf..da6e7e95076d 100644 --- a/packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.gen.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.gen.ts @@ -1,10 +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", 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". + * 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. */ /* diff --git a/packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.schema.yaml similarity index 78% rename from packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.schema.yaml index ec8604e80694..df7b4a5f5174 100644 --- a/packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.schema.yaml +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/read_list_privileges/read_list_privileges.schema.yaml @@ -33,26 +33,26 @@ paths: application/json: schema: oneOf: - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' - - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 401: description: Unsuccessful authentication response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 403: description: Not enough privileges response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' 500: description: Internal server error response content: application/json: schema: - $ref: '../../../kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' components: schemas: diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list/update_list.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list/update_list.gen.ts new file mode 100644 index 000000000000..ffb28c3d0b53 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list/update_list.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Update list API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { ListId, ListName, ListDescription, ListMetadata } from '../model/list_common.gen'; +import { List } from '../model/list_schemas.gen'; + +export type UpdateListRequestBody = z.infer<typeof UpdateListRequestBody>; +export const UpdateListRequestBody = z.object({ + id: ListId, + name: ListName, + description: ListDescription, + meta: ListMetadata.optional(), + version: z.number().int().min(1).optional(), + _version: z.string().optional(), +}); +export type UpdateListRequestBodyInput = z.input<typeof UpdateListRequestBody>; + +export type UpdateListResponse = z.infer<typeof UpdateListResponse>; +export const UpdateListResponse = List; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list/update_list.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list/update_list.schema.yaml new file mode 100644 index 000000000000..a059ede38584 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list/update_list.schema.yaml @@ -0,0 +1,79 @@ +openapi: 3.0.0 +info: + title: Update list API endpoint + version: '2023-10-31' +paths: + /api/lists: + put: + x-labels: [serverless, ess] + operationId: UpdateList + x-codegen-enabled: true + summary: Update a list + description: | + Update a list using the list ID. The original list is replaced, and all unspecified fields are deleted. + > info + > You cannot modify the `id` value. + requestBody: + description: List's properties + required: true + content: + application/json: + schema: + type: object + properties: + id: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListId' + name: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListName' + description: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListDescription' + meta: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListMetadata' + version: + type: integer + minimum: 1 + _version: + type: string + required: + - id + - name + - description + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/list_schemas.schema.yaml#/components/schemas/List' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.gen.ts b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.gen.ts new file mode 100644 index 000000000000..f5667676ab4c --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.gen.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Update list item API endpoint + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { ListItemId, ListItemValue, ListItemMetadata } from '../model/list_common.gen'; +import { ListItem } from '../model/list_schemas.gen'; + +export type UpdateListItemRequestBody = z.infer<typeof UpdateListItemRequestBody>; +export const UpdateListItemRequestBody = z.object({ + id: ListItemId, + value: ListItemValue, + meta: ListItemMetadata.optional(), + _version: z.string().optional(), +}); +export type UpdateListItemRequestBodyInput = z.input<typeof UpdateListItemRequestBody>; + +export type UpdateListItemResponse = z.infer<typeof UpdateListItemResponse>; +export const UpdateListItemResponse = ListItem; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.schema.yaml new file mode 100644 index 000000000000..04d86cf1947a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/update_list_item/update_list_item.schema.yaml @@ -0,0 +1,73 @@ +openapi: 3.0.0 +info: + title: Update list item API endpoint + version: '2023-10-31' +paths: + /api/lists/items: + put: + x-labels: [serverless, ess] + operationId: UpdateListItem + x-codegen-enabled: true + summary: Update a list item + description: | + Update a list item using the list item ID. The original list item is replaced, and all unspecified fields are deleted. + > info + > You cannot modify the `id` value. + requestBody: + description: List item's properties + required: true + content: + application/json: + schema: + type: object + properties: + id: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemId' + value: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemValue' + meta: + $ref: '../model/list_common.schema.yaml#/components/schemas/ListItemMetadata' + _version: + type: string + required: + - id + - value + responses: + 200: + description: Successful response + content: + application/json: + schema: + $ref: '../model/list_schemas.schema.yaml#/components/schemas/ListItem' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 403: + description: Not enough privileges response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 404: + description: List item not found response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../../../../../packages/kbn-openapi-common/schemas/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' diff --git a/packages/kbn-securitysolution-lists-common/docs/openapi/ess/security_solution_lists_api_2023_10_31.bundled.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/docs/openapi/ess/security_solution_lists_api_2023_10_31.bundled.schema.yaml similarity index 100% rename from packages/kbn-securitysolution-lists-common/docs/openapi/ess/security_solution_lists_api_2023_10_31.bundled.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/docs/openapi/ess/security_solution_lists_api_2023_10_31.bundled.schema.yaml diff --git a/packages/kbn-securitysolution-lists-common/docs/openapi/serverless/security_solution_lists_api_2023_10_31.bundled.schema.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/docs/openapi/serverless/security_solution_lists_api_2023_10_31.bundled.schema.yaml similarity index 100% rename from packages/kbn-securitysolution-lists-common/docs/openapi/serverless/security_solution_lists_api_2023_10_31.bundled.schema.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/docs/openapi/serverless/security_solution_lists_api_2023_10_31.bundled.schema.yaml diff --git a/packages/kbn-securitysolution-lists-common/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-lists-common/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/package.json new file mode 100644 index 000000000000..4e2b03521859 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/package.json @@ -0,0 +1,11 @@ +{ + "description": "Security Solution Lists common package", + "license": "Elastic License 2.0", + "name": "@kbn/securitysolution-lists-common", + "private": true, + "version": "1.0.0", + "scripts": { + "openapi:generate": "node scripts/openapi_generate", + "openapi:bundle": "node scripts/openapi_bundle" + } +} diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle.js b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle.js new file mode 100644 index 000000000000..609ed8bb0807 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle.js @@ -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. + */ + +require('../../../../../../src/setup_node_env'); +// eslint-disable-next-line import/no-nodejs-modules +const { join, resolve } = require('path'); +const { bundle } = require('@kbn/openapi-bundler'); + +const ROOT = resolve(__dirname, '..'); + +(async () => { + await bundle({ + sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), + outputFilePath: join( + ROOT, + 'docs/openapi/serverless/security_solution_lists_api_{version}.bundled.schema.yaml' + ), + options: { + includeLabels: ['serverless'], + prototypeDocument: join(ROOT, 'scripts/openapi_bundle_info/lists_serverless.info.yaml'), + }, + }); + + await bundle({ + sourceGlob: join(ROOT, 'api/**/*.schema.yaml'), + outputFilePath: join( + ROOT, + 'docs/openapi/ess/security_solution_lists_api_{version}.bundled.schema.yaml' + ), + options: { + includeLabels: ['ess'], + prototypeDocument: join(ROOT, 'scripts/openapi_bundle_info/lists_ess.info.yaml'), + }, + }); +})(); diff --git a/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle_info/lists_ess.info.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle_info/lists_ess.info.yaml similarity index 100% rename from packages/kbn-securitysolution-lists-common/scripts/openapi_bundle_info/lists_ess.info.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle_info/lists_ess.info.yaml diff --git a/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle_info/lists_serverless.info.yaml b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle_info/lists_serverless.info.yaml similarity index 100% rename from packages/kbn-securitysolution-lists-common/scripts/openapi_bundle_info/lists_serverless.info.yaml rename to x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle_info/lists_serverless.info.yaml diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_generate.js b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_generate.js new file mode 100644 index 000000000000..ecda1a791e1f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_generate.js @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +require('../../../../../../src/setup_node_env'); +// eslint-disable-next-line import/no-nodejs-modules +const { join, resolve } = require('path'); +const { generate } = require('@kbn/openapi-generator'); +const { REPO_ROOT } = require('@kbn/repo-info'); + +const ROOT = resolve(__dirname, '..'); + +(async () => { + await generate({ + title: 'OpenAPI Lists API Schemas', + rootDir: ROOT, + sourceGlob: './api/**/*.schema.yaml', + templateName: 'zod_operation_schema', + }); + + await generate({ + title: 'Lists API client for tests', + rootDir: ROOT, + sourceGlob: './api/**/*.schema.yaml', + templateName: 'api_client_supertest', + skipLinting: true, + bundle: { + outFile: join( + REPO_ROOT, + 'x-pack/test/api_integration/services/security_solution_lists_api.gen.ts' + ), + }, + }); + + await generate({ + title: 'Lists API client for quickstart', + rootDir: ROOT, + sourceGlob: './api/**/*.schema.yaml', + templateName: 'api_client_quickstart', + skipLinting: true, + bundle: { + outFile: join( + REPO_ROOT, + 'x-pack/solutions/security/packages/kbn-securitysolution-lists-common/api/quickstart_client.gen.ts' + ), + }, + }); +})(); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/tsconfig.json new file mode 100644 index 000000000000..6a6637ff64a2 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-lists-common/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "outDir": "target/types", + "types": ["jest", "node"] + }, + "exclude": ["target/**/*"], + "extends": "../../../../../tsconfig.base.json", + "include": ["**/*.ts"], + "kbn_references": [ + "@kbn/zod-helpers", + "@kbn/openapi-common", + "@kbn/test", + "@kbn/tooling-log", + "@kbn/core-http-common", + "@kbn/securitysolution-utils", + "@kbn/zod", + ] +} diff --git a/packages/kbn-securitysolution-t-grid/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/README.md similarity index 100% rename from packages/kbn-securitysolution-t-grid/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-t-grid/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/index.ts new file mode 100644 index 000000000000..371d48e06c14 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/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 * from './src/constants'; +export * from './src/utils'; +// eslint-disable-next-line @kbn/imports/no_boundary_crossing +export * from './src/mock'; diff --git a/packages/kbn-securitysolution-t-grid/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-t-grid/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-t-grid/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/package.json new file mode 100644 index 000000000000..d2f04084da36 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/securitysolution-t-grid", + "version": "1.0.0", + "description": "security solution t-grid packages will allow sharing components between timelines and security_solution plugin until we transfer all functionality to timelines plugin", + "license": "Elastic License 2.0", + "private": true +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/constants/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/constants/index.ts new file mode 100644 index 000000000000..a57893d63995 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/constants/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const HIGHLIGHTED_DROP_TARGET_CLASS_NAME = 'highlighted-drop-target'; +export const EMPTY_PROVIDERS_GROUP_CLASS_NAME = 'empty-providers-group'; + +/** The draggable will move this many pixels via the keyboard when the arrow key is pressed */ +export const KEYBOARD_DRAG_OFFSET = 20; + +export const DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME = 'draggable-keyboard-wrapper'; + +export const ROW_RENDERER_CLASS_NAME = 'row-renderer'; + +export const NOTES_CONTAINER_CLASS_NAME = 'notes-container'; + +export const NOTE_CONTENT_CLASS_NAME = 'note-content'; + +/** This class is added to the document body while dragging */ +export const IS_DRAGGING_CLASS_NAME = 'is-dragging'; + +export const HOVER_ACTIONS_ALWAYS_SHOW_CLASS_NAME = 'hover-actions-always-show'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/mock/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/mock/index.ts new file mode 100644 index 000000000000..770bf1ffb018 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/mock/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 * from './mock_event_details'; diff --git a/packages/kbn-securitysolution-t-grid/src/mock/mock_event_details.ts b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/mock/mock_event_details.ts similarity index 97% rename from packages/kbn-securitysolution-t-grid/src/mock/mock_event_details.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/mock/mock_event_details.ts index 5152132cda4b..538f6fa2cadc 100644 --- a/packages/kbn-securitysolution-t-grid/src/mock/mock_event_details.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/mock/mock_event_details.ts @@ -1,10 +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", 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". + * 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 eventHit = { diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/utils/api/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/utils/api/index.ts new file mode 100644 index 000000000000..add7df10fe13 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/utils/api/index.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { has } from 'lodash/fp'; + +export interface AppError extends Error { + body: { + message: string; + }; +} + +export interface KibanaError extends AppError { + body: { + message: string; + statusCode: number; + }; +} + +export interface SecurityAppError extends AppError { + body: { + message: string; + status_code: number; + }; +} + +export const isKibanaError = (error: unknown): error is KibanaError => + has('message', error) && has('body.message', error) && has('body.statusCode', error); + +export const isSecurityAppError = (error: unknown): error is SecurityAppError => + has('message', error) && has('body.message', error) && has('body.status_code', error); + +export const isAppError = (error: unknown): error is AppError => + isKibanaError(error) || isSecurityAppError(error); + +export const isNotFoundError = (error: unknown) => + (isKibanaError(error) && error.body.statusCode === 404) || + (isSecurityAppError(error) && error.body.status_code === 404); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/utils/drag_and_drop/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/utils/drag_and_drop/index.ts new file mode 100644 index 000000000000..d954ebb055f1 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/utils/drag_and_drop/index.ts @@ -0,0 +1,132 @@ +/* + * 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 { DropResult } from '@hello-pangea/dnd'; + +export const draggableIdPrefix = 'draggableId'; + +export const droppableIdPrefix = 'droppableId'; + +export const draggableContentPrefix = `${draggableIdPrefix}.content.`; + +export const draggableTimelineProvidersPrefix = `${draggableIdPrefix}.timelineProviders.`; + +export const draggableFieldPrefix = `${draggableIdPrefix}.field.`; + +export const droppableContentPrefix = `${droppableIdPrefix}.content.`; + +export const droppableFieldPrefix = `${droppableIdPrefix}.field.`; + +export const droppableTimelineProvidersPrefix = `${droppableIdPrefix}.timelineProviders.`; + +export const droppableTimelineColumnsPrefix = `${droppableIdPrefix}.timelineColumns.`; + +export const droppableTimelineFlyoutBottomBarPrefix = `${droppableIdPrefix}.flyoutButton.`; + +export const getDraggableId = (dataProviderId: string): string => + `${draggableContentPrefix}${dataProviderId}`; + +export const getDraggableFieldId = ({ + contextId, + fieldId, +}: { + contextId: string; + fieldId: string; +}): string => `${draggableFieldPrefix}${escapeContextId(contextId)}.${escapeFieldId(fieldId)}`; + +export const getTimelineProviderDroppableId = ({ + groupIndex, + timelineId, +}: { + groupIndex: number; + timelineId: string; +}): string => `${droppableTimelineProvidersPrefix}${timelineId}.group.${groupIndex}`; + +export const getTimelineProviderDraggableId = ({ + dataProviderId, + groupIndex, + timelineId, +}: { + dataProviderId: string; + groupIndex: number; + timelineId: string; +}): string => + `${draggableTimelineProvidersPrefix}${timelineId}.group.${groupIndex}.${dataProviderId}`; + +export const getDroppableId = (visualizationPlaceholderId: string): string => + `${droppableContentPrefix}${visualizationPlaceholderId}`; + +export const sourceIsContent = (result: DropResult): boolean => + result.source.droppableId.startsWith(droppableContentPrefix); + +export const sourceAndDestinationAreSameTimelineProviders = (result: DropResult): boolean => { + const regex = /^droppableId\.timelineProviders\.(\S+)\./; + const sourceMatches = result.source.droppableId.match(regex) || []; + const destinationMatches = + (result.destination && result.destination.droppableId.match(regex)) || []; + + return ( + sourceMatches.length >= 2 && + destinationMatches.length >= 2 && + sourceMatches[1] === destinationMatches[1] + ); +}; + +export const draggableIsContent = (result: DropResult | { draggableId: string }): boolean => + result.draggableId.startsWith(draggableContentPrefix); + +export const draggableIsField = (result: DropResult | { draggableId: string }): boolean => + result.draggableId.startsWith(draggableFieldPrefix); + +export const reasonIsDrop = (result: DropResult): boolean => result.reason === 'DROP'; + +export const destinationIsTimelineProviders = (result: DropResult): boolean => + result.destination != null && + result.destination.droppableId.startsWith(droppableTimelineProvidersPrefix); + +export const destinationIsTimelineColumns = (result: DropResult): boolean => + result.destination != null && + result.destination.droppableId.startsWith(droppableTimelineColumnsPrefix); + +export const destinationIsTimelineButton = (result: DropResult): boolean => + result.destination != null && + result.destination.droppableId.startsWith(droppableTimelineFlyoutBottomBarPrefix); + +export const getProviderIdFromDraggable = (result: DropResult): string => + result.draggableId.substring(result.draggableId.lastIndexOf('.') + 1); + +export const getFieldIdFromDraggable = (result: DropResult): string => + unEscapeFieldId(result.draggableId.substring(result.draggableId.lastIndexOf('.') + 1)); + +export const escapeDataProviderId = (path: string) => path.replace(/\./g, '_'); + +export const escapeContextId = (path: string) => path.replace(/\./g, '_'); + +export const escapeFieldId = (path: string) => path.replace(/\./g, '!!!DOT!!!'); + +export const unEscapeFieldId = (path: string) => path.replace(/!!!DOT!!!/g, '.'); + +export const providerWasDroppedOnTimeline = (result: DropResult): boolean => + reasonIsDrop(result) && + draggableIsContent(result) && + sourceIsContent(result) && + destinationIsTimelineProviders(result); + +export const userIsReArrangingProviders = (result: DropResult): boolean => + reasonIsDrop(result) && sourceAndDestinationAreSameTimelineProviders(result); + +export const fieldWasDroppedOnTimelineColumns = (result: DropResult): boolean => + reasonIsDrop(result) && draggableIsField(result) && destinationIsTimelineColumns(result); + +/** + * Prevents fields from being dragged or dropped to any area other than column + * header drop zone in the timeline + */ +export const DRAG_TYPE_FIELD = 'drag-type-field'; + +/** This class is added to the document body while timeline field dragging */ +export const IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME = 'is-timeline-field-dragging'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/utils/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/utils/index.ts new file mode 100644 index 000000000000..cafe8ec570c8 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/src/utils/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 './api'; +export * from './drag_and_drop'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/tsconfig.json new file mode 100644 index 000000000000..131ff3e6bb43 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-t-grid/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-securitysolution-utils/README.md b/x-pack/solutions/security/packages/kbn-securitysolution-utils/README.md similarity index 100% rename from packages/kbn-securitysolution-utils/README.md rename to x-pack/solutions/security/packages/kbn-securitysolution-utils/README.md diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/index.ts new file mode 100644 index 000000000000..dcf5ee7c4779 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/index.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 * from './src/add_remove_id_to_item'; +export * from './src/axios'; +export * from './src/transform_data_to_ndjson'; +export * from './src/path_validations'; +export * from './src/esql'; +export * from './src/debounce_async/debounce_async'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/jest.config.js b/x-pack/solutions/security/packages/kbn-securitysolution-utils/jest.config.js new file mode 100644 index 000000000000..c25893390c68 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/solutions/security/packages/kbn-securitysolution-utils'], +}; diff --git a/packages/kbn-securitysolution-utils/kibana.jsonc b/x-pack/solutions/security/packages/kbn-securitysolution-utils/kibana.jsonc similarity index 100% rename from packages/kbn-securitysolution-utils/kibana.jsonc rename to x-pack/solutions/security/packages/kbn-securitysolution-utils/kibana.jsonc diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/package.json b/x-pack/solutions/security/packages/kbn-securitysolution-utils/package.json new file mode 100644 index 000000000000..d9311ef37a28 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/securitysolution-utils", + "version": "1.0.0", + "description": "security solution utilities to use across plugins such lists, security_solution, cases, etc...", + "license": "Elastic License 2.0", + "private": true, + "sideEffects": false +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.test.ts new file mode 100644 index 000000000000..13d13b2acf89 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.test.ts @@ -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 { addIdToItem, removeIdFromItem } from '.'; + +jest.mock('uuid', () => ({ + v4: jest.fn().mockReturnValue('123'), +})); + +describe('add_remove_id_to_item', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('addIdToItem', () => { + test('it adds an id to an empty item', () => { + expect(addIdToItem({})).toEqual({ id: '123' }); + }); + + test('it adds a complex object', () => { + expect( + addIdToItem({ + field: '', + type: 'mapping', + value: '', + }) + ).toEqual({ + id: '123', + field: '', + type: 'mapping', + value: '', + }); + }); + + test('it adds an id to an existing item', () => { + expect(addIdToItem({ test: '456' })).toEqual({ id: '123', test: '456' }); + }); + + test('it does not change the id if it already exists', () => { + expect(addIdToItem({ id: '456' })).toEqual({ id: '456' }); + }); + + test('it returns the same reference if it has an id already', () => { + const obj = { id: '456' }; + expect(addIdToItem(obj)).toBe(obj); + }); + + test('it returns a new reference if it adds an id to an item', () => { + const obj = { test: '456' }; + expect(addIdToItem(obj)).not.toBe(obj); + }); + }); + + describe('removeIdFromItem', () => { + test('it removes an id from an item', () => { + expect(removeIdFromItem({ id: '456' })).toEqual({}); + }); + + test('it returns a new reference if it removes an id from an item', () => { + const obj = { id: '123', test: '456' }; + expect(removeIdFromItem(obj)).not.toBe(obj); + }); + + test('it does not effect an item without an id', () => { + expect(removeIdFromItem({ test: '456' })).toEqual({ test: '456' }); + }); + + test('it returns the same reference if it does not have an id already', () => { + const obj = { test: '456' }; + expect(removeIdFromItem(obj)).toBe(obj); + }); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.ts new file mode 100644 index 000000000000..5133a13bdbec --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/add_remove_id_to_item/index.ts @@ -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 { v4 as uuidv4 } from 'uuid'; + +/** + * This is useful for when you have arrays without an ID and need to add one for + * ReactJS keys. I break the types slightly by introducing an id to an arbitrary item + * but then cast it back to the regular type T. + * Usage of this could be considered tech debt as I am adding an ID when the backend + * could be doing the same thing but it depends on how you want to model your data and + * if you view modeling your data with id's to please ReactJS a good or bad thing. + * @param item The item to add an id to. + */ +type NotArray<T> = T extends unknown[] ? never : T; +export const addIdToItem = <T>(item: NotArray<T>): T => { + const maybeId = item as typeof item & { id?: string }; + if (maybeId.id != null) { + return item; + } else { + return { ...item, id: uuidv4() }; + } +}; + +/** + * This is to reverse the id you added to your arrays for ReactJS keys. + * @param item The item to remove the id from. + */ +export const removeIdFromItem = <T>( + item: NotArray<T> +): + | T + | Pick< + T & { + id?: string | undefined; + }, + Exclude<keyof T, 'id'> + > => { + const maybeId = item as typeof item & { id?: string }; + if (maybeId.id != null) { + const { id, ...noId } = maybeId; + return noId; + } else { + return item; + } +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/axios/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/axios/index.ts new file mode 100644 index 000000000000..44972dba9356 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/axios/index.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AxiosError } from 'axios'; + +export class FormattedAxiosError extends Error { + public readonly request: { + method: string; + url: string; + data: unknown; + }; + public readonly response: { + status: number; + statusText: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data: any; + }; + + constructor(axiosError: AxiosError) { + const method = axiosError.config?.method ?? ''; + const url = axiosError.config?.url ?? ''; + + super( + `${axiosError.message}${ + axiosError?.response?.data ? `: ${JSON.stringify(axiosError?.response?.data)}` : '' + }${url ? `\n(Request: ${method} ${url})` : ''}` + ); + + this.request = { + method, + url, + data: axiosError.config?.data ?? '', + }; + + this.response = { + status: axiosError?.response?.status ?? 0, + statusText: axiosError?.response?.statusText ?? '', + data: axiosError?.response?.data, + }; + + this.name = this.constructor.name; + } + + toJSON() { + return { + message: this.message, + request: this.request, + response: this.response, + }; + } + + toString() { + return JSON.stringify(this.toJSON(), null, 2); + } +} + +/** + * Used with `promise.catch()`, it will format the Axios error to a new error and will re-throw + * @param error + */ +export const catchAxiosErrorFormatAndThrow = (error: Error): never => { + if (error instanceof AxiosError) { + throw new FormattedAxiosError(error); + } + + throw error; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/client_concurrency/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/client_concurrency/index.ts new file mode 100644 index 000000000000..3a44e5061d09 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/client_concurrency/index.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import limit from 'p-limit'; + +/** + * This type is just an async function's type + */ +type RequestFactory<Output> = () => Promise<Output>; + +/** + * Helper function to call a large number of async functions with limited concurrency. + * Example pattern of how to create functions to pass in: + * + * const ruleCopies = duplicateRuleParams(basicRule, 200); + * const functions = ruleCopies.map((rule) => () => detectionsClient.createRule({ body: rule })); + * + * Note that the `map` call in the example returns a *function* that calls detectionsClient.createRule, it doesn't call createRule immediately. + * + * @param functions Async functions to call with limited concurrency + * @param concurrency Maximum number of concurrent function calls + * @returns Results from all functions passed in + */ +export const concurrentlyExec = async <Output>( + requestFactories: Array<RequestFactory<Output>>, + concurrency: number = 10 +) => { + const limiter = limit(concurrency); + const promises = requestFactories.map((f) => limiter(f)); + return Promise.all(promises); +}; diff --git a/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.test.ts similarity index 77% rename from packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.test.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.test.ts index d7e1201e44e8..7508e99a891f 100644 --- a/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.test.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.test.ts @@ -1,10 +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", 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". + * 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 { debounceAsync } from './debounce_async'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.ts new file mode 100644 index 000000000000..ac9f2348d0be --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/debounce_async/debounce_async.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Unlike lodash's debounce, which resolves intermediate calls with the most + * recent value, this implementation waits to resolve intermediate calls until + * the next invocation resolves. + * + * @param fn an async function + * + * @returns A debounced async function that resolves on the next invocation + */ +export function debounceAsync<Args extends unknown[], Result>( + fn: (...args: Args) => Result, + intervalMs: number +): (...args: Args) => Promise<Awaited<Result>> { + let timeoutId: ReturnType<typeof setTimeout> | undefined; + let resolve: (value: Awaited<Result>) => void; + let promise = new Promise<Awaited<Result>>((_resolve) => { + resolve = _resolve; + }); + + return (...args) => { + if (timeoutId) { + clearTimeout(timeoutId); + } + + timeoutId = setTimeout(async () => { + resolve(await fn(...args)); + promise = new Promise((_resolve) => { + resolve = _resolve; + }); + }, intervalMs); + + return promise; + }; +} diff --git a/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.test.ts similarity index 84% rename from packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.test.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.test.ts index cb92d34b33ec..41268ac0fa85 100644 --- a/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.test.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.test.ts @@ -1,10 +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", 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". + * 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 { computeIsESQLQueryAggregating } from './compute_if_esql_query_aggregating'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts new file mode 100644 index 000000000000..d38abdaa481a --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts @@ -0,0 +1,21 @@ +/* + * 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 ESQLAstQueryExpression, parse } from '@kbn/esql-ast'; + +export const isAggregatingQuery = (astExpression: ESQLAstQueryExpression): boolean => + astExpression.commands.some((command) => command.name === 'stats'); + +/** + * compute if esqlQuery is aggregating/grouping, i.e. using STATS...BY command + * @param esqlQuery + * @returns boolean + */ +export const computeIsESQLQueryAggregating = (esqlQuery: string): boolean => { + const { root } = parse(esqlQuery); + return isAggregatingQuery(root); +}; diff --git a/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.test.ts similarity index 80% rename from packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.test.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.test.ts index 7a7726c47e14..39dc934dcdd2 100644 --- a/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.test.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.test.ts @@ -1,10 +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", 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". + * 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 { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.ts new file mode 100644 index 000000000000..654f09cd0b84 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; + +/** + * parses ES|QL query and returns array of indices + */ +export const getIndexListFromEsqlQuery = (query: string | undefined): string[] => { + try { + const indexString = getIndexPatternFromESQLQuery(query); + + return getIndexListFromIndexString(indexString); + } catch (e) { + return []; + } +}; + +/** + * transforms sting of indices, separated by commas to array + * index*, index2* => [index*, index2*] + */ +export const getIndexListFromIndexString = (indexString: string | undefined): string[] => { + if (!indexString) { + return []; + } + return indexString.split(',').map((index) => index.trim()); +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/index.ts new file mode 100644 index 000000000000..ff6f8a455244 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/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 * from './compute_if_esql_query_aggregating'; +export * from './get_index_list_from_esql_query'; +export * from './parse_esql_query'; diff --git a/packages/kbn-securitysolution-utils/src/esql/parse_esql_query.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/parse_esql_query.test.ts similarity index 90% rename from packages/kbn-securitysolution-utils/src/esql/parse_esql_query.test.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/parse_esql_query.test.ts index 6c4fdafd8e70..1e7484fd6e4b 100644 --- a/packages/kbn-securitysolution-utils/src/esql/parse_esql_query.test.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/parse_esql_query.test.ts @@ -1,10 +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", 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". + * 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 { parseEsqlQuery } from './parse_esql_query'; diff --git a/packages/kbn-securitysolution-utils/src/esql/parse_esql_query.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/parse_esql_query.ts similarity index 83% rename from packages/kbn-securitysolution-utils/src/esql/parse_esql_query.ts rename to x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/parse_esql_query.ts index 2a62aed8873a..6e088bf386fb 100644 --- a/packages/kbn-securitysolution-utils/src/esql/parse_esql_query.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/esql/parse_esql_query.ts @@ -1,10 +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", 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". + * 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 ESQLAstQueryExpression, parse, ESQLCommandOption, EditorError } from '@kbn/esql-ast'; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/path_validations/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/path_validations/index.test.ts new file mode 100644 index 000000000000..5304c3a71061 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/path_validations/index.test.ts @@ -0,0 +1,758 @@ +/* + * 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 { + isPathValid, + hasSimpleExecutableName, + OperatingSystem, + ConditionEntryField, + validateWildcardInput, + validateHasWildcardWithWrongOperator, + validatePotentialWildcardInput, + validateFilePathInput, + WILDCARD_WARNING, + FILEPATH_WARNING, +} from '.'; + +describe('validatePotentialWildcardInput', () => { + it('warns on wildcard when field is file.path.text', () => { + expect( + validatePotentialWildcardInput({ + field: 'file.path.text', + os: OperatingSystem.WINDOWS, + value: 'c:\\path*.exe', + }) + ).toEqual(WILDCARD_WARNING); + }); + it('warns on wildcard when field is not file.path.text', () => { + expect( + validatePotentialWildcardInput({ + field: 'event.category', + os: OperatingSystem.WINDOWS, + value: 'some*value', + }) + ).toEqual(WILDCARD_WARNING); + }); +}); + +describe('validateWildcardInput', () => { + it('warns on wildcard for fields that are not file paths', () => { + expect(validateWildcardInput('*')).toEqual(WILDCARD_WARNING); + }); + it('does not warn if no wildcard', () => { + expect(validateWildcardInput('non-wildcard')).toEqual(undefined); + }); +}); + +describe('validateFilePathInput', () => { + describe('windows', () => { + const os = OperatingSystem.WINDOWS; + + it('does not warn on valid filenames', () => { + expect( + validateFilePathInput({ + os, + value: 'C:\\Windows\\*\\FILENAME.EXE-1231205124.gz', + }) + ).not.toBeDefined(); + expect( + validateFilePathInput({ + os, + value: "C:\\Windows\\*\\test$ as2@13---12!@#A,DS.#$^&$!#~ 'as'd.华语.txt", + }) + ).toEqual(undefined); + }); + + it('warns on wildcard in file name at the end of the path', () => { + expect(validateFilePathInput({ os, value: 'c:\\path*.exe' })).toEqual(WILDCARD_WARNING); + expect( + validateFilePathInput({ + os, + value: 'C:\\Windows\\*\\FILENAME.EXE-*.gz', + }) + ).toEqual(WILDCARD_WARNING); + }); + + it('warns on unix paths or non-windows paths', () => { + expect(validateFilePathInput({ os, value: '/opt/bin' })).toEqual(FILEPATH_WARNING); + }); + + it('warns on malformed paths', () => { + expect(validateFilePathInput({ os, value: 'c:\\path/opt' })).toEqual(FILEPATH_WARNING); + expect(validateFilePathInput({ os, value: '1242' })).toEqual(FILEPATH_WARNING); + expect(validateFilePathInput({ os, value: 'w12efdfa' })).toEqual(FILEPATH_WARNING); + expect(validateFilePathInput({ os, value: 'c:\\folder\\' })).toEqual(FILEPATH_WARNING); + }); + }); + describe('unix paths', () => { + const os = + parseInt((Math.random() * 2).toString(), 10) === 1 + ? OperatingSystem.MAC + : OperatingSystem.LINUX; + + it('does not warn on valid filenames', () => { + expect( + validateFilePathInput({ + os, + value: '/opt/*/FILENAME.EXE-1231205124.gz', + }) + ).not.toEqual(WILDCARD_WARNING); + expect( + validateFilePathInput({ + os, + value: "/opt/*/test$ as2@13---12!@#A,DS.#$^&$!#~ 'as'd.华语.txt", + }) + ).not.toEqual(WILDCARD_WARNING); + }); + it('warns on wildcard in file name at the end of the path', () => { + expect(validateFilePathInput({ os, value: '/opt/bin*' })).toEqual(WILDCARD_WARNING); + expect(validateFilePathInput({ os, value: '/opt/FILENAME.EXE-*.gz' })).toEqual( + WILDCARD_WARNING + ); + }); + + it('warns on windows paths', () => { + expect(validateFilePathInput({ os, value: 'd:\\path\\file.exe' })).toEqual(FILEPATH_WARNING); + }); + + it('warns on malformed paths', () => { + expect(validateFilePathInput({ os, value: 'opt/bin\\file.exe' })).toEqual(FILEPATH_WARNING); + expect(validateFilePathInput({ os, value: '1242' })).toEqual(FILEPATH_WARNING); + expect(validateFilePathInput({ os, value: 'w12efdfa' })).toEqual(FILEPATH_WARNING); + expect(validateFilePathInput({ os, value: '/folder/' })).toEqual(FILEPATH_WARNING); + }); + }); +}); + +describe('Wildcard and invalid operator', () => { + it('should return TRUE when operator is not "WILDCARD" and value contains a wildcard', () => { + expect(validateHasWildcardWithWrongOperator({ operator: 'match', value: 'asdf*' })).toEqual( + true + ); + }); + it('should return FALSE when operator is not "WILDCARD" and value does not contain a wildcard', () => { + expect(validateHasWildcardWithWrongOperator({ operator: 'match', value: 'asdf' })).toEqual( + false + ); + }); + it('should return FALSE when operator is "WILDCARD" and value contains a wildcard', () => { + expect(validateHasWildcardWithWrongOperator({ operator: 'wildcard', value: 'asdf*' })).toEqual( + false + ); + }); + it('should return FALSE when operator is "WILDCARD" and value does not contain a wildcard', () => { + expect(validateHasWildcardWithWrongOperator({ operator: 'wildcard', value: 'asdf' })).toEqual( + false + ); + }); +}); + +describe('No Warnings', () => { + it('should not show warnings on non path entries ', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.HASH, + type: 'match', + value: '5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e', + }) + ).toEqual(true); + + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.SIGNER, + type: 'match', + value: '', + }) + ).toEqual(true); + }); +}); + +describe('Unacceptable Windows wildcard paths', () => { + it('should not accept paths that do not have a folder name with a wildcard ', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'c:\\folder', + }) + ).toEqual(false); + }); + + it('should not accept paths that do not have a file name with a wildcard ', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'c:\\path.exe', + }) + ).toEqual(false); + }); + + it('should not accept nested paths that do not have a wildcard', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'c:\\folder\\path.exe', + }) + ).toEqual(false); + }); + + it('should not accept paths with * wildcard and /', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'c:/**/path.exe', + }) + ).toEqual(false); + }); + + it('should not accept paths with ? wildcard and /', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'C:/?indows/pat?', + }) + ).toEqual(false); + }); +}); + +describe('Acceptable Windows wildcard paths', () => { + it('should accept wildcards for folders', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'c:\\**\\path.exe', + }) + ).toEqual(true); + }); + + it('should accept wildcards for folders and files', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'e:\\**\\*.exe', + }) + ).toEqual(true); + }); + + it('should accept paths with single wildcard', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'f:\\*', + }) + ).toEqual(true); + + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'f:\\?', + }) + ).toEqual(true); + }); + + it('should accept paths that have wildcard in filenames', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'a:\\*.*', + }) + ).toEqual(true); + }); + + it('should accept paths with ? as wildcard', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'C:\\?indows\\pat?', + }) + ).toEqual(true); + }); + + it('should accept paths with both ? and * as wildcards', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'C:\\*?', + }) + ).toEqual(true); + }); + + it('should accept paths with multiple wildcards', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'C:\\**', + }) + ).toEqual(true); + + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'C:\\??', + }) + ).toEqual(true); + }); +}); + +describe('Acceptable Windows exact paths', () => { + it('should accept paths when it ends with a folder name', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'match', + value: 'c:\\folder', + }) + ).toEqual(true); + }); + + it('should accept paths when it ends with a file name', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'match', + value: 'c:\\path.exe', + }) + ).toEqual(true); + }); + + it('should accept paths when it ends with a filename in a folder', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'match', + value: 'c:\\folder\\path.exe', + }) + ).toEqual(true); + }); +}); + +describe('Acceptable Windows exact paths with hyphens', () => { + it('should accept paths when paths have folder names with hyphens', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'match', + value: 'c:\\hype-folder-name', + }) + ).toEqual(true); + }); + + it('should accept paths when file names have hyphens', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'match', + value: 'c:\\file-name.exe', + }) + ).toEqual(true); + }); +}); + +describe('Unacceptable Windows exact paths', () => { + it('should not accept paths with /', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'match', + value: 'c:/folder/path.exe', + }) + ).toEqual(false); + }); + + it('should not accept paths not having a <char:> in the suffix', () => { + expect( + isPathValid({ + os: OperatingSystem.WINDOWS, + field: ConditionEntryField.PATH, + type: 'match', + value: '\\folder\\path.exe', + }) + ).toEqual(false); + }); +}); + +/// +describe('Unacceptable Mac/Linux wildcard paths', () => { + it('should not accept paths that do not have a folder name with a wildcard ', () => { + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/folder', + }) + ).toEqual(false); + }); + + it('should not accept paths that do not have a file name with a wildcard ', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/zip.zip', + }) + ).toEqual(false); + }); + + it('should not accept nested paths that do not have a wildcard', () => { + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/opt/pack.tar', + }) + ).toEqual(false); + }); + + it('should not accept paths with * wildcard and \\', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'c:\\**\\path.exe', + }) + ).toEqual(false); + }); + + it('should not accept paths with ? wildcard and \\', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: 'C:\\?indows\\pat?', + }) + ).toEqual(false); + }); +}); + +describe('Acceptable Mac/Linux wildcard paths', () => { + it('should accept wildcards for folders', () => { + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/**/file.', + }) + ).toEqual(true); + }); + + it('should accept wildcards for folders and files', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/usr/bi?/*.js', + }) + ).toEqual(true); + }); + + it('should accept paths with single wildcard', () => { + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/op*', + }) + ).toEqual(true); + + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/op?', + }) + ).toEqual(true); + }); + + it('should accept paths that have wildcard in filenames', () => { + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/*.*', + }) + ).toEqual(true); + }); + + it('should accept paths with ? as wildcard', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/usr/?inux/pat?', + }) + ).toEqual(true); + }); + + it('should accept paths with both ? and * as wildcards', () => { + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/usr/*?', + }) + ).toEqual(true); + }); + + it('should accept paths with multiple wildcards', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/usr/**', + }) + ).toEqual(true); + + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'wildcard', + value: '/opt/??', + }) + ).toEqual(true); + }); +}); + +describe('Acceptable Mac/Linux exact paths', () => { + it('should accept paths when it is the root path', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'match', + value: '/', + }) + ).toEqual(true); + }); + + it('should accept paths when it ends with a file name', () => { + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'match', + value: '/usr/file.ts', + }) + ).toEqual(true); + }); + + it('should accept paths when it ends with a filename in a folder', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'match', + value: '/opt/z.dmg', + }) + ).toEqual(true); + }); +}); + +describe('Acceptable Mac/Linux exact paths with hyphens', () => { + it('should accept paths when paths have folder names with hyphens', () => { + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'match', + value: '/hype-folder-name', + }) + ).toEqual(true); + }); + + it('should accept paths when file names have hyphens', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'match', + value: '/file-name.dmg', + }) + ).toEqual(true); + }); +}); + +describe('Unacceptable Mac/Linux exact paths', () => { + it('should not accept paths with \\', () => { + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'match', + value: 'c:\\folder\\path.exe', + }) + ).toEqual(false); + }); + + it('should not accept paths not starting with /', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'match', + value: 'opt/bin', + }) + ).toEqual(false); + }); + + it('should not accept paths ending with /', () => { + expect( + isPathValid({ + os: OperatingSystem.MAC, + field: ConditionEntryField.PATH, + type: 'match', + value: '/opt/bin/', + }) + ).toEqual(false); + }); + + it('should not accept file extensions with hyphens', () => { + expect( + isPathValid({ + os: OperatingSystem.LINUX, + field: ConditionEntryField.PATH, + type: 'match', + value: '/opt/bin/file.d-mg', + }) + ).toEqual(false); + }); +}); + +describe('hasSimpleExecutableName', () => { + it('should return TRUE when MAC/LINUX wildcard paths have an executable name', () => { + const os = + parseInt((Math.random() * 2).toString(), 10) === 1 + ? OperatingSystem.MAC + : OperatingSystem.LINUX; + + expect( + hasSimpleExecutableName({ + os, + type: 'wildcard', + value: '/opt/*/app', + }) + ).toEqual(true); + expect( + hasSimpleExecutableName({ + os, + type: 'wildcard', + value: '/op*/**/app.dmg', + }) + ).toEqual(true); + expect( + hasSimpleExecutableName({ + os, + type: 'wildcard', + value: "/sy*/test$ as2@13---12!@#A,DS.#$^&$!#~ 'as'd.华语.txt", + }) + ).toEqual(true); + }); + + it('should return FALSE when MAC/LINUX wildcard paths have a wildcard in executable name', () => { + const os = + parseInt((Math.random() * 2).toString(), 10) === 1 + ? OperatingSystem.MAC + : OperatingSystem.LINUX; + + expect( + hasSimpleExecutableName({ + os, + type: 'wildcard', + value: '/op/*/*pp', + }) + ).toEqual(false); + expect( + hasSimpleExecutableName({ + os, + type: 'wildcard', + value: '/op*/b**/ap.m**', + }) + ).toEqual(false); + }); + + it('should return TRUE when WINDOWS wildcards paths have a executable name', () => { + expect( + hasSimpleExecutableName({ + os: OperatingSystem.WINDOWS, + type: 'wildcard', + value: 'c:\\**\\path.exe', + }) + ).toEqual(true); + expect( + hasSimpleExecutableName({ + os: OperatingSystem.WINDOWS, + type: 'wildcard', + value: 'C:\\*\\file-name.path华语 1234.txt', + }) + ).toEqual(true); + expect( + hasSimpleExecutableName({ + os: OperatingSystem.WINDOWS, + type: 'wildcard', + value: "C:\\*\\test$ as2@13---12!@#A,DS.#$^&$!#~ 'as'd.华语.txt", + }) + ).toEqual(true); + }); + + it('should return FALSE when WINDOWS wildcards paths have a wildcard in executable name', () => { + expect( + hasSimpleExecutableName({ + os: OperatingSystem.WINDOWS, + type: 'wildcard', + value: 'c:\\**\\pa*h.exe', + }) + ).toEqual(false); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/path_validations/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/path_validations/index.ts new file mode 100644 index 000000000000..965a5b9fbe0f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/path_validations/index.ts @@ -0,0 +1,243 @@ +/* + * 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'; + +export const WILDCARD_WARNING = i18n.translate('utils.wildcardWarning', { + defaultMessage: `Using wildcards can impact Endpoint performance`, +}); + +export const FILEPATH_WARNING = i18n.translate('utils.filename.pathWarning', { + defaultMessage: `Path may be formed incorrectly; verify value`, +}); + +export enum ConditionEntryField { + HASH = 'process.hash.*', + PATH = 'process.executable.caseless', + SIGNER = 'process.Ext.code_signature', + SIGNER_MAC = 'process.code_signature', +} + +export enum EntryFieldType { + HASH = '.hash.', + EXECUTABLE = '.executable.caseless', + PATH = '.path', + SIGNER = '.code_signature', +} + +export type TrustedAppConditionEntryField = + | 'process.hash.*' + | 'process.executable.caseless' + | 'process.Ext.code_signature' + | 'process.code_signature'; + +export type BlocklistConditionEntryField = + | 'file.hash.*' + | 'file.path' + | 'file.Ext.code_signature' + | 'file.path.caseless'; +export type AllConditionEntryFields = + | TrustedAppConditionEntryField + | BlocklistConditionEntryField + | 'file.path.text'; + +export enum OperatingSystem { + LINUX = 'linux', + MAC = 'macos', + WINDOWS = 'windows', +} + +export type EntryTypes = 'match' | 'wildcard' | 'match_any'; +export type TrustedAppEntryTypes = Extract<EntryTypes, 'match' | 'wildcard'>; +export type EventFiltersTypes = EntryTypes | 'exists' | 'nested'; + +export const validatePotentialWildcardInput = ({ + field = '', + os, + value = '', +}: { + field?: string; + os: OperatingSystem; + value?: string; +}): string | undefined => { + const textInput = value.trim(); + if (field === 'file.path.text') { + return validateFilePathInput({ os, value: textInput }); + } + return validateWildcardInput(textInput); +}; + +export const validateFilePathInput = ({ + os, + value, +}: { + os: OperatingSystem; + value: string; +}): string | undefined => { + const isValidFilePath = isPathValid({ + os, + field: 'file.path.text', + type: 'wildcard', + value, + }); + const hasSimpleFileName = hasSimpleExecutableName({ + os, + type: 'wildcard', + value, + }); + + if (!value.length) { + return FILEPATH_WARNING; + } + + if (isValidFilePath) { + if (hasSimpleFileName !== undefined && !hasSimpleFileName) { + return WILDCARD_WARNING; + } + } else { + return FILEPATH_WARNING; + } +}; + +export const validateWildcardInput = (value: string | string[]): string | undefined => { + const wildcardRegex = /[*?]/; + if (Array.isArray(value)) { + const doesAnyValueContainWildcardInput = value.some((v) => wildcardRegex.test(v)); + if (doesAnyValueContainWildcardInput) { + return WILDCARD_WARNING; + } + } else { + if (wildcardRegex.test(value)) { + return WILDCARD_WARNING; + } + } +}; + +export const validateHasWildcardWithWrongOperator = ({ + operator, + value, +}: { + operator: TrustedAppEntryTypes | EventFiltersTypes; + value: string | string[]; +}): boolean => { + if (operator !== 'wildcard' && validateWildcardInput(value)) { + return true; + } else { + return false; + } +}; + +export const hasSimpleExecutableName = ({ + os, + type, + value, +}: { + os: OperatingSystem; + type: EntryTypes; + value: string; +}): boolean | undefined => { + const separator = os === OperatingSystem.WINDOWS ? '\\' : '/'; + const lastString = value.split(separator).pop(); + if (!lastString) { + return; + } + if (type === 'wildcard') { + return (lastString.split('*').length || lastString.split('?').length) === 1; + } + return true; +}; + +export const isPathValid = ({ + os, + field, + type, + value, +}: { + os: OperatingSystem; + field: AllConditionEntryFields; + type: EntryTypes; + value: string; +}): boolean => { + const pathFields: AllConditionEntryFields[] = [ + 'process.executable.caseless', + 'file.path', + 'file.path.text', + ]; + if (pathFields.includes(field)) { + if (type === 'wildcard') { + return os === OperatingSystem.WINDOWS + ? isWindowsWildcardPathValid(value) + : isLinuxMacWildcardPathValid(value); + } + return doesPathMatchRegex({ value, os }); + } + return true; +}; + +const doesPathMatchRegex = ({ os, value }: { os: OperatingSystem; value: string }): boolean => { + if (os === OperatingSystem.WINDOWS) { + const filePathRegex = + /^[a-z]:(?:|\\\\[^<>:"'/\\|?*]+\\[^<>:"'/\\|?*]+|%\w+%|)[\\](?:[^<>:"'/\\|?*]+[\\/])*([^<>:"'/\\|?*])+$/i; + return filePathRegex.test(value); + } + return /^(\/|(\/[\w\-]+)+|\/[\w\-]+\.[\w]+|(\/[\w-]+)+\/[\w\-]+\.[\w]+)$/i.test(value); +}; + +const isWindowsWildcardPathValid = (path: string): boolean => { + const firstCharacter = path[0]; + const lastCharacter = path.slice(-1); + const trimmedValue = path.trim(); + const hasSlash = /\//.test(trimmedValue); + if (path.length === 0) { + return false; + } else if ( + hasSlash || + trimmedValue.length !== path.length || + firstCharacter === '^' || + lastCharacter === '\\' || + !hasWildcard({ path, isWindowsPath: true }) + ) { + return false; + } else { + return true; + } +}; + +const isLinuxMacWildcardPathValid = (path: string): boolean => { + const firstCharacter = path[0]; + const lastCharacter = path.slice(-1); + const trimmedValue = path.trim(); + if (path.length === 0) { + return false; + } else if ( + trimmedValue.length !== path.length || + firstCharacter !== '/' || + lastCharacter === '/' || + path.length > 1024 === true || + path.includes('//') === true || + !hasWildcard({ path, isWindowsPath: false }) + ) { + return false; + } else { + return true; + } +}; + +const hasWildcard = ({ + path, + isWindowsPath, +}: { + path: string; + isWindowsPath: boolean; +}): boolean => { + for (const pathComponent of path.split(isWindowsPath ? '\\' : '/')) { + if (/[\*|\?]+/.test(pathComponent) === true) { + return true; + } + } + return false; +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts new file mode 100644 index 000000000000..d91eb687d86d --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts @@ -0,0 +1,83 @@ +/* + * 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 { transformDataToNdjson } from '.'; + +export const ANCHOR_DATE = '2020-02-20T03:57:54.037Z'; + +const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE) => ({ + author: [], + id: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9', + created_at: new Date(anchorDate).toISOString(), + updated_at: new Date(anchorDate).toISOString(), + created_by: 'elastic', + description: 'some description', + enabled: true, + false_positives: ['false positive 1', 'false positive 2'], + from: 'now-6m', + immutable: false, + name: 'Query with a rule id', + query: 'user.name: root or user.name: admin', + references: ['test 1', 'test 2'], + severity: 'high', + severity_mapping: [], + updated_by: 'elastic_kibana', + tags: ['some fake tag 1', 'some fake tag 2'], + to: 'now', + type: 'query', + threat: [], + version: 1, + output_index: '.siem-signals-default', + max_signals: 100, + risk_score: 55, + risk_score_mapping: [], + language: 'kuery', + rule_id: 'query-rule-id', + interval: '5m', + exceptions_list: [], +}); + +describe('transformDataToNdjson', () => { + test('if rules are empty it returns an empty string', () => { + const ruleNdjson = transformDataToNdjson([]); + expect(ruleNdjson).toEqual(''); + }); + + test('single rule will transform with new line ending character for ndjson', () => { + const rule = getRulesSchemaMock(); + const ruleNdjson = transformDataToNdjson([rule]); + expect(ruleNdjson.endsWith('\n')).toBe(true); + }); + + test('multiple rules will transform with two new line ending characters for ndjson', () => { + const result1 = getRulesSchemaMock(); + const result2 = getRulesSchemaMock(); + result2.id = 'some other id'; + result2.rule_id = 'some other id'; + result2.name = 'Some other rule'; + + const ruleNdjson = transformDataToNdjson([result1, result2]); + // this is how we count characters in JavaScript :-) + const count = ruleNdjson.split('\n').length - 1; + expect(count).toBe(2); + }); + + test('you can parse two rules back out without errors', () => { + const result1 = getRulesSchemaMock(); + const result2 = getRulesSchemaMock(); + result2.id = 'some other id'; + result2.rule_id = 'some other id'; + result2.name = 'Some other rule'; + + const ruleNdjson = transformDataToNdjson([result1, result2]); + const ruleStrings = ruleNdjson.split('\n'); + const reParsed1 = JSON.parse(ruleStrings[0]); + const reParsed2 = JSON.parse(ruleStrings[1]); + expect(reParsed1).toEqual(result1); + expect(reParsed2).toEqual(result2); + }); +}); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.ts new file mode 100644 index 000000000000..572b777731c4 --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const transformDataToNdjson = (data: unknown[]): string => { + if (data.length !== 0) { + const dataString = data.map((item) => JSON.stringify(item)).join('\n'); + return `${dataString}\n`; + } else { + return ''; + } +}; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-utils/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-utils/tsconfig.json new file mode 100644 index 000000000000..063735a114da --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-securitysolution-utils/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "kbn_references": [ + "@kbn/i18n", + "@kbn/esql-utils", + "@kbn/esql-ast", + "@kbn/esql-validation-autocomplete" + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/x-pack/solutions/security/plugins/asset_inventory/README.md b/x-pack/solutions/security/plugins/asset_inventory/README.md index e1dd9d4ac890..313a45cec6f7 100755 --- a/x-pack/solutions/security/plugins/asset_inventory/README.md +++ b/x-pack/solutions/security/plugins/asset_inventory/README.md @@ -8,6 +8,14 @@ Centralized asset inventory experience within the Elastic Security solution. A c See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment. +### Feature flag + +First, enable the `assetInventoryStoreEnabled` experimental feature flag by adding the following to your `kibana.dev.yml`: + +```yml +xpack.securitySolution.enableExperimental: ['assetInventoryStoreEnabled'] +``` + ## Testing For general guidelines, read [Kibana Testing Guide](https://www.elastic.co/guide/en/kibana/current/development-tests.html) for more details diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/server/routes/graph/route.ts b/x-pack/solutions/security/plugins/cloud_security_posture/server/routes/graph/route.ts index f9544b656f92..f65574781585 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/server/routes/graph/route.ts +++ b/x-pack/solutions/security/plugins/cloud_security_posture/server/routes/graph/route.ts @@ -43,7 +43,7 @@ export const defineGraphRoute = (router: CspRouter) => const cspContext = await context.csp; const { nodesLimit, showUnknownTarget = false } = request.body; - const { eventIds, start, end, esQuery } = request.body.query as GraphRequest['query']; + const { originEventIds, start, end, esQuery } = request.body.query as GraphRequest['query']; const spaceId = (await cspContext.spaces?.spacesService?.getActiveSpace(request))?.id; try { @@ -53,7 +53,7 @@ export const defineGraphRoute = (router: CspRouter) => esClient: cspContext.esClient, }, query: { - eventIds, + originEventIds, spaceId, start, end, diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/server/routes/graph/v1.ts b/x-pack/solutions/security/plugins/cloud_security_posture/server/routes/graph/v1.ts index b14a2ba3e06a..d506bb856e76 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/server/routes/graph/v1.ts +++ b/x-pack/solutions/security/plugins/cloud_security_posture/server/routes/graph/v1.ts @@ -33,7 +33,8 @@ interface GraphEdge { action: string; targetIds: string[] | string; eventOutcome: string; - isAlert: boolean; + isOrigin: boolean; + isOriginAlert: boolean; } interface LabelEdges { @@ -46,10 +47,15 @@ interface GraphContextServices { esClient: IScopedClusterClient; } +interface OriginEventId { + id: string; + isAlert: boolean; +} + interface GetGraphParams { services: GraphContextServices; query: { - eventIds: string[]; + originEventIds: OriginEventId[]; spaceId?: string; start: string | number; end: string | number; @@ -61,11 +67,13 @@ interface GetGraphParams { export const getGraph = async ({ services: { esClient, logger }, - query: { eventIds, spaceId = 'default', start, end, esQuery }, + query: { originEventIds, spaceId = 'default', start, end, esQuery }, showUnknownTarget, nodesLimit, }: GetGraphParams): Promise<Pick<GraphResponse, 'nodes' | 'edges' | 'messages'>> => { - logger.trace(`Fetching graph for [eventIds: ${eventIds.join(', ')}] in [spaceId: ${spaceId}]`); + logger.trace( + `Fetching graph for [originEventIds: ${originEventIds.join(', ')}] in [spaceId: ${spaceId}]` + ); const results = await fetchGraph({ esClient, @@ -73,7 +81,7 @@ export const getGraph = async ({ logger, start, end, - eventIds, + originEventIds, esQuery, }); @@ -132,7 +140,7 @@ const fetchGraph = async ({ logger, start, end, - eventIds, + originEventIds, showUnknownTarget, esQuery, }: { @@ -140,15 +148,21 @@ const fetchGraph = async ({ logger: Logger; start: string | number; end: string | number; - eventIds: string[]; + originEventIds: OriginEventId[]; showUnknownTarget: boolean; esQuery?: EsQuery; }): Promise<EsqlToRecords<GraphEdge>> => { + const originAlertIds = originEventIds.filter((originEventId) => originEventId.isAlert); const query = `from logs-* | WHERE event.action IS NOT NULL AND actor.entity.id IS NOT NULL -| EVAL isAlert = ${ - eventIds.length > 0 - ? `event.id in (${eventIds.map((_id, idx) => `?al_id${idx}`).join(', ')})` +| EVAL isOrigin = ${ + originEventIds.length > 0 + ? `event.id in (${originEventIds.map((_id, idx) => `?og_id${idx}`).join(', ')})` + : 'false' + } +| EVAL isOriginAlert = isOrigin AND ${ + originAlertIds.length > 0 + ? `event.id in (${originAlertIds.map((_id, idx) => `?og_alrt_id${idx}`).join(', ')})` : 'false' } | STATS badge = COUNT(*), @@ -159,19 +173,26 @@ const fetchGraph = async ({ action = event.action, targetIds = target.entity.id, eventOutcome = event.outcome, - isAlert + isOrigin, + isOriginAlert | LIMIT 1000 -| SORT isAlert DESC`; +| SORT isOrigin DESC`; logger.trace(`Executing query [${query}]`); + const eventIds = originEventIds.map((originEventId) => originEventId.id); return await esClient.asCurrentUser.helpers .esql({ columnar: false, filter: buildDslFilter(eventIds, showUnknownTarget, start, end, esQuery), query, // @ts-ignore - types are not up to date - params: [...eventIds.map((id, idx) => ({ [`al_id${idx}`]: id }))], + params: [ + ...originEventIds.map((originEventId, idx) => ({ [`og_id${idx}`]: originEventId.id })), + ...originEventIds + .filter((originEventId) => originEventId.isAlert) + .map((originEventId, idx) => ({ [`og_alrt_id${idx}`]: originEventId.id })), + ], }) .toRecords<GraphEdge>(); }; @@ -238,7 +259,7 @@ const createNodes = (records: GraphEdge[], context: Omit<ParseContext, 'edgesMap break; } - const { ips, hosts, users, actorIds, action, targetIds, isAlert, eventOutcome } = record; + const { ips, hosts, users, actorIds, action, targetIds, isOriginAlert, eventOutcome } = record; const actorIdsArray = castArray(actorIds); const targetIdsArray = castArray(targetIds); const unknownTargets: string[] = []; @@ -257,7 +278,7 @@ const createNodes = (records: GraphEdge[], context: Omit<ParseContext, 'edgesMap nodesMap[id] = { id, label: unknownTargets.includes(id) ? 'Unknown' : undefined, - color: isAlert ? 'danger' : 'primary', + color: isOriginAlert ? 'danger' : 'primary', ...determineEntityNodeShape( id, castArray(ips ?? []), @@ -280,7 +301,7 @@ const createNodes = (records: GraphEdge[], context: Omit<ParseContext, 'edgesMap const labelNode: LabelNodeDataModel = { id: edgeId + `label(${action})outcome(${eventOutcome})`, label: action, - color: isAlert ? 'danger' : eventOutcome === 'failed' ? 'warning' : 'primary', + color: isOriginAlert ? 'danger' : eventOutcome === 'failed' ? 'warning' : 'primary', shape: 'label', }; diff --git a/x-pack/plugins/lists/.storybook/main.js b/x-pack/solutions/security/plugins/lists/.storybook/main.js similarity index 100% rename from x-pack/plugins/lists/.storybook/main.js rename to x-pack/solutions/security/plugins/lists/.storybook/main.js diff --git a/x-pack/solutions/security/plugins/lists/README.md b/x-pack/solutions/security/plugins/lists/README.md new file mode 100644 index 000000000000..b04d7097ddc6 --- /dev/null +++ b/x-pack/solutions/security/plugins/lists/README.md @@ -0,0 +1,238 @@ +README.md for developers working on the backend lists on how to get started +using the CURL scripts in the scripts folder. + +The scripts rely on CURL and jq: + +- [CURL](https://curl.haxx.se) +- [jq](https://stedolan.github.io/jq/) + +Install curl and jq (mac instructions) + +```sh +brew update +brew install curl +brew install jq +``` + +Open `$HOME/.zshrc` or `${HOME}.bashrc` depending on your SHELL output from `echo $SHELL` +and add these environment variables: + +```sh +export ELASTICSEARCH_USERNAME=${user} +export ELASTICSEARCH_PASSWORD=${password} +export ELASTICSEARCH_URL=https://${ip}:9200 +export KIBANA_URL=http://localhost:5601 +export TASK_MANAGER_INDEX=.kibana-task-manager-${your user id} +export KIBANA_INDEX=.kibana-${your user id} +``` + +source `$HOME/.zshrc` or `${HOME}.bashrc` to ensure variables are set: + +```sh +source ~/.zshrc +``` + +Open your `kibana.dev.yml` file and add these lines with your name: + +```sh +xpack.lists.listIndex: '.lists-your-name' +xpack.lists.listItemIndex: '.items-your-name' +``` + +Restart Kibana and ensure that you are using `--no-base-path` as changing the base path is a feature but will +get in the way of the CURL scripts written as is. + +Go to the scripts folder `cd kibana/x-pack/solutions/security/plugins/lists/server/scripts` and run: + +```sh +./hard_reset.sh +./post_list.sh +``` + +which will: + +- Delete any existing lists you have +- Delete any existing list items you have +- Delete any existing exception lists you have +- Delete any existing exception list items you have +- Delete any existing mapping, policies, and templates, you might have previously had. +- Add the latest list and list item index and its mappings using your settings from `kibana.dev.yml` environment variable of `xpack.lists.listIndex` and `xpack.lists.listItemIndex`. +- Posts the sample list from `./lists/new/ip_list.json` + +Now you can run + +```sh +./post_list.sh +``` + +You should see the new list created like so: + +```sh +{ + "id": "ip_list", + "created_at": "2020-05-28T19:15:22.344Z", + "created_by": "yo", + "description": "This list describes bad internet ip", + "name": "Simple list with an ip", + "tie_breaker_id": "c57efbc4-4977-4a32-995f-cfd296bed521", + "type": "ip", + "updated_at": "2020-05-28T19:15:22.344Z", + "updated_by": "yo" +} +``` + +You can add a list item like so: + +```sh + ./post_list_item.sh +``` + +You should see the new list item created and attached to the above list like so: + +```sh +{ + "id": "hand_inserted_item_id", + "type": "ip", + "value": "127.0.0.1", + "created_at": "2020-05-28T19:15:49.790Z", + "created_by": "yo", + "list_id": "ip_list", + "tie_breaker_id": "a881bf2e-1e17-4592-bba8-d567cb07d234", + "updated_at": "2020-05-28T19:15:49.790Z", + "updated_by": "yo" +} +``` + +If you want to post an exception list it would be like so: + +```sh +./post_exception_list.sh +``` + +You should see the new exception list created like so: + +```sh +{ + "created_at": "2020-05-28T19:16:31.052Z", + "created_by": "yo", + "description": "This is a sample endpoint type exception", + "id": "bcb94680-a117-11ea-ad9d-c71f4820e65b", + "list_id": "endpoint_list", + "name": "Sample Endpoint Exception List", + "namespace_type": "single", + "tags": [ + "user added string for a tag", + "malware" + ], + "tie_breaker_id": "86e08c8c-c970-4b08-a6e2-cdba7bb4e023", + "type": "endpoint", + "updated_at": "2020-05-28T19:16:31.080Z", + "updated_by": "yo" +} +``` + +And you can attach exception list items like so: + +```ts +{ + "comments": [], + "created_at": "2020-05-28T19:17:21.099Z", + "created_by": "yo", + "description": "This is a sample endpoint type exception", + "entries": [ + { + "field": "actingProcess.file.signer", + "operator": "included", + "type": "match", + "value": "Elastic, N.V." + }, + { + "field": "event.category", + "operator": "included", + "type": "match_any", + "value": [ + "process", + "malware" + ] + } + ], + "id": "da8d3b30-a117-11ea-ad9d-c71f4820e65b", + "item_id": "endpoint_list_item", + "list_id": "endpoint_list", + "name": "Sample Endpoint Exception List", + "namespace_type": "single", + "os_types": ["linux"], + "tags": [ + "user added string for a tag", + "malware" + ], + "tie_breaker_id": "21f84703-9476-4af8-a212-aad31e18dcb9", + "type": "simple", + "updated_at": "2020-05-28T19:17:21.123Z", + "updated_by": "yo" +} +``` + +You can then do find for each one like so: + +```sh +./find_lists.sh +``` + +```sh +{ + "cursor": "WzIwLFsiYzU3ZWZiYzQtNDk3Ny00YTMyLTk5NWYtY2ZkMjk2YmVkNTIxIl1d", + "data": [ + { + "id": "ip_list", + "created_at": "2020-05-28T19:15:22.344Z", + "created_by": "yo", + "description": "This list describes bad internet ip", + "name": "Simple list with an ip", + "tie_breaker_id": "c57efbc4-4977-4a32-995f-cfd296bed521", + "type": "ip", + "updated_at": "2020-05-28T19:15:22.344Z", + "updated_by": "yo" + } + ], + "page": 1, + "per_page": 20, + "total": 1 +} +``` + +or for finding exception lists: + +```sh +./find_exception_lists.sh +``` + +```sh +{ + "data": [ + { + "created_at": "2020-05-28T19:16:31.052Z", + "created_by": "yo", + "description": "This is a sample endpoint type exception", + "id": "bcb94680-a117-11ea-ad9d-c71f4820e65b", + "list_id": "endpoint_list", + "name": "Sample Endpoint Exception List", + "namespace_type": "single", + "os_types": ["linux"], + "tags": [ + "user added string for a tag", + "malware" + ], + "tie_breaker_id": "86e08c8c-c970-4b08-a6e2-cdba7bb4e023", + "type": "endpoint", + "updated_at": "2020-05-28T19:16:31.080Z", + "updated_by": "yo" + } + ], + "page": 1, + "per_page": 20, + "total": 1 +} +``` + +See the full scripts folder for all the capabilities. diff --git a/x-pack/plugins/lists/common/api/exceptions/create_exception_list/create_exception_list_route.ts b/x-pack/solutions/security/plugins/lists/common/api/exceptions/create_exception_list/create_exception_list_route.ts similarity index 100% rename from x-pack/plugins/lists/common/api/exceptions/create_exception_list/create_exception_list_route.ts rename to x-pack/solutions/security/plugins/lists/common/api/exceptions/create_exception_list/create_exception_list_route.ts diff --git a/x-pack/plugins/lists/common/api/exceptions/get_exception_filter/get_exception_filter_route.ts b/x-pack/solutions/security/plugins/lists/common/api/exceptions/get_exception_filter/get_exception_filter_route.ts similarity index 100% rename from x-pack/plugins/lists/common/api/exceptions/get_exception_filter/get_exception_filter_route.ts rename to x-pack/solutions/security/plugins/lists/common/api/exceptions/get_exception_filter/get_exception_filter_route.ts diff --git a/x-pack/plugins/lists/common/api/index.ts b/x-pack/solutions/security/plugins/lists/common/api/index.ts similarity index 100% rename from x-pack/plugins/lists/common/api/index.ts rename to x-pack/solutions/security/plugins/lists/common/api/index.ts diff --git a/x-pack/plugins/lists/common/api/values/find_lists_by_size/find_lists_by_size_route.ts b/x-pack/solutions/security/plugins/lists/common/api/values/find_lists_by_size/find_lists_by_size_route.ts similarity index 100% rename from x-pack/plugins/lists/common/api/values/find_lists_by_size/find_lists_by_size_route.ts rename to x-pack/solutions/security/plugins/lists/common/api/values/find_lists_by_size/find_lists_by_size_route.ts diff --git a/x-pack/plugins/lists/common/constants.mock.ts b/x-pack/solutions/security/plugins/lists/common/constants.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/constants.mock.ts rename to x-pack/solutions/security/plugins/lists/common/constants.mock.ts diff --git a/x-pack/plugins/lists/common/index.ts b/x-pack/solutions/security/plugins/lists/common/index.ts similarity index 100% rename from x-pack/plugins/lists/common/index.ts rename to x-pack/solutions/security/plugins/lists/common/index.ts diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/create_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/create_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/create_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/create_list_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/create_list_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/create_list_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/request/get_exception_filter_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/get_exception_filter_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/get_exception_filter_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/get_exception_filter_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/request/import_exceptions_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/import_exceptions_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/import_exceptions_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/import_exceptions_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/request/import_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/import_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/import_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/import_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/update_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/update_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/update_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/request/update_list_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/request/update_list_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/request/update_list_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/request/update_list_schema.mock.ts diff --git a/x-pack/solutions/security/plugins/lists/common/schemas/response/acknowledge_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/acknowledge_schema.mock.ts new file mode 100644 index 000000000000..0aa6990d44b3 --- /dev/null +++ b/x-pack/solutions/security/plugins/lists/common/schemas/response/acknowledge_schema.mock.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. + */ + +import type { AcknowledgeSchema } from '@kbn/securitysolution-io-ts-list-types'; + +export const getAcknowledgeSchemaResponseMock = (): AcknowledgeSchema => ({ + acknowledged: true, +}); diff --git a/x-pack/plugins/lists/common/schemas/response/exception_export_details_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/exception_export_details_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/exception_export_details_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/exception_export_details_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/exception_list_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/exception_list_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_summary_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/exception_list_summary_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/exception_list_summary_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/exception_list_summary_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/found_all_list_items_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/found_all_list_items_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/found_all_list_items_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/found_all_list_items_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/found_exception_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/found_exception_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/found_exception_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/found_exception_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/found_exception_list_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/found_exception_list_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/found_exception_list_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/found_exception_list_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/found_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/found_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/found_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/found_list_item_schema.mock.ts diff --git a/x-pack/solutions/security/plugins/lists/common/schemas/response/found_list_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/found_list_schema.mock.ts new file mode 100644 index 000000000000..1691221842d3 --- /dev/null +++ b/x-pack/solutions/security/plugins/lists/common/schemas/response/found_list_schema.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FoundListSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { getListResponseMock } from './list_schema.mock'; + +export const getFoundListSchemaMock = (): FoundListSchema => ({ + cursor: '123', + data: [getListResponseMock()], + page: 1, + per_page: 1, + total: 1, +}); diff --git a/x-pack/plugins/lists/common/schemas/response/found_lists_by_size_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/found_lists_by_size_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/found_lists_by_size_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/found_lists_by_size_schema.mock.ts diff --git a/x-pack/solutions/security/plugins/lists/common/schemas/response/list_item_index_exist_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/list_item_index_exist_schema.mock.ts new file mode 100644 index 000000000000..afbb03cab870 --- /dev/null +++ b/x-pack/solutions/security/plugins/lists/common/schemas/response/list_item_index_exist_schema.mock.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. + */ + +import type { ListItemIndexExistSchema } from '@kbn/securitysolution-io-ts-list-types'; + +export const getListItemIndexExistSchemaResponseMock = (): ListItemIndexExistSchema => ({ + list_index: true, + list_item_index: true, +}); diff --git a/x-pack/plugins/lists/common/schemas/response/list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/list_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/list_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/list_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/list_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/response/search_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/response/search_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/response/search_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/response/search_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/types/comment.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/types/comment.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/types/comment.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/types/comment.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/types/create_comment.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/types/create_comment.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/types/create_comment.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/types/create_comment.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/types/entries.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/types/entries.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/types/entries.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/types/entries.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/types/entry_exists.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/types/entry_exists.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/types/entry_exists.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/types/entry_exists.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/types/entry_list.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/types/entry_list.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/types/entry_list.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/types/entry_list.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/types/entry_match.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/types/entry_match.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/types/entry_match.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/types/entry_match.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/types/entry_match_any.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/types/entry_match_any.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/types/entry_match_any.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/types/entry_match_any.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/types/entry_match_wildcard.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/types/entry_match_wildcard.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/types/entry_match_wildcard.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/types/entry_match_wildcard.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/types/entry_nested.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/types/entry_nested.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/types/entry_nested.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/types/entry_nested.mock.ts diff --git a/x-pack/plugins/lists/common/schemas/types/update_comment.mock.ts b/x-pack/solutions/security/plugins/lists/common/schemas/types/update_comment.mock.ts similarity index 100% rename from x-pack/plugins/lists/common/schemas/types/update_comment.mock.ts rename to x-pack/solutions/security/plugins/lists/common/schemas/types/update_comment.mock.ts diff --git a/x-pack/solutions/security/plugins/lists/jest.config.js b/x-pack/solutions/security/plugins/lists/jest.config.js new file mode 100644 index 000000000000..3bbf109f51ab --- /dev/null +++ b/x-pack/solutions/security/plugins/lists/jest.config.js @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + collectCoverageFrom: [ + '<rootDir>/x-pack/solutions/security/plugins/lists/{common,public,server}/**/*.{ts,tsx}', + ], + coverageDirectory: + '<rootDir>/target/kibana-coverage/jest/x-pack/solutions/security/plugins/lists', + coverageReporters: ['text', 'html'], + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['<rootDir>/x-pack/solutions/security/plugins/lists'], +}; diff --git a/x-pack/plugins/lists/kibana.jsonc b/x-pack/solutions/security/plugins/lists/kibana.jsonc similarity index 100% rename from x-pack/plugins/lists/kibana.jsonc rename to x-pack/solutions/security/plugins/lists/kibana.jsonc diff --git a/x-pack/plugins/lists/public/common/empty_value.ts b/x-pack/solutions/security/plugins/lists/public/common/empty_value.ts similarity index 100% rename from x-pack/plugins/lists/public/common/empty_value.ts rename to x-pack/solutions/security/plugins/lists/public/common/empty_value.ts diff --git a/x-pack/plugins/lists/public/exceptions/api.test.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/api.test.ts similarity index 100% rename from x-pack/plugins/lists/public/exceptions/api.test.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/api.test.ts diff --git a/x-pack/plugins/lists/public/exceptions/components/__mock__/show_value_list_modal.mock.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/__mock__/show_value_list_modal.mock.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/__mock__/show_value_list_modal.mock.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/__mock__/show_value_list_modal.mock.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/and_or_badge/index.stories.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/index.stories.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/and_or_badge/index.stories.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/index.stories.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/and_or_badge/index.test.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/index.test.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/and_or_badge/index.test.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/index.test.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/and_or_badge/index.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/index.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/and_or_badge/index.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/index.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge.test.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge.test.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge.test.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge.test.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge_antenna.test.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge_antenna.test.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge_antenna.test.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge_antenna.test.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge_antenna.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge_antenna.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge_antenna.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/rounded_badge_antenna.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/and_or_badge/translations.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/translations.ts similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/and_or_badge/translations.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/and_or_badge/translations.ts diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/and_badge.test.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/and_badge.test.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/and_badge.test.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/and_badge.test.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/and_badge.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/and_badge.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/and_badge.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/and_badge.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/builder.stories.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/builder.stories.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/builder.stories.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/builder.stories.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_delete_button.test.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/entry_delete_button.test.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/entry_delete_button.test.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/entry_delete_button.test.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_delete_button.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/entry_delete_button.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/entry_delete_button.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/entry_delete_button.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.stories.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/entry_renderer.stories.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.stories.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/entry_renderer.stories.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.test.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/exception_item_renderer.test.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.test.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/exception_item_renderer.test.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/exception_item_renderer.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/exception_item_renderer.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/helpers.test.ts similarity index 99% rename from x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/helpers.test.ts index 2c0f8b198b38..c5afe22bb73c 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts +++ b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/helpers.test.ts @@ -1669,7 +1669,7 @@ describe('Exception builder helpers', () => { }); describe('#filterExceptionItems', () => { - // Please see `x-pack/plugins/lists/public/exceptions/transforms.ts` doc notes + // Please see `x-pack/solutions/security/plugins/lists/public/exceptions/transforms.ts` doc notes // for context around the temporary `id` test('it correctly validates entries that include a temporary `id`', () => { const output: ExceptionsBuilderReturnExceptionItem[] = filterExceptionItems([ diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/index.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/index.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/index.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/index.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/logic_buttons.test.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/logic_buttons.test.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/logic_buttons.test.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/logic_buttons.test.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/logic_buttons.tsx b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/logic_buttons.tsx similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/logic_buttons.tsx rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/logic_buttons.tsx diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/reducer.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/reducer.ts similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/reducer.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/reducer.ts diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/selectors.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/selectors.ts similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/selectors.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/selectors.ts diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/translations.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/translations.ts similarity index 100% rename from x-pack/plugins/lists/public/exceptions/components/builder/translations.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/components/builder/translations.ts diff --git a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.test.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/hooks/persist_exception_item.test.ts similarity index 96% rename from x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.test.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/hooks/persist_exception_item.test.ts index 95ca6285f0d7..029488f850d4 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.test.ts +++ b/x-pack/solutions/security/plugins/lists/public/exceptions/hooks/persist_exception_item.test.ts @@ -17,7 +17,7 @@ import { getExceptionListItemSchemaMock } from '../../../common/schemas/response const mockKibanaHttpService = coreMock.createStart().http; -// TODO: Port this test over to packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.test.ts once the other mocks are added to the kbn package system +// TODO: Port this test over to x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.test.ts once the other mocks are added to the kbn package system describe('usePersistExceptionItem', () => { let addExceptionListItemSpy: jest.SpyInstance<ReturnType<typeof api.addEndpointExceptionList>>; diff --git a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.test.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/hooks/persist_exception_list.test.ts similarity index 95% rename from x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.test.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/hooks/persist_exception_list.test.ts index c27888b6d8cc..159426d71160 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.test.ts +++ b/x-pack/solutions/security/plugins/lists/public/exceptions/hooks/persist_exception_list.test.ts @@ -18,7 +18,7 @@ const mockKibanaHttpService = coreMock.createStart().http; jest.mock('@kbn/securitysolution-list-api'); -// TODO: Port this to the kbn package of: packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.test.ts once the kibana mocks are ported +// TODO: Port this to the kbn package of: x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.test.ts once the kibana mocks are ported describe('usePersistExceptionList', () => { const onError = jest.fn(); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/hooks/use_api.test.ts similarity index 99% rename from x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/hooks/use_api.test.ts index a980261ba5f8..2ceaab63cc04 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts +++ b/x-pack/solutions/security/plugins/lists/public/exceptions/hooks/use_api.test.ts @@ -32,7 +32,7 @@ jest.mock('uuid', () => ({ const mockKibanaHttpService = coreMock.createStart().http; -// TODO: Once the mocks are figured out and the types are moved into kbn core this test should be moved next to the file: packages/kbn-securitysolution-list-hooks/src/use_api/index.test.ts +// TODO: Once the mocks are figured out and the types are moved into kbn core this test should be moved next to the file: x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_api/index.test.ts describe('useApi', () => { const onErrorMock = jest.fn(); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_lists.test.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/hooks/use_exception_lists.test.ts similarity index 97% rename from x-pack/plugins/lists/public/exceptions/hooks/use_exception_lists.test.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/hooks/use_exception_lists.test.ts index 6c684d199e53..db01cc54e855 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_lists.test.ts +++ b/x-pack/solutions/security/plugins/lists/public/exceptions/hooks/use_exception_lists.test.ts @@ -20,7 +20,7 @@ const mockKibanaHttpService = coreMock.createStart().http; const mockKibanaNotificationsService = coreMock.createStart().notifications; jest.mock('@kbn/securitysolution-list-api'); -// TODO: Move this test to the kbn package: packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.test.ts once mocks are ported over +// TODO: Move this test to the kbn package: x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.test.ts once mocks are ported over describe('useExceptionLists', () => { beforeEach(() => { diff --git a/x-pack/plugins/lists/public/exceptions/transforms.test.ts b/x-pack/solutions/security/plugins/lists/public/exceptions/transforms.test.ts similarity index 100% rename from x-pack/plugins/lists/public/exceptions/transforms.test.ts rename to x-pack/solutions/security/plugins/lists/public/exceptions/transforms.test.ts diff --git a/x-pack/plugins/lists/public/index.ts b/x-pack/solutions/security/plugins/lists/public/index.ts similarity index 100% rename from x-pack/plugins/lists/public/index.ts rename to x-pack/solutions/security/plugins/lists/public/index.ts diff --git a/x-pack/plugins/lists/public/lists/hooks/use_create_list_index.test.ts b/x-pack/solutions/security/plugins/lists/public/lists/hooks/use_create_list_index.test.ts similarity index 93% rename from x-pack/plugins/lists/public/lists/hooks/use_create_list_index.test.ts rename to x-pack/solutions/security/plugins/lists/public/lists/hooks/use_create_list_index.test.ts index 7021d49940f2..806d8b633708 100644 --- a/x-pack/plugins/lists/public/lists/hooks/use_create_list_index.test.ts +++ b/x-pack/solutions/security/plugins/lists/public/lists/hooks/use_create_list_index.test.ts @@ -15,7 +15,7 @@ import { createQueryWrapperMock } from '../mocks/query_wrapper'; jest.mock('@kbn/securitysolution-list-api'); -// TODO: This test should be ported to the package: packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.test.ts once we have mocks in kbn packages +// TODO: This test should be ported to the package: x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.test.ts once we have mocks in kbn packages const { wrapper: queryWrapper, queryClient } = createQueryWrapperMock(); diff --git a/x-pack/plugins/lists/public/lists/hooks/use_delete_list.test.ts b/x-pack/solutions/security/plugins/lists/public/lists/hooks/use_delete_list.test.ts similarity index 86% rename from x-pack/plugins/lists/public/lists/hooks/use_delete_list.test.ts rename to x-pack/solutions/security/plugins/lists/public/lists/hooks/use_delete_list.test.ts index 7a339f3e12a4..72276e7c474d 100644 --- a/x-pack/plugins/lists/public/lists/hooks/use_delete_list.test.ts +++ b/x-pack/solutions/security/plugins/lists/public/lists/hooks/use_delete_list.test.ts @@ -14,7 +14,7 @@ import { getListResponseMock } from '../../../common/schemas/response/list_schem jest.mock('@kbn/securitysolution-list-api'); -// TODO: This test should be ported to the package: packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.test.ts once we have mocks in kbn packages +// TODO: This test should be ported to the package: x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.test.ts once we have mocks in kbn packages describe('useDeleteList', () => { let httpMock: ReturnType<typeof httpServiceMock.createStartContract>; diff --git a/x-pack/plugins/lists/public/lists/hooks/use_export_list.test.ts b/x-pack/solutions/security/plugins/lists/public/lists/hooks/use_export_list.test.ts similarity index 86% rename from x-pack/plugins/lists/public/lists/hooks/use_export_list.test.ts rename to x-pack/solutions/security/plugins/lists/public/lists/hooks/use_export_list.test.ts index 8f08ad5c8bd0..6cafd4958f7a 100644 --- a/x-pack/plugins/lists/public/lists/hooks/use_export_list.test.ts +++ b/x-pack/solutions/security/plugins/lists/public/lists/hooks/use_export_list.test.ts @@ -12,7 +12,7 @@ import { httpServiceMock } from '@kbn/core/public/mocks'; jest.mock('@kbn/securitysolution-list-api'); -// TODO: Move this test to the kbn package: packages/kbn-securitysolution-list-hooks/src/use_export_list/index.ts once Mocks are ported from Kibana +// TODO: Move this test to the kbn package: x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.ts once Mocks are ported from Kibana describe('useExportList', () => { let httpMock: ReturnType<typeof httpServiceMock.createStartContract>; diff --git a/x-pack/plugins/lists/public/lists/hooks/use_import_list.test.ts b/x-pack/solutions/security/plugins/lists/public/lists/hooks/use_import_list.test.ts similarity index 94% rename from x-pack/plugins/lists/public/lists/hooks/use_import_list.test.ts rename to x-pack/solutions/security/plugins/lists/public/lists/hooks/use_import_list.test.ts index d56478a0321b..00a3a39f1f1a 100644 --- a/x-pack/plugins/lists/public/lists/hooks/use_import_list.test.ts +++ b/x-pack/solutions/security/plugins/lists/public/lists/hooks/use_import_list.test.ts @@ -14,7 +14,7 @@ import { getListResponseMock } from '../../../common/schemas/response/list_schem jest.mock('@kbn/securitysolution-list-api'); -// TODO: Port this test over to: packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts once mocks are moved to packages +// TODO: Port this test over to: x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts once mocks are moved to packages describe('useImportList', () => { let httpMock: ReturnType<typeof httpServiceMock.createStartContract>; diff --git a/x-pack/plugins/lists/public/lists/hooks/use_read_list_index.test.ts b/x-pack/solutions/security/plugins/lists/public/lists/hooks/use_read_list_index.test.ts similarity index 91% rename from x-pack/plugins/lists/public/lists/hooks/use_read_list_index.test.ts rename to x-pack/solutions/security/plugins/lists/public/lists/hooks/use_read_list_index.test.ts index 643ac2df5b05..cd36b6d53106 100644 --- a/x-pack/plugins/lists/public/lists/hooks/use_read_list_index.test.ts +++ b/x-pack/solutions/security/plugins/lists/public/lists/hooks/use_read_list_index.test.ts @@ -17,7 +17,7 @@ jest.mock('@kbn/securitysolution-list-api'); const { wrapper: queryWrapper } = createQueryWrapperMock(); -// TODO: Port this code over to the package: packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.test.ts once kibana has mocks in packages +// TODO: Port this code over to the package: x-pack/solutions/security/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.test.ts once kibana has mocks in packages // FLAKY: https://github.com/elastic/kibana/issues/178026 describe.skip('useReadListIndex', () => { diff --git a/x-pack/plugins/lists/public/lists/mocks/query_wrapper.tsx b/x-pack/solutions/security/plugins/lists/public/lists/mocks/query_wrapper.tsx similarity index 100% rename from x-pack/plugins/lists/public/lists/mocks/query_wrapper.tsx rename to x-pack/solutions/security/plugins/lists/public/lists/mocks/query_wrapper.tsx diff --git a/x-pack/plugins/lists/public/lists/types.ts b/x-pack/solutions/security/plugins/lists/public/lists/types.ts similarity index 100% rename from x-pack/plugins/lists/public/lists/types.ts rename to x-pack/solutions/security/plugins/lists/public/lists/types.ts diff --git a/x-pack/plugins/lists/public/plugin.ts b/x-pack/solutions/security/plugins/lists/public/plugin.ts similarity index 100% rename from x-pack/plugins/lists/public/plugin.ts rename to x-pack/solutions/security/plugins/lists/public/plugin.ts diff --git a/x-pack/plugins/lists/public/shared_exports.ts b/x-pack/solutions/security/plugins/lists/public/shared_exports.ts similarity index 100% rename from x-pack/plugins/lists/public/shared_exports.ts rename to x-pack/solutions/security/plugins/lists/public/shared_exports.ts diff --git a/x-pack/plugins/lists/public/types.ts b/x-pack/solutions/security/plugins/lists/public/types.ts similarity index 100% rename from x-pack/plugins/lists/public/types.ts rename to x-pack/solutions/security/plugins/lists/public/types.ts diff --git a/x-pack/plugins/lists/scripts/storybook.js b/x-pack/solutions/security/plugins/lists/scripts/storybook.js similarity index 100% rename from x-pack/plugins/lists/scripts/storybook.js rename to x-pack/solutions/security/plugins/lists/scripts/storybook.js diff --git a/x-pack/plugins/lists/server/config.mock.ts b/x-pack/solutions/security/plugins/lists/server/config.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/config.mock.ts rename to x-pack/solutions/security/plugins/lists/server/config.mock.ts diff --git a/x-pack/plugins/lists/server/config.test.ts b/x-pack/solutions/security/plugins/lists/server/config.test.ts similarity index 100% rename from x-pack/plugins/lists/server/config.test.ts rename to x-pack/solutions/security/plugins/lists/server/config.test.ts diff --git a/x-pack/plugins/lists/server/config.ts b/x-pack/solutions/security/plugins/lists/server/config.ts similarity index 100% rename from x-pack/plugins/lists/server/config.ts rename to x-pack/solutions/security/plugins/lists/server/config.ts diff --git a/x-pack/plugins/lists/server/error_with_status_code.ts b/x-pack/solutions/security/plugins/lists/server/error_with_status_code.ts similarity index 100% rename from x-pack/plugins/lists/server/error_with_status_code.ts rename to x-pack/solutions/security/plugins/lists/server/error_with_status_code.ts diff --git a/x-pack/plugins/lists/server/get_space_id.test.ts b/x-pack/solutions/security/plugins/lists/server/get_space_id.test.ts similarity index 100% rename from x-pack/plugins/lists/server/get_space_id.test.ts rename to x-pack/solutions/security/plugins/lists/server/get_space_id.test.ts diff --git a/x-pack/plugins/lists/server/get_space_id.ts b/x-pack/solutions/security/plugins/lists/server/get_space_id.ts similarity index 100% rename from x-pack/plugins/lists/server/get_space_id.ts rename to x-pack/solutions/security/plugins/lists/server/get_space_id.ts diff --git a/x-pack/plugins/lists/server/get_user.test.ts b/x-pack/solutions/security/plugins/lists/server/get_user.test.ts similarity index 100% rename from x-pack/plugins/lists/server/get_user.test.ts rename to x-pack/solutions/security/plugins/lists/server/get_user.test.ts diff --git a/x-pack/plugins/lists/server/get_user.ts b/x-pack/solutions/security/plugins/lists/server/get_user.ts similarity index 100% rename from x-pack/plugins/lists/server/get_user.ts rename to x-pack/solutions/security/plugins/lists/server/get_user.ts diff --git a/x-pack/plugins/lists/server/handlers/create_exception_list_handler.ts b/x-pack/solutions/security/plugins/lists/server/handlers/create_exception_list_handler.ts similarity index 100% rename from x-pack/plugins/lists/server/handlers/create_exception_list_handler.ts rename to x-pack/solutions/security/plugins/lists/server/handlers/create_exception_list_handler.ts diff --git a/x-pack/plugins/lists/server/index.ts b/x-pack/solutions/security/plugins/lists/server/index.ts similarity index 100% rename from x-pack/plugins/lists/server/index.ts rename to x-pack/solutions/security/plugins/lists/server/index.ts diff --git a/x-pack/plugins/lists/server/mocks.ts b/x-pack/solutions/security/plugins/lists/server/mocks.ts similarity index 100% rename from x-pack/plugins/lists/server/mocks.ts rename to x-pack/solutions/security/plugins/lists/server/mocks.ts diff --git a/x-pack/plugins/lists/server/plugin.ts b/x-pack/solutions/security/plugins/lists/server/plugin.ts similarity index 100% rename from x-pack/plugins/lists/server/plugin.ts rename to x-pack/solutions/security/plugins/lists/server/plugin.ts diff --git a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/create_endpoint_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/create_endpoint_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/create_endpoint_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/create_endpoint_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/create_exception_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/create_exception_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/create_exception_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/create_exception_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/create_exception_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/delete_endpoint_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/delete_endpoint_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/delete_exception_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/delete_exception_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/delete_exception_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/delete_exception_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/delete_exception_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/duplicate_exception_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/duplicate_exception_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/duplicate_exception_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/duplicate_exception_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/endpoint_disallowed_fields.ts b/x-pack/solutions/security/plugins/lists/server/routes/endpoint_disallowed_fields.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/endpoint_disallowed_fields.ts rename to x-pack/solutions/security/plugins/lists/server/routes/endpoint_disallowed_fields.ts diff --git a/x-pack/plugins/lists/server/routes/export_exception_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/export_exception_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/export_exception_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/export_exception_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/find_endpoint_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/find_endpoint_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/find_exception_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/find_exception_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/find_exception_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/find_exception_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/find_exception_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/import_exceptions_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/import_exceptions_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/import_exceptions_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/import_exceptions_route.ts diff --git a/x-pack/plugins/lists/server/routes/index.ts b/x-pack/solutions/security/plugins/lists/server/routes/index.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/index.ts rename to x-pack/solutions/security/plugins/lists/server/routes/index.ts diff --git a/x-pack/plugins/lists/server/routes/init_routes.ts b/x-pack/solutions/security/plugins/lists/server/routes/init_routes.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/init_routes.ts rename to x-pack/solutions/security/plugins/lists/server/routes/init_routes.ts diff --git a/x-pack/plugins/lists/server/routes/internal/create_exception_filter_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/internal/create_exception_filter_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/internal/create_exception_filter_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/internal/create_exception_filter_route.ts diff --git a/x-pack/plugins/lists/server/routes/internal/create_exceptions_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/internal/create_exceptions_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/internal/create_exceptions_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/internal/create_exceptions_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/internal/find_lists_by_size_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/internal/find_lists_by_size_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/internal/find_lists_by_size_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/internal/find_lists_by_size_route.ts diff --git a/x-pack/plugins/lists/server/routes/list/create_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list/create_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list/create_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list/create_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/list/delete_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list/delete_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list/delete_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list/delete_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/list/import_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list/import_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list/import_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list/import_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/list/patch_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list/patch_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list/patch_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list/patch_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/list/read_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list/read_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list/read_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list/read_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/list/update_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list/update_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list/update_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list/update_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_index/create_list_index_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_index/create_list_index_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_index/create_list_index_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_index/create_list_index_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_index/delete_list_index_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_index/delete_list_index_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_index/delete_list_index_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_index/delete_list_index_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_index/export_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_index/export_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_index/export_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_index/export_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_index/find_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_index/find_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_index/find_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_index/find_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_index/read_list_index_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_index/read_list_index_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_index/read_list_index_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_index/read_list_index_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_item/create_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_item/create_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_item/create_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_item/create_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_item/delete_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_item/delete_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_item/delete_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_item/delete_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_item/find_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_item/find_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_item/find_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_item/find_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_item/patch_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_item/patch_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_item/patch_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_item/patch_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_item/read_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_item/read_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_item/read_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_item/read_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_item/update_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_item/update_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_item/update_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_item/update_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/list_privileges/read_list_privileges_route.mock.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_privileges/read_list_privileges_route.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_privileges/read_list_privileges_route.mock.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_privileges/read_list_privileges_route.mock.ts diff --git a/x-pack/plugins/lists/server/routes/list_privileges/read_list_privileges_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/list_privileges/read_list_privileges_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/list_privileges/read_list_privileges_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/list_privileges/read_list_privileges_route.ts diff --git a/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/read_endpoint_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/read_endpoint_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/read_exception_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/read_exception_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/read_exception_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/read_exception_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/read_exception_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/summary_exception_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/summary_exception_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/summary_exception_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/update_endpoint_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/update_endpoint_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/update_exception_list_item_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/update_exception_list_item_route.ts diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts b/x-pack/solutions/security/plugins/lists/server/routes/update_exception_list_route.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/update_exception_list_route.ts rename to x-pack/solutions/security/plugins/lists/server/routes/update_exception_list_route.ts diff --git a/x-pack/plugins/lists/server/routes/utils/build_siem_response.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/build_siem_response.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/build_siem_response.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/build_siem_response.ts diff --git a/x-pack/plugins/lists/server/routes/utils/create_stream_from_buffer.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/create_stream_from_buffer.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/create_stream_from_buffer.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/create_stream_from_buffer.ts diff --git a/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/get_error_message_exception_list.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/get_error_message_exception_list.ts diff --git a/x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list_item.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/get_error_message_exception_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/get_error_message_exception_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/get_error_message_exception_list_item.ts diff --git a/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/get_exception_list_client.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/get_exception_list_client.ts diff --git a/x-pack/plugins/lists/server/routes/utils/get_internal_list_client.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/get_internal_list_client.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/get_internal_list_client.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/get_internal_list_client.ts diff --git a/x-pack/plugins/lists/server/routes/utils/get_list_client.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/get_list_client.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/get_list_client.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/get_list_client.ts diff --git a/x-pack/plugins/lists/server/routes/utils/index.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/index.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/index.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/index.ts diff --git a/x-pack/plugins/lists/server/routes/utils/route_validation.test.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/route_validation.test.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/route_validation.test.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/route_validation.test.ts diff --git a/x-pack/plugins/lists/server/routes/utils/route_validation.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/route_validation.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/route_validation.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/route_validation.ts diff --git a/x-pack/plugins/lists/server/routes/utils/validate_comments_to_update.test.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/validate_comments_to_update.test.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/validate_comments_to_update.test.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/validate_comments_to_update.test.ts diff --git a/x-pack/plugins/lists/server/routes/utils/validate_comments_to_update.ts b/x-pack/solutions/security/plugins/lists/server/routes/utils/validate_comments_to_update.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/utils/validate_comments_to_update.ts rename to x-pack/solutions/security/plugins/lists/server/routes/utils/validate_comments_to_update.ts diff --git a/x-pack/plugins/lists/server/routes/validate.ts b/x-pack/solutions/security/plugins/lists/server/routes/validate.ts similarity index 100% rename from x-pack/plugins/lists/server/routes/validate.ts rename to x-pack/solutions/security/plugins/lists/server/routes/validate.ts diff --git a/x-pack/plugins/lists/server/saved_objects/exception_list.ts b/x-pack/solutions/security/plugins/lists/server/saved_objects/exception_list.ts similarity index 100% rename from x-pack/plugins/lists/server/saved_objects/exception_list.ts rename to x-pack/solutions/security/plugins/lists/server/saved_objects/exception_list.ts diff --git a/x-pack/plugins/lists/server/saved_objects/index.ts b/x-pack/solutions/security/plugins/lists/server/saved_objects/index.ts similarity index 100% rename from x-pack/plugins/lists/server/saved_objects/index.ts rename to x-pack/solutions/security/plugins/lists/server/saved_objects/index.ts diff --git a/x-pack/plugins/lists/server/saved_objects/init_saved_objects.ts b/x-pack/solutions/security/plugins/lists/server/saved_objects/init_saved_objects.ts similarity index 100% rename from x-pack/plugins/lists/server/saved_objects/init_saved_objects.ts rename to x-pack/solutions/security/plugins/lists/server/saved_objects/init_saved_objects.ts diff --git a/x-pack/plugins/lists/server/saved_objects/migrations.test.ts b/x-pack/solutions/security/plugins/lists/server/saved_objects/migrations.test.ts similarity index 100% rename from x-pack/plugins/lists/server/saved_objects/migrations.test.ts rename to x-pack/solutions/security/plugins/lists/server/saved_objects/migrations.test.ts diff --git a/x-pack/plugins/lists/server/saved_objects/migrations.ts b/x-pack/solutions/security/plugins/lists/server/saved_objects/migrations.ts similarity index 100% rename from x-pack/plugins/lists/server/saved_objects/migrations.ts rename to x-pack/solutions/security/plugins/lists/server/saved_objects/migrations.ts diff --git a/x-pack/plugins/lists/server/schemas/common/get_shard.mock.ts b/x-pack/solutions/security/plugins/lists/server/schemas/common/get_shard.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/common/get_shard.mock.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/common/get_shard.mock.ts diff --git a/x-pack/plugins/lists/server/schemas/common/schemas.test.ts b/x-pack/solutions/security/plugins/lists/server/schemas/common/schemas.test.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/common/schemas.test.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/common/schemas.test.ts diff --git a/x-pack/plugins/lists/server/schemas/common/schemas.ts b/x-pack/solutions/security/plugins/lists/server/schemas/common/schemas.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/common/schemas.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/common/schemas.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_query/create_es_bulk_type.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/create_es_bulk_type.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_query/create_es_bulk_type.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/create_es_bulk_type.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_query/index.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/index.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_query/index.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/index.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_query/index_es_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/index_es_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_query/index_es_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/index_es_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_query/index_es_list_item_schema.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/index_es_list_item_schema.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_query/index_es_list_item_schema.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/index_es_list_item_schema.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_query/index_es_list_schema.mock.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/index_es_list_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_query/index_es_list_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/index_es_list_schema.mock.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_query/index_es_list_schema.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/index_es_list_schema.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_query/index_es_list_schema.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/index_es_list_schema.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_query/update_es_list_item_schema.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/update_es_list_item_schema.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_query/update_es_list_item_schema.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/update_es_list_item_schema.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_query/update_es_list_schema.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/update_es_list_schema.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_query/update_es_list_schema.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_query/update_es_list_schema.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_response/index.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/index.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_response/index.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/index.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.mock.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.mock.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.test.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.test.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.test.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.test.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_item_schema.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_schema.mock.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_schema.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_schema.mock.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_schema.mock.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_schema.test.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_schema.test.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_schema.test.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_schema.test.ts diff --git a/x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_schema.ts b/x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_schema.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/elastic_response/search_es_list_schema.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/elastic_response/search_es_list_schema.ts diff --git a/x-pack/plugins/lists/server/schemas/saved_objects/exceptions_list_so_schema.ts b/x-pack/solutions/security/plugins/lists/server/schemas/saved_objects/exceptions_list_so_schema.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/saved_objects/exceptions_list_so_schema.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/saved_objects/exceptions_list_so_schema.ts diff --git a/x-pack/plugins/lists/server/schemas/saved_objects/index.ts b/x-pack/solutions/security/plugins/lists/server/schemas/saved_objects/index.ts similarity index 100% rename from x-pack/plugins/lists/server/schemas/saved_objects/index.ts rename to x-pack/solutions/security/plugins/lists/server/schemas/saved_objects/index.ts diff --git a/x-pack/plugins/lists/server/scripts/check_env_variables.sh b/x-pack/solutions/security/plugins/lists/server/scripts/check_env_variables.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/check_env_variables.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/check_env_variables.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_all_exception_lists.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_all_exception_lists.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_all_exception_lists.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_all_exception_lists.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_endpoint_list_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_endpoint_list_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_endpoint_list_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_endpoint_list_item.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_endpoint_list_item_by_id.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_endpoint_list_item_by_id.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_endpoint_list_item_by_id.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_endpoint_list_item_by_id.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_exception_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_exception_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_exception_list.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list_by_id.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_exception_list_by_id.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_exception_list_by_id.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_exception_list_by_id.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_exception_list_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_exception_list_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_exception_list_item.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_exception_list_item_by_id.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_list.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_list_index.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_list_index.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_list_index.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_list_index.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_list_item_by_id.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_list_item_by_id.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_list_item_by_id.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_list_item_by_id.sh diff --git a/x-pack/plugins/lists/server/scripts/delete_list_item_by_value.sh b/x-pack/solutions/security/plugins/lists/server/scripts/delete_list_item_by_value.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/delete_list_item_by_value.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/delete_list_item_by_value.sh diff --git a/x-pack/plugins/lists/server/scripts/download_load_lists_example.sh b/x-pack/solutions/security/plugins/lists/server/scripts/download_load_lists_example.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/download_load_lists_example.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/download_load_lists_example.sh diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/files/import.ndjson b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/files/import.ndjson similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/files/import.ndjson rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/files/import.ndjson diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_auto_id.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_1.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_1.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_1.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_1.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_2_agnostic.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_2_agnostic.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_2_agnostic.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_2_agnostic.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_3.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_3.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_3.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_detection_3.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_1_multi_value_list.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_1_multi_value_list.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_1_multi_value_list.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_1_multi_value_list.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_1_non_value_list.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_1_non_value_list.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_1_non_value_list.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_1_non_value_list.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_2_multi_value_list.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_2_multi_value_list.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_2_multi_value_list.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_2_multi_value_list.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_2_non_value_list.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_2_non_value_list.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_2_non_value_list.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_2_non_value_list.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_3_non_value_list.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_3_non_value_list.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_3_non_value_list.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_3_non_value_list.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_3_single_value_list.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_3_single_value_list.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_3_single_value_list.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/references/exception_list_item_3_single_value_list.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/updates/simple_update.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/updates/simple_update.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json diff --git a/x-pack/plugins/lists/server/scripts/export_list_items.sh b/x-pack/solutions/security/plugins/lists/server/scripts/export_list_items.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/export_list_items.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/export_list_items.sh diff --git a/x-pack/plugins/lists/server/scripts/export_list_items_to_file.sh b/x-pack/solutions/security/plugins/lists/server/scripts/export_list_items_to_file.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/export_list_items_to_file.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/export_list_items_to_file.sh diff --git a/x-pack/plugins/lists/server/scripts/find_endpoint_list_items.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_endpoint_list_items.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_endpoint_list_items.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_endpoint_list_items.sh diff --git a/x-pack/plugins/lists/server/scripts/find_exception_list_items.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_exception_list_items.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_exception_list_items.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_exception_list_items.sh diff --git a/x-pack/plugins/lists/server/scripts/find_exception_list_items_by_filter.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_exception_list_items_by_filter.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_exception_list_items_by_filter.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_exception_list_items_by_filter.sh diff --git a/x-pack/plugins/lists/server/scripts/find_exception_lists.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_exception_lists.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_exception_lists.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_exception_lists.sh diff --git a/x-pack/plugins/lists/server/scripts/find_exception_lists_by_filter.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_exception_lists_by_filter.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_exception_lists_by_filter.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_exception_lists_by_filter.sh diff --git a/x-pack/plugins/lists/server/scripts/find_list_items.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_list_items.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_list_items.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_list_items.sh diff --git a/x-pack/plugins/lists/server/scripts/find_list_items_with_cursor.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_list_items_with_cursor.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_list_items_with_cursor.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_list_items_with_cursor.sh diff --git a/x-pack/plugins/lists/server/scripts/find_list_items_with_sort.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_list_items_with_sort.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_list_items_with_sort.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_list_items_with_sort.sh diff --git a/x-pack/plugins/lists/server/scripts/find_list_items_with_sort_cursor.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_list_items_with_sort_cursor.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_list_items_with_sort_cursor.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_list_items_with_sort_cursor.sh diff --git a/x-pack/plugins/lists/server/scripts/find_lists.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_lists.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_lists.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_lists.sh diff --git a/x-pack/plugins/lists/server/scripts/find_lists_with_cursor.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_lists_with_cursor.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_lists_with_cursor.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_lists_with_cursor.sh diff --git a/x-pack/plugins/lists/server/scripts/find_lists_with_filter.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_lists_with_filter.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_lists_with_filter.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_lists_with_filter.sh diff --git a/x-pack/plugins/lists/server/scripts/find_lists_with_sort.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_lists_with_sort.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_lists_with_sort.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_lists_with_sort.sh diff --git a/x-pack/plugins/lists/server/scripts/find_lists_with_sort_cursor.sh b/x-pack/solutions/security/plugins/lists/server/scripts/find_lists_with_sort_cursor.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/find_lists_with_sort_cursor.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/find_lists_with_sort_cursor.sh diff --git a/x-pack/plugins/lists/server/scripts/get_endpoint_list_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/get_endpoint_list_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/get_endpoint_list_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/get_endpoint_list_item.sh diff --git a/x-pack/plugins/lists/server/scripts/get_endpoint_list_item_by_id.sh b/x-pack/solutions/security/plugins/lists/server/scripts/get_endpoint_list_item_by_id.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/get_endpoint_list_item_by_id.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/get_endpoint_list_item_by_id.sh diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/get_exception_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/get_exception_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/get_exception_list.sh diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list_by_id.sh b/x-pack/solutions/security/plugins/lists/server/scripts/get_exception_list_by_id.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/get_exception_list_by_id.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/get_exception_list_by_id.sh diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/get_exception_list_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/get_exception_list_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/get_exception_list_item.sh diff --git a/x-pack/plugins/lists/server/scripts/get_exception_list_item_by_id.sh b/x-pack/solutions/security/plugins/lists/server/scripts/get_exception_list_item_by_id.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/get_exception_list_item_by_id.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/get_exception_list_item_by_id.sh diff --git a/x-pack/plugins/lists/server/scripts/get_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/get_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/get_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/get_list.sh diff --git a/x-pack/plugins/lists/server/scripts/get_list_item_by_id.sh b/x-pack/solutions/security/plugins/lists/server/scripts/get_list_item_by_id.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/get_list_item_by_id.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/get_list_item_by_id.sh diff --git a/x-pack/plugins/lists/server/scripts/get_list_item_by_value.sh b/x-pack/solutions/security/plugins/lists/server/scripts/get_list_item_by_value.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/get_list_item_by_value.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/get_list_item_by_value.sh diff --git a/x-pack/plugins/lists/server/scripts/get_privileges.sh b/x-pack/solutions/security/plugins/lists/server/scripts/get_privileges.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/get_privileges.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/get_privileges.sh diff --git a/x-pack/plugins/lists/server/scripts/hard_reset.sh b/x-pack/solutions/security/plugins/lists/server/scripts/hard_reset.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/hard_reset.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/hard_reset.sh diff --git a/x-pack/plugins/lists/server/scripts/import_exception_lists.sh b/x-pack/solutions/security/plugins/lists/server/scripts/import_exception_lists.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/import_exception_lists.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/import_exception_lists.sh diff --git a/x-pack/plugins/lists/server/scripts/import_list_items.sh b/x-pack/solutions/security/plugins/lists/server/scripts/import_list_items.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/import_list_items.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/import_list_items.sh diff --git a/x-pack/plugins/lists/server/scripts/import_list_items_by_filename.sh b/x-pack/solutions/security/plugins/lists/server/scripts/import_list_items_by_filename.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/import_list_items_by_filename.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/import_list_items_by_filename.sh diff --git a/x-pack/plugins/lists/server/scripts/lists/files/binary.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/binary.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/binary.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/binary.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/boolean.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/boolean.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/boolean.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/boolean.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/byte.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/byte.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/byte.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/byte.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/date.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/date.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/date.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/date.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/date_nanos.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/date_nanos.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/date_nanos.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/date_nanos.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/date_range.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/date_range.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/date_range.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/date_range.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/date_range_custom_format.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/date_range_custom_format.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/date_range_custom_format.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/date_range_custom_format.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/double.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/double.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/double.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/double.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/double_range.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/double_range.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/double_range.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/double_range.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/float.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/float.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/float.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/float.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/float_range.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/float_range.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/float_range.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/float_range.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/geo_hash.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/geo_hash.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/geo_hash.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/geo_hash.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/geo_point.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/geo_point.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/geo_point.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/geo_point.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/geo_point_wkt.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/geo_point_wkt.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/geo_point_wkt.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/geo_point_wkt.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/half_float.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/half_float.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/half_float.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/half_float.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/hosts.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/hosts.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/hosts.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/hosts.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/integer.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/integer.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/integer.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/integer.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/integer_range.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/integer_range.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/integer_range.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/integer_range.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/ip_range.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/ip_range.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/ip_range.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/ip_range.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/ip_range_cidr.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/ip_range_cidr.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/ip_range_cidr.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/ip_range_cidr.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/ip_range_mixed.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/ip_range_mixed.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/ip_range_mixed.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/ip_range_mixed.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/ips.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/ips.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/ips.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/ips.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/long.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/long.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/long.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/long.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/long_range.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/long_range.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/long_range.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/long_range.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/short.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/short.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/short.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/short.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/files/text.txt b/x-pack/solutions/security/plugins/lists/server/scripts/lists/files/text.txt similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/files/text.txt rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/files/text.txt diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/binary_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/binary_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/binary_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/binary_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/boolean_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/boolean_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/boolean_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/boolean_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/byte_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/byte_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/byte_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/byte_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/date_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/date_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/date_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/date_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/date_nanos_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/date_nanos_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/date_nanos_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/date_nanos_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/date_range_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/date_range_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/date_range_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/date_range_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/double_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/double_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/double_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/double_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/double_range_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/double_range_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/double_range_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/double_range_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/float_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/float_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/float_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/float_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/float_range_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/float_range_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/float_range_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/float_range_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/geo_point_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/geo_point_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/geo_point_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/geo_point_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/geo_shape_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/geo_shape_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/geo_shape_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/geo_shape_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/half_float_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/half_float_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/half_float_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/half_float_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/integer_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/integer_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/integer_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/integer_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/integer_range_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/integer_range_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/integer_range_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/integer_range_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/ip_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/ip_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/ip_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/ip_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/ip_item_everything.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/ip_item_everything.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/ip_item_everything.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/ip_item_everything.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/ip_range_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/ip_range_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/ip_range_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/ip_range_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/keyword_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/keyword_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/keyword_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/keyword_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/long_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/long_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/long_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/long_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/long_range_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/long_range_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/long_range_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/long_range_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/shape_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/shape_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/shape_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/shape_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/short_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/short_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/short_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/short_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/text_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/text_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/items/text_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/items/text_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/list_ip_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/list_ip_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/list_ip_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/list_ip_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/list_keyword_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/list_keyword_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/list_keyword_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/list_keyword_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/auto_id.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/auto_id.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/auto_id.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/auto_id.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/binary.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/binary.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/binary.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/binary.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/boolean.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/boolean.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/boolean.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/boolean.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/byte.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/byte.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/byte.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/byte.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/date.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/date.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/date.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/date.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/date_nanos.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/date_nanos.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/date_nanos.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/date_nanos.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/date_range.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/date_range.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/date_range.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/date_range.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/date_range_custom_format.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/date_range_custom_format.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/date_range_custom_format.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/date_range_custom_format.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/double.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/double.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/double.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/double.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/double_range.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/double_range.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/double_range.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/double_range.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/everything.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/everything.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/everything.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/everything.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/float.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/float.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/float.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/float.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/float_range.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/float_range.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/float_range.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/float_range.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/geo_point.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/geo_point.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/geo_point.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/geo_point.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/geo_shape.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/geo_shape.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/geo_shape.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/geo_shape.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/half_float.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/half_float.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/half_float.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/half_float.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/integer.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/integer.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/integer.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/integer.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/integer_range.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/integer_range.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/integer_range.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/integer_range.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/ip.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/ip.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/ip.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/ip.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/ip_custom_format.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/ip_custom_format.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/ip_custom_format.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/ip_custom_format.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/ip_no_id.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/ip_no_id.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/ip_no_id.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/ip_no_id.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/ip_range.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/ip_range.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/ip_range.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/ip_range.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/keyword.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/keyword.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/keyword.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/keyword.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/keyword_custom_format.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/keyword_custom_format.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/keyword_custom_format.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/keyword_custom_format.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/long.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/long.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/long.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/long.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/shape.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/shape.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/shape.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/shape.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/short.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/short.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/short.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/short.json diff --git a/x-pack/plugins/lists/server/scripts/lists/new/lists/text.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/text.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/new/lists/text.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/new/lists/text.json diff --git a/x-pack/plugins/lists/server/scripts/lists/patches/ip_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/patches/ip_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/patches/ip_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/patches/ip_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/patches/simplest_updated_name.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/patches/simplest_updated_name.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/patches/simplest_updated_name.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/patches/simplest_updated_name.json diff --git a/x-pack/plugins/lists/server/scripts/lists/updates/ip_item.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/updates/ip_item.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/updates/ip_item.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/updates/ip_item.json diff --git a/x-pack/plugins/lists/server/scripts/lists/updates/simple_update.json b/x-pack/solutions/security/plugins/lists/server/scripts/lists/updates/simple_update.json similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists/updates/simple_update.json rename to x-pack/solutions/security/plugins/lists/server/scripts/lists/updates/simple_update.json diff --git a/x-pack/plugins/lists/server/scripts/lists_index_exists.sh b/x-pack/solutions/security/plugins/lists/server/scripts/lists_index_exists.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/lists_index_exists.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/lists_index_exists.sh diff --git a/x-pack/plugins/lists/server/scripts/patch_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/patch_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/patch_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/patch_list.sh diff --git a/x-pack/plugins/lists/server/scripts/patch_list_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/patch_list_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/patch_list_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/patch_list_item.sh diff --git a/x-pack/plugins/lists/server/scripts/post_endpoint_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/post_endpoint_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/post_endpoint_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/post_endpoint_list.sh diff --git a/x-pack/plugins/lists/server/scripts/post_endpoint_list_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/post_endpoint_list_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/post_endpoint_list_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/post_endpoint_list_item.sh diff --git a/x-pack/plugins/lists/server/scripts/post_exception_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/post_exception_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/post_exception_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/post_exception_list.sh diff --git a/x-pack/plugins/lists/server/scripts/post_exception_list_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/post_exception_list_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/post_exception_list_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/post_exception_list_item.sh diff --git a/x-pack/plugins/lists/server/scripts/post_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/post_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/post_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/post_list.sh diff --git a/x-pack/plugins/lists/server/scripts/post_list_index.sh b/x-pack/solutions/security/plugins/lists/server/scripts/post_list_index.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/post_list_index.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/post_list_index.sh diff --git a/x-pack/plugins/lists/server/scripts/post_list_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/post_list_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/post_list_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/post_list_item.sh diff --git a/x-pack/plugins/lists/server/scripts/post_x_exception_list_items.sh b/x-pack/solutions/security/plugins/lists/server/scripts/post_x_exception_list_items.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/post_x_exception_list_items.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/post_x_exception_list_items.sh diff --git a/x-pack/plugins/lists/server/scripts/quick_start.sh b/x-pack/solutions/security/plugins/lists/server/scripts/quick_start.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/quick_start.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/quick_start.sh diff --git a/x-pack/plugins/lists/server/scripts/quick_start_value_list_references.sh b/x-pack/solutions/security/plugins/lists/server/scripts/quick_start_value_list_references.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/quick_start_value_list_references.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/quick_start_value_list_references.sh diff --git a/x-pack/plugins/lists/server/scripts/summary_exception_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/summary_exception_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/summary_exception_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/summary_exception_list.sh diff --git a/x-pack/plugins/lists/server/scripts/update_endpoint_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/update_endpoint_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/update_endpoint_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/update_endpoint_item.sh diff --git a/x-pack/plugins/lists/server/scripts/update_exception_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/update_exception_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/update_exception_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/update_exception_list.sh diff --git a/x-pack/plugins/lists/server/scripts/update_exception_list_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/update_exception_list_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/update_exception_list_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/update_exception_list_item.sh diff --git a/x-pack/plugins/lists/server/scripts/update_list.sh b/x-pack/solutions/security/plugins/lists/server/scripts/update_list.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/update_list.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/update_list.sh diff --git a/x-pack/plugins/lists/server/scripts/update_list_item.sh b/x-pack/solutions/security/plugins/lists/server/scripts/update_list_item.sh similarity index 100% rename from x-pack/plugins/lists/server/scripts/update_list_item.sh rename to x-pack/solutions/security/plugins/lists/server/scripts/update_list_item.sh diff --git a/x-pack/plugins/lists/server/services/exception_lists/build_exception_filter.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/build_exception_filter.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/build_exception_filter.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/build_exception_filter.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/build_exception_filter.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/build_exception_filter.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/build_exception_filter.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/build_exception_filter.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/bulk_create_exception_list_items.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/bulk_create_exception_list_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/bulk_create_exception_list_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/bulk_create_exception_list_items.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/close_point_in_time.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/close_point_in_time.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/close_point_in_time.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/close_point_in_time.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_list.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/create_endpoint_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/create_endpoint_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/create_endpoint_list.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/create_exception_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/create_exception_list.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/create_exception_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/create_exception_list_item.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/delete_exception_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/delete_exception_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/delete_exception_list.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/delete_exception_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/delete_exception_list_item.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/delete_exception_list_items_by_list.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/exception_list_client.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/exception_list_client.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/exception_list_client.mock.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/exception_list_client.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/exception_list_client.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/exception_list_client.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/exception_list_client.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/exception_list_client.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/exception_list_client_types.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/exception_list_client_types.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/export_exception_list_and_items.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/export_exception_list_and_items.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/export_exception_list_and_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/export_exception_list_and_items.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/find_exception_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list_item.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item_point_in_time_finder.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list_item_point_in_time_finder.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/find_exception_list_item_point_in_time_finder.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list_item_point_in_time_finder.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list_items.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items_point_in_time_finder.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list_items_point_in_time_finder.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items_point_in_time_finder.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list_items_point_in_time_finder.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_point_in_time_finder.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list_point_in_time_finder.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/find_exception_list_point_in_time_finder.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_exception_list_point_in_time_finder.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_value_list_exception_list_items.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_value_list_exception_list_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/find_value_list_exception_list_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_value_list_exception_list_items.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_value_list_exception_list_items_point_in_time_finder.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_value_list_exception_list_items_point_in_time_finder.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/find_value_list_exception_list_items_point_in_time_finder.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/find_value_list_exception_list_items_point_in_time_finder.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/get_exception_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/get_exception_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/get_exception_list.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/get_exception_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/get_exception_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/get_exception_list_item.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/get_exception_list_summary.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/get_exception_list_summary.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/get_exception_list_summary.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/import_exception_list_and_items.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/import_exception_list_and_items.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/import_exception_list_and_items.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/import_exception_list_and_items.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/import_exception_list_and_items.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/import_exception_list_and_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/import_exception_list_and_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/import_exception_list_and_items.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/index.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/index.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/index.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/index.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/open_point_in_time.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/open_point_in_time.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/open_point_in_time.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/open_point_in_time.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/update_exception_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/update_exception_list.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/update_exception_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/update_exception_list_item.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_overwrite_exception_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/update_overwrite_exception_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/update_overwrite_exception_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/update_overwrite_exception_list_item.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/errors.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/errors.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/errors.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/errors.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/get_exception_list_filter.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/get_exception_list_filter.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/get_exception_list_filter.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/get_exception_list_filter.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/get_exception_list_filter.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/get_exception_list_filter.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/get_exception_list_filter.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/get_exception_list_filter.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/get_exception_lists_item_filter.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/get_exception_lists_item_filter.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/get_exception_lists_item_filter.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/get_exception_lists_item_filter.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/get_exception_lists_item_filter.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/get_exception_lists_item_filter.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/get_exception_lists_item_filter.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/get_exception_lists_item_filter.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_items.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_items.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_items.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_items.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_items.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_items.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_lists.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_lists.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_lists.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_lists.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_lists.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_lists.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_lists.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_create_imported_lists.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_items.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_items.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_items.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_items.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_items.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_items.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_lists.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_lists.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_lists.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_lists.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_lists.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_lists.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_lists.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/bulk_update_imported_lists.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/create_exceptions_stream_logic.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/create_exceptions_stream_logic.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/create_exceptions_stream_logic.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/create_exceptions_stream_logic.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/create_exceptions_stream_logic.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/create_exceptions_stream_logic.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/create_exceptions_stream_logic.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/create_exceptions_stream_logic.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_items.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_items.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_items.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_items.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_items.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_items.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_lists.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_lists.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_lists.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_lists.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_lists.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_lists.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_lists.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/dedupe_incoming_lists.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/delete_list_items_to_overwrite.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/delete_list_items_to_overwrite.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/delete_list_items_to_overwrite.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/delete_list_items_to_overwrite.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/delete_list_items_to_overwrite.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/delete_list_items_to_overwrite.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/delete_list_items_to_overwrite.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/delete_list_items_to_overwrite.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_item_types.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_item_types.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_item_types.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_item_types.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_item_types.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_item_types.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_item_types.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_item_types.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_types.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_types.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_types.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_types.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_types.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_types.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_types.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/find_all_exception_list_types.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/import_exception_list_items.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/import_exception_list_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/import_exception_list_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/import_exception_list_items.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/import_exception_lists.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/import_exception_lists.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/import_exception_lists.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/import_exception_lists.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/is_import_regular.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/is_import_regular.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/is_import_regular.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/is_import_regular.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/is_import_regular.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/is_import_regular.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/is_import_regular.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/is_import_regular.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_exception_items_to_create_or_update.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_exception_items_to_create_or_update.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_exception_items_to_create_or_update.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_exception_items_to_create_or_update.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_exception_items_to_create_update.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_exception_items_to_create_update.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_exception_items_to_create_update.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_exception_items_to_create_update.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_exception_lists_to_create_or_update.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_exception_lists_to_create_or_update.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_exception_lists_to_create_or_update.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_exception_lists_to_create_or_update.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_exception_lists_to_create_update.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_exception_lists_to_create_update.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_exception_lists_to_create_update.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_exception_lists_to_create_update.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_import_by_namespace.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_import_by_namespace.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_import_by_namespace.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_import_by_namespace.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_import_by_namespace.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_import_by_namespace.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_import_by_namespace.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_import_by_namespace.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_import_responses.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_import_responses.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_import_responses.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_import_responses.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_import_responses.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_import_responses.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/import/sort_import_responses.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/import/sort_import_responses.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/index.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/index.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/index.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/index.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/index.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/index.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/index.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/index.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/validate_data.test.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/validate_data.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/validate_data.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/validate_data.test.ts diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils/validate_data.ts b/x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/validate_data.ts similarity index 100% rename from x-pack/plugins/lists/server/services/exception_lists/utils/validate_data.ts rename to x-pack/solutions/security/plugins/lists/server/services/exception_lists/utils/validate_data.ts diff --git a/x-pack/plugins/lists/server/services/extension_points/errors.ts b/x-pack/solutions/security/plugins/lists/server/services/extension_points/errors.ts similarity index 100% rename from x-pack/plugins/lists/server/services/extension_points/errors.ts rename to x-pack/solutions/security/plugins/lists/server/services/extension_points/errors.ts diff --git a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/extension_points/extension_point_storage.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/extension_points/extension_point_storage.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/extension_points/extension_point_storage.mock.ts diff --git a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.test.ts b/x-pack/solutions/security/plugins/lists/server/services/extension_points/extension_point_storage.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/extension_points/extension_point_storage.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/extension_points/extension_point_storage.test.ts diff --git a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.ts b/x-pack/solutions/security/plugins/lists/server/services/extension_points/extension_point_storage.ts similarity index 100% rename from x-pack/plugins/lists/server/services/extension_points/extension_point_storage.ts rename to x-pack/solutions/security/plugins/lists/server/services/extension_points/extension_point_storage.ts diff --git a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.test.ts b/x-pack/solutions/security/plugins/lists/server/services/extension_points/extension_point_storage_client.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/extension_points/extension_point_storage_client.test.ts diff --git a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.ts b/x-pack/solutions/security/plugins/lists/server/services/extension_points/extension_point_storage_client.ts similarity index 100% rename from x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.ts rename to x-pack/solutions/security/plugins/lists/server/services/extension_points/extension_point_storage_client.ts diff --git a/x-pack/plugins/lists/server/services/extension_points/index.ts b/x-pack/solutions/security/plugins/lists/server/services/extension_points/index.ts similarity index 100% rename from x-pack/plugins/lists/server/services/extension_points/index.ts rename to x-pack/solutions/security/plugins/lists/server/services/extension_points/index.ts diff --git a/x-pack/plugins/lists/server/services/extension_points/types.ts b/x-pack/solutions/security/plugins/lists/server/services/extension_points/types.ts similarity index 100% rename from x-pack/plugins/lists/server/services/extension_points/types.ts rename to x-pack/solutions/security/plugins/lists/server/services/extension_points/types.ts diff --git a/x-pack/plugins/lists/server/services/items/buffer_lines.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/buffer_lines.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/buffer_lines.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/buffer_lines.test.ts diff --git a/x-pack/plugins/lists/server/services/items/buffer_lines.ts b/x-pack/solutions/security/plugins/lists/server/services/items/buffer_lines.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/buffer_lines.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/buffer_lines.ts diff --git a/x-pack/plugins/lists/server/services/items/create_list_item.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/create_list_item.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/create_list_item.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/create_list_item.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/create_list_item.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/create_list_item.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/create_list_item.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/create_list_item.test.ts diff --git a/x-pack/plugins/lists/server/services/items/create_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/items/create_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/create_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/create_list_item.ts diff --git a/x-pack/plugins/lists/server/services/items/create_list_items_bulk.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/create_list_items_bulk.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/create_list_items_bulk.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/create_list_items_bulk.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/create_list_items_bulk.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/create_list_items_bulk.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/create_list_items_bulk.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/create_list_items_bulk.test.ts diff --git a/x-pack/plugins/lists/server/services/items/create_list_items_bulk.ts b/x-pack/solutions/security/plugins/lists/server/services/items/create_list_items_bulk.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/create_list_items_bulk.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/create_list_items_bulk.ts diff --git a/x-pack/plugins/lists/server/services/items/delete_list_item.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/delete_list_item.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/delete_list_item.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/delete_list_item.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item.test.ts diff --git a/x-pack/plugins/lists/server/services/items/delete_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/delete_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item.ts diff --git a/x-pack/plugins/lists/server/services/items/delete_list_item_by_value.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item_by_value.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/delete_list_item_by_value.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item_by_value.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/delete_list_item_by_value.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item_by_value.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/delete_list_item_by_value.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item_by_value.test.ts diff --git a/x-pack/plugins/lists/server/services/items/delete_list_item_by_value.ts b/x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item_by_value.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/delete_list_item_by_value.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/delete_list_item_by_value.ts diff --git a/x-pack/plugins/lists/server/services/items/find_all_list_items.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/find_all_list_items.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/find_all_list_items.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/find_all_list_items.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/find_all_list_items.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/find_all_list_items.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/find_all_list_items.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/find_all_list_items.test.ts diff --git a/x-pack/plugins/lists/server/services/items/find_all_list_items.ts b/x-pack/solutions/security/plugins/lists/server/services/items/find_all_list_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/find_all_list_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/find_all_list_items.ts diff --git a/x-pack/plugins/lists/server/services/items/find_list_item.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/find_list_item.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/find_list_item.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/find_list_item.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/find_list_item.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/find_list_item.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/find_list_item.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/find_list_item.test.ts diff --git a/x-pack/plugins/lists/server/services/items/find_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/items/find_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/find_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/find_list_item.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item.test.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_by_value.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_value.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item_by_value.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_value.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_by_value.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_value.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item_by_value.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_value.test.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_by_value.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_value.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item_by_value.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_value.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_by_values.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_values.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item_by_values.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_values.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_by_values.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_values.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item_by_values.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_values.test.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_by_values.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_values.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item_by_values.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_by_values.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_index.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_index.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item_index.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_index.test.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_index.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_index.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item_index.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_index.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_template.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_template.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item_template.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_template.test.ts diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_template.ts b/x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_template.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/get_list_item_template.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/get_list_item_template.ts diff --git a/x-pack/plugins/lists/server/services/items/index.ts b/x-pack/solutions/security/plugins/lists/server/services/items/index.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/index.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/index.ts diff --git a/x-pack/plugins/lists/server/services/items/list_item_mappings.json b/x-pack/solutions/security/plugins/lists/server/services/items/list_item_mappings.json similarity index 100% rename from x-pack/plugins/lists/server/services/items/list_item_mappings.json rename to x-pack/solutions/security/plugins/lists/server/services/items/list_item_mappings.json diff --git a/x-pack/plugins/lists/server/services/items/list_item_policy.json b/x-pack/solutions/security/plugins/lists/server/services/items/list_item_policy.json similarity index 100% rename from x-pack/plugins/lists/server/services/items/list_item_policy.json rename to x-pack/solutions/security/plugins/lists/server/services/items/list_item_policy.json diff --git a/x-pack/plugins/lists/server/services/items/search_list_item_by_values.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/search_list_item_by_values.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/search_list_item_by_values.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/search_list_item_by_values.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/search_list_item_by_values.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/search_list_item_by_values.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/search_list_item_by_values.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/search_list_item_by_values.test.ts diff --git a/x-pack/plugins/lists/server/services/items/search_list_item_by_values.ts b/x-pack/solutions/security/plugins/lists/server/services/items/search_list_item_by_values.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/search_list_item_by_values.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/search_list_item_by_values.ts diff --git a/x-pack/plugins/lists/server/services/items/test_readable.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/test_readable.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/test_readable.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/test_readable.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/update_list_item.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/update_list_item.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/update_list_item.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/update_list_item.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/update_list_item.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/update_list_item.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/update_list_item.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/update_list_item.test.ts diff --git a/x-pack/plugins/lists/server/services/items/update_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/items/update_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/update_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/update_list_item.ts diff --git a/x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/write_lines_to_bulk_list_items.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/write_lines_to_bulk_list_items.mock.ts diff --git a/x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/write_lines_to_bulk_list_items.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/write_lines_to_bulk_list_items.test.ts diff --git a/x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.ts b/x-pack/solutions/security/plugins/lists/server/services/items/write_lines_to_bulk_list_items.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/write_lines_to_bulk_list_items.ts diff --git a/x-pack/plugins/lists/server/services/items/write_list_items_to_stream.test.ts b/x-pack/solutions/security/plugins/lists/server/services/items/write_list_items_to_stream.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/write_list_items_to_stream.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/write_list_items_to_stream.test.ts diff --git a/x-pack/plugins/lists/server/services/items/write_list_items_to_stream.ts b/x-pack/solutions/security/plugins/lists/server/services/items/write_list_items_to_stream.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/write_list_items_to_stream.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/write_list_items_to_stream.ts diff --git a/x-pack/plugins/lists/server/services/items/write_list_items_to_streams.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/items/write_list_items_to_streams.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/items/write_list_items_to_streams.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/items/write_list_items_to_streams.mock.ts diff --git a/x-pack/plugins/lists/server/services/lists/create_list.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/create_list.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/create_list.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/create_list.mock.ts diff --git a/x-pack/plugins/lists/server/services/lists/create_list.test.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/create_list.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/create_list.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/create_list.test.ts diff --git a/x-pack/plugins/lists/server/services/lists/create_list.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/create_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/create_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/create_list.ts diff --git a/x-pack/plugins/lists/server/services/lists/create_list_if_it_does_not_exist.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/create_list_if_it_does_not_exist.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/create_list_if_it_does_not_exist.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/create_list_if_it_does_not_exist.ts diff --git a/x-pack/plugins/lists/server/services/lists/delete_list.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/delete_list.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/delete_list.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/delete_list.mock.ts diff --git a/x-pack/plugins/lists/server/services/lists/delete_list.test.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/delete_list.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/delete_list.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/delete_list.test.ts diff --git a/x-pack/plugins/lists/server/services/lists/delete_list.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/delete_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/delete_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/delete_list.ts diff --git a/x-pack/plugins/lists/server/services/lists/find_list.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/find_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/find_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/find_list.ts diff --git a/x-pack/plugins/lists/server/services/lists/get_list.test.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/get_list.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/get_list.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/get_list.test.ts diff --git a/x-pack/plugins/lists/server/services/lists/get_list.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/get_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/get_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/get_list.ts diff --git a/x-pack/plugins/lists/server/services/lists/get_list_index.test.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/get_list_index.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/get_list_index.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/get_list_index.test.ts diff --git a/x-pack/plugins/lists/server/services/lists/get_list_index.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/get_list_index.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/get_list_index.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/get_list_index.ts diff --git a/x-pack/plugins/lists/server/services/lists/get_list_template.test.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/get_list_template.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/get_list_template.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/get_list_template.test.ts diff --git a/x-pack/plugins/lists/server/services/lists/get_list_template.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/get_list_template.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/get_list_template.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/get_list_template.ts diff --git a/x-pack/plugins/lists/server/services/lists/index.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/index.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/index.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/index.ts diff --git a/x-pack/plugins/lists/server/services/lists/list_client.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/list_client.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/list_client.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/list_client.mock.ts diff --git a/x-pack/plugins/lists/server/services/lists/list_client.test.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/list_client.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/list_client.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/list_client.test.ts diff --git a/x-pack/plugins/lists/server/services/lists/list_client.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/list_client.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/list_client.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/list_client.ts diff --git a/x-pack/plugins/lists/server/services/lists/list_client_types.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/list_client_types.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/list_client_types.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/list_client_types.ts diff --git a/x-pack/plugins/lists/server/services/lists/list_mappings.json b/x-pack/solutions/security/plugins/lists/server/services/lists/list_mappings.json similarity index 100% rename from x-pack/plugins/lists/server/services/lists/list_mappings.json rename to x-pack/solutions/security/plugins/lists/server/services/lists/list_mappings.json diff --git a/x-pack/plugins/lists/server/services/lists/list_policy.json b/x-pack/solutions/security/plugins/lists/server/services/lists/list_policy.json similarity index 100% rename from x-pack/plugins/lists/server/services/lists/list_policy.json rename to x-pack/solutions/security/plugins/lists/server/services/lists/list_policy.json diff --git a/x-pack/plugins/lists/server/services/lists/types.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/types.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/types.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/types.ts diff --git a/x-pack/plugins/lists/server/services/lists/update_list.mock.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/update_list.mock.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/update_list.mock.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/update_list.mock.ts diff --git a/x-pack/plugins/lists/server/services/lists/update_list.test.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/update_list.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/update_list.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/update_list.test.ts diff --git a/x-pack/plugins/lists/server/services/lists/update_list.ts b/x-pack/solutions/security/plugins/lists/server/services/lists/update_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/lists/update_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/lists/update_list.ts diff --git a/x-pack/plugins/lists/server/services/utils/calculate_scroll_math.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/calculate_scroll_math.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/calculate_scroll_math.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/calculate_scroll_math.ts diff --git a/x-pack/plugins/lists/server/services/utils/check_version_conflict.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/check_version_conflict.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/check_version_conflict.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/check_version_conflict.ts diff --git a/x-pack/plugins/lists/server/services/utils/encode_decode_cursor.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/encode_decode_cursor.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/encode_decode_cursor.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/encode_decode_cursor.ts diff --git a/x-pack/plugins/lists/server/services/utils/escape_query.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/escape_query.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/escape_query.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/escape_query.ts diff --git a/x-pack/plugins/lists/server/services/utils/find_source_type.test.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/find_source_type.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/find_source_type.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/find_source_type.test.ts diff --git a/x-pack/plugins/lists/server/services/utils/find_source_type.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/find_source_type.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/find_source_type.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/find_source_type.ts diff --git a/x-pack/plugins/lists/server/services/utils/find_source_value.test.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/find_source_value.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/find_source_value.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/find_source_value.test.ts diff --git a/x-pack/plugins/lists/server/services/utils/find_source_value.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/find_source_value.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/find_source_value.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/find_source_value.ts diff --git a/x-pack/plugins/lists/server/services/utils/get_query_filter.test.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/get_query_filter.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/get_query_filter.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/get_query_filter.test.ts diff --git a/x-pack/plugins/lists/server/services/utils/get_query_filter.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/get_query_filter.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/get_query_filter.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/get_query_filter.ts diff --git a/x-pack/plugins/lists/server/services/utils/get_query_filter_from_type_value.test.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/get_query_filter_from_type_value.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/get_query_filter_from_type_value.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/get_query_filter_from_type_value.test.ts diff --git a/x-pack/plugins/lists/server/services/utils/get_query_filter_from_type_value.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/get_query_filter_from_type_value.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/get_query_filter_from_type_value.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/get_query_filter_from_type_value.ts diff --git a/x-pack/plugins/lists/server/services/utils/get_search_after_scroll.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/get_search_after_scroll.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/get_search_after_scroll.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/get_search_after_scroll.ts diff --git a/x-pack/plugins/lists/server/services/utils/get_search_after_with_tie_breaker.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/get_search_after_with_tie_breaker.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/get_search_after_with_tie_breaker.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/get_search_after_with_tie_breaker.ts diff --git a/x-pack/plugins/lists/server/services/utils/get_sort_with_tie_breaker.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/get_sort_with_tie_breaker.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/get_sort_with_tie_breaker.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/get_sort_with_tie_breaker.ts diff --git a/x-pack/plugins/lists/server/services/utils/get_source_with_tie_breaker.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/get_source_with_tie_breaker.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/get_source_with_tie_breaker.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/get_source_with_tie_breaker.ts diff --git a/x-pack/plugins/lists/server/services/utils/index.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/index.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/index.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/index.ts diff --git a/x-pack/plugins/lists/server/services/utils/scroll_to_start_page.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/scroll_to_start_page.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/scroll_to_start_page.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/scroll_to_start_page.ts diff --git a/x-pack/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.test.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.test.ts diff --git a/x-pack/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.ts diff --git a/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/transform_elastic_to_list.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/transform_elastic_to_list.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/transform_elastic_to_list.ts diff --git a/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.test.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/transform_elastic_to_list_item.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/transform_elastic_to_list_item.test.ts diff --git a/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts diff --git a/x-pack/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.test.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.test.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.test.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.test.ts diff --git a/x-pack/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.ts diff --git a/x-pack/plugins/lists/server/services/utils/wait_until_document_indexed.ts b/x-pack/solutions/security/plugins/lists/server/services/utils/wait_until_document_indexed.ts similarity index 100% rename from x-pack/plugins/lists/server/services/utils/wait_until_document_indexed.ts rename to x-pack/solutions/security/plugins/lists/server/services/utils/wait_until_document_indexed.ts diff --git a/x-pack/plugins/lists/server/types.ts b/x-pack/solutions/security/plugins/lists/server/types.ts similarity index 100% rename from x-pack/plugins/lists/server/types.ts rename to x-pack/solutions/security/plugins/lists/server/types.ts diff --git a/x-pack/solutions/security/plugins/lists/tsconfig.json b/x-pack/solutions/security/plugins/lists/tsconfig.json new file mode 100644 index 000000000000..c0f751d665ee --- /dev/null +++ b/x-pack/solutions/security/plugins/lists/tsconfig.json @@ -0,0 +1,50 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 + "server/**/*.json" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/spaces-plugin", + "@kbn/security-plugin", + "@kbn/unified-search-plugin", + "@kbn/securitysolution-io-ts-list-types", + "@kbn/securitysolution-list-constants", + "@kbn/securitysolution-list-hooks", + "@kbn/securitysolution-list-api", + "@kbn/securitysolution-lists-common", + "@kbn/securitysolution-exceptions-common", + "@kbn/securitysolution-endpoint-exceptions-common", + "@kbn/kibana-react-plugin", + "@kbn/i18n", + "@kbn/data-plugin", + "@kbn/securitysolution-list-utils", + "@kbn/securitysolution-utils", + "@kbn/es-query", + "@kbn/i18n-react", + "@kbn/securitysolution-autocomplete", + "@kbn/config-schema", + "@kbn/securitysolution-io-ts-utils", + "@kbn/core-http-server", + "@kbn/securitysolution-es-utils", + "@kbn/securitysolution-io-ts-types", + "@kbn/std", + "@kbn/utils", + "@kbn/logging-mocks", + "@kbn/utility-types", + "@kbn/core-elasticsearch-client-server-mocks", + "@kbn/core-saved-objects-server", + "@kbn/zod-helpers", + "@kbn/core-security-server", + "@kbn/core-http-server-mocks", + "@kbn/core-http-server-utils" + ], + "exclude": ["target/**/*"] +} diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts index 56b1fafdb5a7..3a765e0c8313 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts @@ -17,6 +17,7 @@ import { GetProcessesRouteRequestSchema } from '../response_actions/running_proc import { KillProcessRouteRequestSchema } from '../response_actions/kill_process'; import { SuspendProcessRouteRequestSchema } from '../response_actions/suspend_process'; import { UploadActionRequestSchema } from '../response_actions/upload'; +import { RunScriptActionRequestSchema } from '../response_actions/run_script'; export const ResponseActionBodySchema = schema.oneOf([ IsolateRouteRequestSchema.body, @@ -28,6 +29,7 @@ export const ResponseActionBodySchema = schema.oneOf([ ExecuteActionRequestSchema.body, UploadActionRequestSchema.body, ScanActionRequestSchema.body, + RunScriptActionRequestSchema.body, ]); export type ResponseActionsRequestBody = TypeOf<typeof ResponseActionBodySchema>; diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/run_script/run_script.ts b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/run_script/run_script.ts index dfa88941b34e..95c035e86688 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/run_script/run_script.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/run_script/run_script.ts @@ -26,27 +26,27 @@ export const RunScriptActionRequestSchema = { /** * The script to run */ - Raw: schema.maybe(NonEmptyString), + raw: schema.maybe(NonEmptyString), /** * The path to the script on the host to run */ - HostPath: schema.maybe(NonEmptyString), + hostPath: schema.maybe(NonEmptyString), /** * The path to the script in the cloud to run */ - CloudFile: schema.maybe(NonEmptyString), + cloudFile: schema.maybe(NonEmptyString), /** * The command line to run */ - CommandLine: schema.maybe(NonEmptyString), + commandLine: schema.maybe(NonEmptyString), /** * The max timeout value before the command is killed. Number represents milliseconds */ - Timeout: schema.maybe(schema.number({ min: 1 })), + timeout: schema.maybe(schema.number({ min: 1 })), }, { validate: (params) => { - if (!params.Raw && !params.HostPath && !params.CloudFile) { + if (!params.raw && !params.hostPath && !params.cloudFile) { return 'At least one of Raw, HostPath, or CloudFile must be provided'; } }, diff --git a/x-pack/solutions/security/plugins/security_solution/common/endpoint/service/response_actions/type_guards.ts b/x-pack/solutions/security/plugins/security_solution/common/endpoint/service/response_actions/type_guards.ts index 707be0a4d1e6..ece0a9501e3f 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/endpoint/service/response_actions/type_guards.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/endpoint/service/response_actions/type_guards.ts @@ -15,6 +15,8 @@ import type { ResponseActionUploadOutputContent, ResponseActionUploadParameters, GetProcessesActionOutputContent, + ResponseActionRunScriptOutputContent, + ResponseActionRunScriptParameters, } from '../../types'; import { RESPONSE_ACTION_AGENT_TYPE, RESPONSE_ACTION_TYPE } from './constants'; @@ -47,6 +49,15 @@ export const isProcessesAction = ( return action.command === 'running-processes'; }; +export const isRunScriptAction = ( + action: MaybeImmutable<SomeObjectWithCommand> +): action is ActionDetails< + ResponseActionRunScriptOutputContent, + ResponseActionRunScriptParameters +> => { + return action.command === 'runscript'; +}; + // type guards to ensure only the matching string values are attached to the types filter type export const isAgentType = (type: string): type is (typeof RESPONSE_ACTION_AGENT_TYPE)[number] => RESPONSE_ACTION_AGENT_TYPE.includes(type as (typeof RESPONSE_ACTION_AGENT_TYPE)[number]); diff --git a/x-pack/solutions/security/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/solutions/security/plugins/security_solution/common/endpoint/types/actions.ts index 131a8d0c6df5..e3c47102ce71 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/endpoint/types/actions.ts @@ -97,7 +97,8 @@ export interface ResponseActionScanOutputContent { } export interface ResponseActionRunScriptOutputContent { - output: string; + stdout: string; + stderr: string; code: string; } diff --git a/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts b/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts index 428a48cf4b7b..56f4657500c4 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts @@ -109,11 +109,6 @@ export const allowedExperimentalValues = Object.freeze({ */ securitySolutionNotesDisabled: false, - /** - * Disables entity and alert previews - */ - entityAlertPreviewDisabled: false, - /** * Enables the Assistant Model Evaluation advanced setting and API endpoint, introduced in `8.11.0`. */ @@ -266,8 +261,12 @@ export const allowedExperimentalValues = Object.freeze({ /** * Enables CrowdStrike's RunScript RTR command */ - crowdstrikeRunScriptEnabled: false, + + /** + * Enables the Asset Inventory feature + */ + assetInventoryStoreEnabled: false, }); type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>; diff --git a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/constants.ts b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/constants.ts index 531669608ed8..88ed777c21d6 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/constants.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/constants.ts @@ -44,7 +44,7 @@ export enum SiemMigrationStatus { FAILED = 'failed', } -export enum SiemMigrationRuleTranslationResult { +export enum RuleTranslationResult { FULL = 'full', PARTIAL = 'partial', UNTRANSLATABLE = 'untranslatable', @@ -60,3 +60,5 @@ export const DEFAULT_TRANSLATION_FIELDS = { to: 'now', interval: '5m', } as const; + +export const EMPTY_RESOURCE_PLACEHOLDER = '<empty>'; diff --git a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts index d9c33ebbdf70..27db56aedb45 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts @@ -285,21 +285,47 @@ export const RuleMigrationTranslationStats = z.object({ */ rules: z.object({ /** - * The total number of rules to migrate. + * The total number of rules in the migration. */ total: z.number().int(), /** - * The number of rules that matched Elastic prebuilt rules. - */ - prebuilt: z.number().int(), - /** - * The number of rules that did not match Elastic prebuilt rules and will be installed as custom rules. + * The number of rules that have been successfully translated. */ - custom: z.number().int(), + success: z.object({ + /** + * The total number of rules that have been successfully translated. + */ + total: z.number().int(), + /** + * The translation results + */ + result: z.object({ + /** + * The number of rules that have been fully translated. + */ + full: z.number().int(), + /** + * The number of rules that have been partially translated. + */ + partial: z.number().int(), + /** + * The number of rules that could not be translated. + */ + untranslatable: z.number().int(), + }), + /** + * The number of rules that have been successfully translated and can be installed. + */ + installable: z.number().int(), + /** + * The number of rules that have been successfully translated and matched Elastic prebuilt rules. + */ + prebuilt: z.number().int(), + }), /** - * The number of rules that can be installed. + * The number of rules that have failed translation. */ - installable: z.number().int(), + failed: z.number().int(), }), }); diff --git a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml index 6fce9f0d51f5..f3a85b3e3744 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml @@ -234,23 +234,50 @@ components: description: The rules migration translation stats. required: - total - - prebuilt - - custom - - installable + - success + - failed properties: total: type: integer - description: The total number of rules to migrate. - prebuilt: - type: integer - description: The number of rules that matched Elastic prebuilt rules. - custom: - type: integer - description: The number of rules that did not match Elastic prebuilt rules and will be installed as custom rules. - installable: + description: The total number of rules in the migration. + success: + type: object + description: The number of rules that have been successfully translated. + required: + - total + - result + - installable + - prebuilt + properties: + total: + type: integer + description: The total number of rules that have been successfully translated. + result: + type: object + description: The translation results + required: + - full + - partial + - untranslatable + properties: + full: + type: integer + description: The number of rules that have been fully translated. + partial: + type: integer + description: The number of rules that have been partially translated. + untranslatable: + type: integer + description: The number of rules that could not be translated. + installable: + type: integer + description: The number of rules that have been successfully translated and can be installed. + prebuilt: + type: integer + description: The number of rules that have been successfully translated and matched Elastic prebuilt rules. + failed: type: integer - description: The number of rules that can be installed. - + description: The number of rules that have failed translation. RuleMigrationTranslationResult: type: string description: The rule translation result. diff --git a/x-pack/solutions/security/plugins/security_solution/docs/openapi/README.md b/x-pack/solutions/security/plugins/security_solution/docs/openapi/README.md index 4c3b55c8ab23..3cbe1c1f4233 100644 --- a/x-pack/solutions/security/plugins/security_solution/docs/openapi/README.md +++ b/x-pack/solutions/security/plugins/security_solution/docs/openapi/README.md @@ -32,8 +32,8 @@ Security Solution has multiple API domains scattered across Kibana. Currently th - Security Endpoint Exceptions - - Bundling script: `packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_bundle.js` - - Bundles location: `packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/{ess|serverless}` + - Bundling script: `x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/scripts/openapi_bundle.js` + - Bundles location: `x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common/docs/openapi/{ess|serverless}` - Security Endpoint Management @@ -47,13 +47,13 @@ Security Solution has multiple API domains scattered across Kibana. Currently th - Security Security Exceptions - - Bundling script: `packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle.js` - - Bundles location: `packages/kbn-securitysolution-exceptions-common/docs/openapi/{ess|serverless}` + - Bundling script: `x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/scripts/openapi_bundle.js` + - Bundles location: `x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common/docs/openapi/{ess|serverless}` - Security Lists - - Bundling script: `packages/kbn-securitysolution-lists-common/scripts/openapi_bundle.js` - - Bundles location: `packages/kbn-securitysolution-lists-common/docs/openapi/{ess|serverless}` + - Bundling script: `x-pack/solutions/security/packages/kbn-securitysolution-lists-common/scripts/openapi_bundle.js` + - Bundles location: `x-pack/solutions/security/packages/kbn-securitysolution-lists-common/docs/openapi/{ess|serverless}` - Security Osquery diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/panel_text/index.ts b/x-pack/solutions/security/plugins/security_solution/public/common/components/panel_text/index.ts new file mode 100644 index 000000000000..e11d5f18e46b --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/panel_text/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 { PanelText, type PanelTextProps } from './panel_text'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/panel_text/panel_text.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/panel_text/panel_text.tsx new file mode 100644 index 000000000000..5c1fc6746bcd --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/panel_text/panel_text.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { type PropsWithChildren } from 'react'; +import { css, type CSSInterpolation } from '@emotion/css'; +import { EuiText, useEuiTheme, COLOR_MODES_STANDARD, type EuiTextProps } from '@elastic/eui'; + +export interface PanelTextProps extends PropsWithChildren<EuiTextProps> { + subdued?: true; + semiBold?: true; +} +export const PanelText = React.memo<PanelTextProps>(({ children, subdued, semiBold, ...props }) => { + const { euiTheme, colorMode } = useEuiTheme(); + const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; + + let color; + if (subdued && !isDarkMode) { + color = 'subdued'; + } + + const style: CSSInterpolation = {}; + if (semiBold) { + style.fontWeight = euiTheme.font.weight.semiBold; + } + + return ( + <EuiText {...props} color={color} className={css(style)}> + {children} + </EuiText> + ); +}); +PanelText.displayName = 'PanelText'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts index 14ab3fc7ca15..3c1aececa866 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts @@ -7,6 +7,8 @@ import type { UseQueryOptions } from '@tanstack/react-query'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback } from 'react'; +import moment from 'moment'; +import { i18n } from '@kbn/i18n'; import type { RiskEngineStatusResponse } from '../../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; import { RiskEngineStatusEnum } from '../../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; import { useEntityAnalyticsRoutes } from '../api'; @@ -38,6 +40,22 @@ export const useIsNewRiskScoreModuleInstalled = (): RiskScoreModuleStatus => { return { isLoading: false, installed: !!riskEngineStatus?.isNewRiskScoreModuleInstalled }; }; +export const useRiskEngineCountdownTime = ( + riskEngineStatus: RiskEngineStatus | undefined +): string => { + const { status, runAt } = riskEngineStatus?.risk_engine_task_status || {}; + const isRunning = status === 'running' || (!!runAt && new Date(runAt) < new Date()); + + return isRunning + ? i18n.translate( + 'xpack.securitySolution.entityAnalytics.assetCriticalityResultStep.riskEngine.nowRunningMessage', + { + defaultMessage: 'Now running', + } + ) + : moment(runAt).fromNow(true); +}; + export interface RiskEngineStatus extends RiskEngineStatusResponse { isUpdateAvailable: boolean; isNewRiskScoreModuleInstalled: boolean; diff --git a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.tsx b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.tsx index 432e03c231a4..187dc67ff1be 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.tsx @@ -12,15 +12,17 @@ import { EuiText, EuiFlexItem, } from '@elastic/eui'; -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { RiskEngineStatusEnum } from '../../../../../common/api/entity_analytics'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; -import { formatTimeFromNow } from '../helpers'; import { useScheduleNowRiskEngineMutation } from '../../../api/hooks/use_schedule_now_risk_engine_mutation'; -import { useRiskEngineStatus } from '../../../api/hooks/use_risk_engine_status'; +import { + useRiskEngineStatus, + useRiskEngineCountdownTime, +} from '../../../api/hooks/use_risk_engine_status'; const TEN_SECONDS = 10000; @@ -29,7 +31,7 @@ export const ScheduleRiskEngineCallout: React.FC = () => { refetchInterval: TEN_SECONDS, structuralSharing: false, // Force the component to rerender after every Risk Engine Status API call }); - + const isRunning = riskEngineStatus?.risk_engine_task_status?.status === 'running'; const { addSuccess, addError } = useAppToasts(); const { isLoading: isLoadingRiskEngineSchedule, mutate: scheduleRiskEngineMutation } = useScheduleNowRiskEngineMutation({ @@ -53,25 +55,7 @@ export const ScheduleRiskEngineCallout: React.FC = () => { }), }); - const { status, runAt } = riskEngineStatus?.risk_engine_task_status || {}; - - const isRunning = useMemo( - () => status === 'running' || (!!runAt && new Date(runAt) < new Date()), - [runAt, status] - ); - - const countDownText = useMemo( - () => - isRunning - ? i18n.translate( - 'xpack.securitySolution.entityAnalytics.assetCriticalityResultStep.riskEngine.nowRunningMessage', - { - defaultMessage: 'Now running', - } - ) - : formatTimeFromNow(riskEngineStatus?.risk_engine_task_status?.runAt), - [isRunning, riskEngineStatus?.risk_engine_task_status?.runAt] - ); + const countDownText = useRiskEngineCountdownTime(riskEngineStatus); const scheduleRiskEngine = useCallback(() => { scheduleRiskEngineMutation(); diff --git a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx index 6995656db31a..7ff2a6feb958 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx @@ -49,12 +49,6 @@ const riskScore = { }, }; -const mockUseIsExperimentalFeatureEnabled = jest.fn().mockReturnValue(false); - -jest.mock('../../../../../common/hooks/use_experimental_features', () => ({ - useIsExperimentalFeatureEnabled: () => mockUseIsExperimentalFeatureEnabled(), -})); - const riskScoreWithAssetCriticalityContribution = (contribution: number) => { const score = JSON.parse(JSON.stringify(riskScore)); score.user.risk.category_2_score = contribution; @@ -129,8 +123,7 @@ describe('RiskInputsTab', () => { expect(queryByTestId('risk-input-contexts-title')).toBeInTheDocument(); }); - it('it renders alert preview button when feature flag is enable', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('it renders alert preview button', () => { mockUseRiskScore.mockReturnValue({ loading: false, error: false, @@ -151,28 +144,6 @@ describe('RiskInputsTab', () => { expect(getByTestId(EXPAND_ALERT_TEST_ID)).toBeInTheDocument(); }); - it('it does not render alert preview button when feature flag is disable', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); - mockUseRiskScore.mockReturnValue({ - loading: false, - error: false, - data: [riskScore], - }); - mockUseRiskContributingAlerts.mockReturnValue({ - loading: false, - error: false, - data: [alertInputDataMock], - }); - - const { queryByTestId } = render( - <TestProviders> - <RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" scopeId={'scopeId'} /> - </TestProviders> - ); - - expect(queryByTestId(EXPAND_ALERT_TEST_ID)).not.toBeInTheDocument(); - }); - it('Displays 0.00 for the asset criticality contribution if the contribution value is less than -0.01', () => { mockUseUiSetting.mockReturnValue([true]); diff --git a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx index 78010434ee59..72a6af0fe768 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx @@ -14,7 +14,6 @@ import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; import { get } from 'lodash/fp'; import { AlertPreviewButton } from '../../../../../flyout/shared/components/alert_preview_button'; -import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; import { useGlobalTime } from '../../../../../common/containers/use_global_time'; import { useQueryInspector } from '../../../../../common/components/page/manage_query'; import { formatRiskScore } from '../../../../common'; @@ -98,26 +97,20 @@ export const RiskInputsTab = ({ entityType, entityName, scopeId }: RiskInputsTab }), [] ); - const isPreviewEnabled = !useIsExperimentalFeatureEnabled('entityAlertPreviewDisabled'); const inputColumns: Array<EuiBasicTableColumn<InputAlert>> = useMemo( () => [ - ...(isPreviewEnabled - ? [ - { - render: (data: InputAlert) => ( - <AlertPreviewButton - id={data._id} - indexName={data.input.index} - scopeId={scopeId} - data-test-subj={EXPAND_ALERT_TEST_ID} - /> - ), - width: '5%', - }, - ] - : []), - + { + render: (data: InputAlert) => ( + <AlertPreviewButton + id={data._id} + indexName={data.input.index} + scopeId={scopeId} + data-test-subj={EXPAND_ALERT_TEST_ID} + /> + ), + width: '5%', + }, { name: ( <FormattedMessage @@ -172,7 +165,7 @@ export const RiskInputsTab = ({ entityType, entityName, scopeId }: RiskInputsTab render: formatContribution, }, ], - [isPreviewEnabled, scopeId] + [scopeId] ); if (riskScoreError) { diff --git a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx index 547ee0235e77..1c245dc779d0 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx @@ -289,7 +289,7 @@ export const RiskScoreEnableSection: React.FC<{ <RiskEngineStatusRow currentRiskEngineStatus={currentRiskEngineStatus} onSwitchClick={onSwitchClick} - isLoading={isLoading} + isLoading={isLoading && !currentRiskEngineStatus} privileges={privileges} /> )} diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.test.tsx index 6fd56c3aa519..7e5088b0195c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.test.tsx @@ -11,7 +11,6 @@ import { TestProviders } from '../../../../common/mock'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { CorrelationsDetailsAlertsTable } from './correlations_details_alerts_table'; import { usePaginatedAlerts } from '../hooks/use_paginated_alerts'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { mockFlyoutApi } from '../../shared/mocks/mock_flyout_context'; import { mockContextValue } from '../../shared/mocks/mock_context'; import { DocumentDetailsPreviewPanelKey } from '../../shared/constants/panel_keys'; @@ -20,8 +19,6 @@ import { DocumentDetailsContext } from '../../shared/context'; import { RulePreviewPanelKey, RULE_PREVIEW_BANNER } from '../../../rule_details/right'; jest.mock('../hooks/use_paginated_alerts'); -jest.mock('../../../../common/hooks/use_experimental_features'); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; jest.mock('@kbn/expandable-flyout'); @@ -47,7 +44,6 @@ const renderCorrelationsTable = (panelContext: DocumentDetailsContext) => describe('CorrelationsDetailsAlertsTable', () => { beforeEach(() => { jest.mocked(useExpandableFlyoutApi).mockReturnValue(mockFlyoutApi); - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); jest.mocked(usePaginatedAlerts).mockReturnValue({ setPagination: jest.fn(), setSorting: jest.fn(), @@ -88,16 +84,16 @@ describe('CorrelationsDetailsAlertsTable', () => { }); it('renders EuiBasicTable with correct props', () => { - const { getByTestId, queryByTestId, queryAllByRole } = + const { getByTestId, getAllByTestId, queryAllByRole } = renderCorrelationsTable(mockContextValue); expect(getByTestId(`${TEST_ID}InvestigateInTimeline`)).toBeInTheDocument(); expect(getByTestId(`${TEST_ID}Table`)).toBeInTheDocument(); - expect(queryByTestId(`${TEST_ID}AlertPreviewButton`)).not.toBeInTheDocument(); + expect(getAllByTestId(`${TEST_ID}AlertPreviewButton`)).toHaveLength(2); expect(jest.mocked(usePaginatedAlerts)).toHaveBeenCalled(); - expect(queryAllByRole('columnheader').length).toBe(4); + expect(queryAllByRole('columnheader').length).toBe(5); expect(queryAllByRole('row').length).toBe(3); // 1 header row and 2 data rows expect(queryAllByRole('row')[1].textContent).toContain('Jan 1, 2022 @ 00:00:00.000'); expect(queryAllByRole('row')[1].textContent).toContain('Reason1'); @@ -105,8 +101,7 @@ describe('CorrelationsDetailsAlertsTable', () => { expect(queryAllByRole('row')[1].textContent).toContain('Severity1'); }); - it('renders open preview button when feature flag is on', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('renders open preview button', () => { const { getByTestId, getAllByTestId } = renderCorrelationsTable({ ...mockContextValue, isPreviewMode: true, @@ -128,8 +123,7 @@ describe('CorrelationsDetailsAlertsTable', () => { }); }); - it('opens rule preview when feature flag is on and isPreview is false', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('opens rule preview when isPreview is false', () => { const { getAllByTestId } = renderCorrelationsTable(mockContextValue); expect(getAllByTestId(`${TEST_ID}RulePreview`).length).toBe(2); @@ -145,8 +139,7 @@ describe('CorrelationsDetailsAlertsTable', () => { }); }); - it('does not render preview link when feature flag is on and isPreview is true', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('does not render preview link when isPreview is true', () => { const { queryByTestId } = renderCorrelationsTable({ ...mockContextValue, isPreview: true }); expect(queryByTestId(`${TEST_ID}RulePreview`)).not.toBeInTheDocument(); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx index d8497ca984ea..4d757b46edb2 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx @@ -14,7 +14,6 @@ import { isRight } from 'fp-ts/lib/Either'; import { ALERT_REASON, ALERT_RULE_NAME } from '@kbn/rule-data-utils'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { CellTooltipWrapper } from '../../shared/components/cell_tooltip_wrapper'; import type { DataProvider } from '../../../../../common/types'; import { SeverityBadge } from '../../../../common/components/severity_badge'; @@ -82,8 +81,6 @@ export const CorrelationsDetailsAlertsTable: FC<CorrelationsDetailsAlertsTablePr sorting, error, } = usePaginatedAlerts(alertIds || []); - const isPreviewEnabled = !useIsExperimentalFeatureEnabled('entityAlertPreviewDisabled'); - const { isPreview } = useDocumentDetailsContext(); const onTableChange = useCallback( @@ -129,21 +126,17 @@ export const CorrelationsDetailsAlertsTable: FC<CorrelationsDetailsAlertsTablePr const columns = useMemo( () => [ - ...(isPreviewEnabled - ? [ - { - render: (row: Record<string, unknown>) => ( - <AlertPreviewButton - id={row.id as string} - indexName={row.index as string} - data-test-subj={`${dataTestSubj}AlertPreviewButton`} - scopeId={scopeId} - /> - ), - width: '5%', - }, - ] - : []), + { + render: (row: Record<string, unknown>) => ( + <AlertPreviewButton + id={row.id as string} + indexName={row.index as string} + data-test-subj={`${dataTestSubj}AlertPreviewButton`} + scopeId={scopeId} + /> + ), + width: '5%', + }, { field: '@timestamp', name: ( @@ -176,20 +169,16 @@ export const CorrelationsDetailsAlertsTable: FC<CorrelationsDetailsAlertsTablePr const ruleId = row['kibana.alert.rule.uuid'] as string; return ( <CellTooltipWrapper tooltip={ruleName}> - {isPreviewEnabled ? ( - <PreviewLink - field={ALERT_RULE_NAME} - value={ruleName} - scopeId={scopeId} - ruleId={ruleId} - isPreview={isPreview} - data-test-subj={`${dataTestSubj}RulePreview`} - > - <span>{ruleName}</span> - </PreviewLink> - ) : ( + <PreviewLink + field={ALERT_RULE_NAME} + value={ruleName} + scopeId={scopeId} + ruleId={ruleId} + isPreview={isPreview} + data-test-subj={`${dataTestSubj}RulePreview`} + > <span>{ruleName}</span> - )} + </PreviewLink> </CellTooltipWrapper> ); }, @@ -229,7 +218,7 @@ export const CorrelationsDetailsAlertsTable: FC<CorrelationsDetailsAlertsTablePr }, }, ], - [isPreviewEnabled, scopeId, dataTestSubj, isPreview] + [scopeId, dataTestSubj, isPreview] ); return ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/graph_visualization.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/graph_visualization.tsx index 96374b81e18d..3e7ab71b7f62 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/graph_visualization.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/graph_visualization.tsx @@ -29,12 +29,20 @@ export const GraphVisualization: React.FC = memo(() => { const dataView = useGetScopedSourcererDataView({ sourcererScope: SourcererScopeName.default, }); - const { getFieldsData, dataAsNestedObject } = useDocumentDetailsContext(); - const { eventIds, timestamp } = useGraphPreview({ + const { getFieldsData, dataAsNestedObject, dataFormattedForFieldBrowser } = + useDocumentDetailsContext(); + const { + eventIds, + timestamp = new Date().toISOString(), + isAlert, + } = useGraphPreview({ getFieldsData, ecsData: dataAsNestedObject, + dataFormattedForFieldBrowser, }); + const originEventIds = eventIds.map((id) => ({ id, isAlert })); + return ( <div data-test-subj={GRAPH_VISUALIZATION_TEST_ID} @@ -46,7 +54,16 @@ export const GraphVisualization: React.FC = memo(() => { > {dataView && ( <React.Suspense fallback={<EuiLoadingSpinner />}> - <GraphInvestigationLazy dataView={dataView} eventIds={eventIds} timestamp={timestamp} /> + <GraphInvestigationLazy + initialState={{ + dataView, + originEventIds, + timeRange: { + from: `${timestamp}||-30m`, + to: `${timestamp}||+30m`, + }, + }} + /> </React.Suspense> )} </div> diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx index e08a79466522..86d04a9c94a7 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx @@ -17,7 +17,6 @@ import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml import { mockAnomalies } from '../../../../common/components/ml/mock'; import { useHostDetails } from '../../../../explore/hosts/containers/hosts/details'; import { useHostRelatedUsers } from '../../../../common/containers/related_entities/related_users'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { RiskSeverity } from '../../../../../common/search_strategy'; import { HOST_DETAILS_TEST_ID, @@ -46,9 +45,6 @@ jest.mock('@kbn/expandable-flyout'); jest.mock('@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'); jest.mock('@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'); -jest.mock('../../../../common/hooks/use_experimental_features'); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; - jest.mock('react-router-dom', () => { const actual = jest.requireActual('react-router-dom'); return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; @@ -180,20 +176,18 @@ describe('<HostDetails />', () => { mockUseHostDetails.mockReturnValue(mockHostDetailsResponse); mockUseRiskScore.mockReturnValue(mockRiskScoreResponse); mockUseHostsRelatedUsers.mockReturnValue(mockRelatedUsersResponse); - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); (useMisconfigurationPreview as jest.Mock).mockReturnValue({}); (useVulnerabilitiesPreview as jest.Mock).mockReturnValue({}); (useAlertsByStatus as jest.Mock).mockReturnValue({ isLoading: false, items: {} }); }); it('should render host details correctly', () => { - const { getByTestId, queryByTestId } = renderHostDetails(mockContextValue); + const { getByTestId } = renderHostDetails(mockContextValue); expect(getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(HOST_DETAILS_TEST_ID))).toBeInTheDocument(); - expect(queryByTestId(HOST_DETAILS_LINK_TEST_ID)).not.toBeInTheDocument(); + expect(getByTestId(HOST_DETAILS_LINK_TEST_ID)).toBeInTheDocument(); }); - it('should render host name as clicable link when preview is not disabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('should render host name as clicable link', () => { const { getByTestId } = renderHostDetails(mockContextValue); expect(getByTestId(HOST_DETAILS_LINK_TEST_ID)).toBeInTheDocument(); @@ -242,7 +236,7 @@ describe('<HostDetails />', () => { describe('Related users', () => { it('should render the related user table with correct dates and indices', () => { - const { getByTestId, queryByTestId } = renderHostDetails(mockContextValue); + const { getByTestId } = renderHostDetails(mockContextValue); expect(mockUseHostsRelatedUsers).toBeCalledWith({ from: timestamp, hostName: 'test host', @@ -250,7 +244,7 @@ describe('<HostDetails />', () => { skip: false, }); expect(getByTestId(HOST_DETAILS_RELATED_USERS_TABLE_TEST_ID)).toBeInTheDocument(); - expect(queryByTestId(HOST_DETAILS_RELATED_USERS_LINK_TEST_ID)).not.toBeInTheDocument(); + expect(getByTestId(HOST_DETAILS_RELATED_USERS_LINK_TEST_ID)).toBeInTheDocument(); }); it('should render user risk score column when license and capabilities are valid', () => { @@ -296,8 +290,7 @@ describe('<HostDetails />', () => { ); }); - it('should render user name as clicable link when preview is not disabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('should render user name as clicable link', () => { const { getAllByTestId } = renderHostDetails(mockContextValue); expect(getAllByTestId(HOST_DETAILS_RELATED_USERS_LINK_TEST_ID).length).toBe(1); diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx index e3fd2fef8bc6..9555860a329b 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx @@ -25,14 +25,12 @@ import type { EuiBasicTableColumn } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import type { RelatedUser } from '../../../../../common/search_strategy/security_solution/related_entities/related_users'; import type { RiskSeverity } from '../../../../../common/search_strategy'; import { HostOverview } from '../../../../overview/components/host_overview'; import { AnomalyTableProvider } from '../../../../common/components/ml/anomaly/anomaly_table_provider'; import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect'; -import { NetworkDetailsLink } from '../../../../common/components/links'; import { RiskScoreEntity } from '../../../../../common/search_strategy'; import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common'; import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/default_renderer'; @@ -110,7 +108,6 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s const isEntityAnalyticsAuthorized = isPlatinumOrTrialLicense && hasEntityAnalyticsCapability; const { openPreviewPanel } = useExpandableFlyoutApi(); - const isPreviewEnabled = !useIsExperimentalFeatureEnabled('entityAlertPreviewDisabled'); const narrowDateRange = useCallback<NarrowDateRange>( (score, interval) => { @@ -176,16 +173,12 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s render: (user: string) => ( <EuiText grow={false} size="xs"> <CellActions field={USER_NAME_FIELD_NAME} value={user}> - {isPreviewEnabled ? ( - <PreviewLink - field={USER_NAME_FIELD_NAME} - value={user} - scopeId={scopeId} - data-test-subj={HOST_DETAILS_RELATED_USERS_LINK_TEST_ID} - /> - ) : ( - <>{user}</> - )} + <PreviewLink + field={USER_NAME_FIELD_NAME} + value={user} + scopeId={scopeId} + data-test-subj={HOST_DETAILS_RELATED_USERS_LINK_TEST_ID} + /> </CellActions> </EuiText> ), @@ -208,15 +201,13 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s render={(ip) => ip == null ? ( getEmptyTagValue() - ) : isPreviewEnabled ? ( + ) : ( <PreviewLink field={HOST_IP_FIELD_NAME} value={ip} scopeId={scopeId} data-test-subj={HOST_DETAILS_RELATED_USERS_IP_LINK_TEST_ID} /> - ) : ( - <NetworkDetailsLink ip={ip} /> ) } scopeId={scopeId} @@ -242,7 +233,7 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s ] : []), ], - [isEntityAnalyticsAuthorized, scopeId, isPreviewEnabled] + [isEntityAnalyticsAuthorized, scopeId] ); const relatedUsersCount = useMemo( @@ -273,19 +264,14 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s }; const hostLink = useMemo( - () => - isPreviewEnabled - ? { - callback: openHostPreview, - tooltip: i18n.translate( - 'xpack.securitySolution.flyout.left.insights.entities.host.hostPreviewTitle', - { - defaultMessage: 'Preview host', - } - ), - } - : undefined, - [isPreviewEnabled, openHostPreview] + () => ({ + callback: openHostPreview, + tooltip: i18n.translate( + 'xpack.securitySolution.flyout.left.insights.entities.host.hostPreviewTitle', + { defaultMessage: 'Preview host' } + ), + }), + [openHostPreview] ); return ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/prevalence_details.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/prevalence_details.test.tsx index e8b1b71e9cd0..e0bef746b213 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/prevalence_details.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/prevalence_details.test.tsx @@ -24,7 +24,6 @@ import { import { usePrevalence } from '../../shared/hooks/use_prevalence'; import { TestProviders } from '../../../../common/mock'; import { licenseService } from '../../../../common/hooks/use_license'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { mockFlyoutApi } from '../../shared/mocks/mock_flyout_context'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { HostPreviewPanelKey } from '../../../entity_details/host_right'; @@ -46,9 +45,6 @@ jest.mock('../../../../common/lib/kibana', () => { }; }); -jest.mock('../../../../common/hooks/use_experimental_features'); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; - jest.mock('../../shared/hooks/use_prevalence'); const mockDispatch = jest.fn(); @@ -138,7 +134,6 @@ describe('PrevalenceDetails', () => { jest.clearAllMocks(); licenseServiceMock.isPlatinumPlus.mockReturnValue(true); jest.mocked(useExpandableFlyoutApi).mockReturnValue(mockFlyoutApi); - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); }); it('should render the table with all data if license is platinum', () => { @@ -162,13 +157,10 @@ describe('PrevalenceDetails', () => { ).toBeGreaterThan(1); expect(queryByTestId(PREVALENCE_DETAILS_UPSELL_TEST_ID)).not.toBeInTheDocument(); expect(queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument(); - expect( - queryByTestId(PREVALENCE_DETAILS_TABLE_PREVIEW_LINK_CELL_TEST_ID) - ).not.toBeInTheDocument(); + expect(getAllByTestId(PREVALENCE_DETAILS_TABLE_PREVIEW_LINK_CELL_TEST_ID)).toHaveLength(2); }); - it('should render host and user name as clickable link if preview is enabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('should render host and user name as clickable link', () => { (usePrevalence as jest.Mock).mockReturnValue(mockPrevelanceReturnValue); const { getAllByTestId } = renderPrevalenceDetails(); diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/prevalence_details.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/prevalence_details.tsx index 6491bc9ad274..77ab6eb66df3 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/prevalence_details.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/prevalence_details.tsx @@ -22,7 +22,6 @@ import { useEuiTheme, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { FormattedCount } from '../../../../common/components/formatted_number'; import { useLicense } from '../../../../common/hooks/use_license'; import { InvestigateInTimelineButton } from '../../../../common/components/event_details/investigate_in_timeline_button'; @@ -81,10 +80,6 @@ interface PrevalenceDetailsRow extends PrevalenceData { * License to drive the rendering of the last 2 prevalence columns */ isPlatinumPlus: boolean; - /** - * If enabled, clicking host or user should open an entity preview - */ - isPreviewEnabled: boolean; /** * Scope id to pass to the preview link */ @@ -115,7 +110,7 @@ const columns: Array<EuiBasicTableColumn<PrevalenceDetailsRow>> = [ render: (data: PrevalenceDetailsRow) => ( <EuiFlexGroup direction="column" gutterSize="none"> {data.values.map((value) => { - if (data.isPreviewEnabled && hasPreview(data.field)) { + if (hasPreview(data.field)) { return ( <EuiFlexItem key={value}> <CellActions field={data.field} value={value}> @@ -331,7 +326,6 @@ export const PrevalenceDetails: React.FC = () => { useDocumentDetailsContext(); const isPlatinumPlus = useLicense().isPlatinumPlus(); - const isPreviewEnabled = !useIsExperimentalFeatureEnabled('entityAlertPreviewDisabled'); // these two are used by the usePrevalence hook to fetch the data const [start, setStart] = useState(DEFAULT_FROM); @@ -382,10 +376,9 @@ export const PrevalenceDetails: React.FC = () => { from: absoluteStart, to: absoluteEnd, isPlatinumPlus, - isPreviewEnabled, scopeId, })), - [data, absoluteStart, absoluteEnd, isPlatinumPlus, isPreviewEnabled, scopeId] + [data, absoluteStart, absoluteEnd, isPlatinumPlus, scopeId] ); const upsell = ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx index 5bfb8a7df50d..993e9101ba0f 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx @@ -16,7 +16,6 @@ import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml import { mockAnomalies } from '../../../../common/components/ml/mock'; import { useObservedUserDetails } from '../../../../explore/users/containers/users/observed_details'; import { useUserRelatedHosts } from '../../../../common/containers/related_entities/related_hosts'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { RiskSeverity } from '../../../../../common/search_strategy'; import { USER_DETAILS_TEST_ID, @@ -43,9 +42,6 @@ import { useAlertsByStatus } from '../../../../overview/components/detection_res jest.mock('@kbn/expandable-flyout'); jest.mock('@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'); -jest.mock('../../../../common/hooks/use_experimental_features'); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; - jest.mock('react-router-dom', () => { const actual = jest.requireActual('react-router-dom'); return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; @@ -174,19 +170,17 @@ describe('<UserDetails />', () => { mockUseObservedUserDetails.mockReturnValue(mockUserDetailsResponse); mockUseRiskScore.mockReturnValue(mockRiskScoreResponse); mockUseUsersRelatedHosts.mockReturnValue(mockRelatedHostsResponse); - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); (useMisconfigurationPreview as jest.Mock).mockReturnValue({}); (useAlertsByStatus as jest.Mock).mockReturnValue({ isLoading: false, items: {} }); }); it('should render user details correctly', () => { - const { getByTestId, queryByTestId } = renderUserDetails(mockContextValue); + const { getByTestId } = renderUserDetails(mockContextValue); expect(getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(USER_DETAILS_TEST_ID))).toBeInTheDocument(); - expect(queryByTestId(USER_DETAILS_LINK_TEST_ID)).not.toBeInTheDocument(); + expect(getByTestId(USER_DETAILS_LINK_TEST_ID)).toBeInTheDocument(); }); - it('should render user name as clicable link when feature flag is true', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('should render user name as clicable link', () => { const { getByTestId } = renderUserDetails(mockContextValue); expect(getByTestId(USER_DETAILS_LINK_TEST_ID)).toBeInTheDocument(); @@ -233,7 +227,7 @@ describe('<UserDetails />', () => { describe('Related hosts', () => { it('should render the related host table with correct dates and indices', () => { - const { getByTestId, queryByTestId } = renderUserDetails(mockContextValue); + const { getByTestId } = renderUserDetails(mockContextValue); expect(mockUseUsersRelatedHosts).toBeCalledWith({ from: timestamp, userName: 'test user', @@ -241,7 +235,7 @@ describe('<UserDetails />', () => { skip: false, }); expect(getByTestId(USER_DETAILS_RELATED_HOSTS_TABLE_TEST_ID)).toBeInTheDocument(); - expect(queryByTestId(USER_DETAILS_RELATED_HOSTS_LINK_TEST_ID)).not.toBeInTheDocument(); + expect(getByTestId(USER_DETAILS_RELATED_HOSTS_LINK_TEST_ID)).toBeInTheDocument(); }); it('should render host risk score column when license is valid', () => { @@ -274,8 +268,7 @@ describe('<UserDetails />', () => { ); }); - it('should render host name and ip as clicable link when preview is enabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('should render host name and ip as clicable link', () => { const { getAllByTestId } = renderUserDetails(mockContextValue); expect(getAllByTestId(USER_DETAILS_RELATED_HOSTS_LINK_TEST_ID).length).toBe(1); diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx index e88cbb54f947..00366a367a81 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx @@ -25,14 +25,12 @@ import type { EuiBasicTableColumn } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import type { RelatedHost } from '../../../../../common/search_strategy/security_solution/related_entities/related_hosts'; import type { RiskSeverity } from '../../../../../common/search_strategy'; import { UserOverview } from '../../../../overview/components/user_overview'; import { AnomalyTableProvider } from '../../../../common/components/ml/anomaly/anomaly_table_provider'; import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect'; -import { NetworkDetailsLink } from '../../../../common/components/links'; import { RiskScoreEntity } from '../../../../../common/search_strategy'; import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common'; import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/default_renderer'; @@ -109,7 +107,6 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s const isEntityAnalyticsAuthorized = isPlatinumOrTrialLicense && hasEntityAnalyticsCapability; const { openPreviewPanel } = useExpandableFlyoutApi(); - const isPreviewEnabled = !useIsExperimentalFeatureEnabled('entityAlertPreviewDisabled'); const narrowDateRange = useCallback<NarrowDateRange>( (score, interval) => { @@ -175,16 +172,12 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s render: (host: string) => ( <EuiText grow={false} size="xs"> <CellActions field={HOST_NAME_FIELD_NAME} value={host}> - {isPreviewEnabled ? ( - <PreviewLink - field={HOST_NAME_FIELD_NAME} - value={host} - scopeId={scopeId} - data-test-subj={USER_DETAILS_RELATED_HOSTS_LINK_TEST_ID} - /> - ) : ( - <>{host}</> - )} + <PreviewLink + field={HOST_NAME_FIELD_NAME} + value={host} + scopeId={scopeId} + data-test-subj={USER_DETAILS_RELATED_HOSTS_LINK_TEST_ID} + /> </CellActions> </EuiText> ), @@ -207,15 +200,13 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s render={(ip) => ip == null ? ( getEmptyTagValue() - ) : isPreviewEnabled ? ( + ) : ( <PreviewLink field={HOST_IP_FIELD_NAME} value={ip} scopeId={scopeId} data-test-subj={USER_DETAILS_RELATED_HOSTS_IP_LINK_TEST_ID} /> - ) : ( - <NetworkDetailsLink ip={ip} /> ) } scopeId={scopeId} @@ -241,7 +232,7 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s ] : []), ], - [isEntityAnalyticsAuthorized, scopeId, isPreviewEnabled] + [isEntityAnalyticsAuthorized, scopeId] ); const relatedHostsCount = useMemo( @@ -272,19 +263,14 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s }; const userLink = useMemo( - () => - isPreviewEnabled - ? { - callback: openUserPreview, - tooltip: i18n.translate( - 'xpack.securitySolution.flyout.left.insights.entities.user.userPreviewTitle', - { - defaultMessage: 'Preview user', - } - ), - } - : undefined, - [isPreviewEnabled, openUserPreview] + () => ({ + callback: openUserPreview, + tooltip: i18n.translate( + 'xpack.securitySolution.flyout.left.insights.entities.user.userPreviewTitle', + { defaultMessage: 'Preview user' } + ), + }), + [openUserPreview] ); return ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/tabs/visualize_tab.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/tabs/visualize_tab.tsx index 89e00e06e3a4..1a8e5906e247 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/tabs/visualize_tab.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/tabs/visualize_tab.tsx @@ -88,7 +88,8 @@ const graphVisualizationButton: EuiButtonGroupOptionProps = { * Visualize view displayed in the document details expandable flyout left section */ export const VisualizeTab = memo(() => { - const { scopeId, getFieldsData, dataAsNestedObject } = useDocumentDetailsContext(); + const { scopeId, getFieldsData, dataAsNestedObject, dataFormattedForFieldBrowser } = + useDocumentDetailsContext(); const { openPreviewPanel } = useExpandableFlyoutApi(); const panels = useExpandableFlyoutState(); const [activeVisualizationId, setActiveVisualizationId] = useState( @@ -123,6 +124,7 @@ export const VisualizeTab = memo(() => { const { hasGraphRepresentation } = useGraphPreview({ getFieldsData, ecsData: dataAsNestedObject, + dataFormattedForFieldBrowser, }); const isGraphFeatureEnabled = useIsExperimentalFeatureEnabled( diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx index c805f2a3c67a..9965c5300e71 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx @@ -86,6 +86,7 @@ describe('<GraphPreviewContainer />', () => { timestamp, eventIds: [], hasGraphRepresentation: true, + isAlert: true, }); const { getByTestId, queryByTestId, findByTestId } = renderGraphPreview(); @@ -111,7 +112,109 @@ describe('<GraphPreviewContainer />', () => { expect(mockUseFetchGraphData.mock.calls[0][0]).toEqual({ req: { query: { - eventIds: [], + originEventIds: [], + start: `${timestamp}||-30m`, + end: `${timestamp}||+30m`, + }, + }, + options: { + enabled: true, + refetchOnWindowFocus: false, + }, + }); + }); + + it('should render component for alert', async () => { + mockUseFetchGraphData.mockReturnValue({ + isLoading: false, + isError: false, + data: { nodes: DEFAULT_NODES, edges: [] }, + }); + + const timestamp = new Date().toISOString(); + + (useGraphPreview as jest.Mock).mockReturnValue({ + timestamp, + eventIds: ['eventId'], + isAlert: true, + hasGraphRepresentation: true, + }); + + const { getByTestId, queryByTestId, findByTestId } = renderGraphPreview(); + + // Using findByTestId to wait for the component to be rendered because it is a lazy loaded component + expect(await findByTestId(GRAPH_PREVIEW_TEST_ID)).toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).not.toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).not.toBeInTheDocument(); + expect(mockUseFetchGraphData).toHaveBeenCalled(); + expect(mockUseFetchGraphData.mock.calls[0][0]).toEqual({ + req: { + query: { + originEventIds: [{ id: 'eventId', isAlert: true }], + start: `${timestamp}||-30m`, + end: `${timestamp}||+30m`, + }, + }, + options: { + enabled: true, + refetchOnWindowFocus: false, + }, + }); + }); + + it('should render component for event', async () => { + mockUseFetchGraphData.mockReturnValue({ + isLoading: false, + isError: false, + data: { nodes: DEFAULT_NODES, edges: [] }, + }); + + const timestamp = new Date().toISOString(); + + (useGraphPreview as jest.Mock).mockReturnValue({ + timestamp, + eventIds: ['eventId'], + isAlert: false, + hasGraphRepresentation: true, + }); + + const { getByTestId, queryByTestId, findByTestId } = renderGraphPreview(); + + // Using findByTestId to wait for the component to be rendered because it is a lazy loaded component + expect(await findByTestId(GRAPH_PREVIEW_TEST_ID)).toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).not.toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).not.toBeInTheDocument(); + expect(mockUseFetchGraphData).toHaveBeenCalled(); + expect(mockUseFetchGraphData.mock.calls[0][0]).toEqual({ + req: { + query: { + originEventIds: [{ id: 'eventId', isAlert: false }], start: `${timestamp}||-30m`, end: `${timestamp}||+30m`, }, @@ -136,6 +239,7 @@ describe('<GraphPreviewContainer />', () => { timestamp, eventIds: [], hasGraphRepresentation: true, + isAlert: true, }); const { getByTestId, queryByTestId, findByTestId } = renderGraphPreview({ @@ -164,7 +268,7 @@ describe('<GraphPreviewContainer />', () => { expect(mockUseFetchGraphData.mock.calls[0][0]).toEqual({ req: { query: { - eventIds: [], + originEventIds: [], start: `${timestamp}||-30m`, end: `${timestamp}||+30m`, }, @@ -189,6 +293,7 @@ describe('<GraphPreviewContainer />', () => { timestamp, eventIds: [], hasGraphRepresentation: true, + isAlert: true, }); const { getByTestId, queryByTestId, findByTestId } = renderGraphPreview({ @@ -217,7 +322,7 @@ describe('<GraphPreviewContainer />', () => { expect(mockUseFetchGraphData.mock.calls[0][0]).toEqual({ req: { query: { - eventIds: [], + originEventIds: [], start: `${timestamp}||-30m`, end: `${timestamp}||+30m`, }, @@ -243,6 +348,7 @@ describe('<GraphPreviewContainer />', () => { timestamp, eventIds: [], hasGraphRepresentation: true, + isAlert: true, }); const { getByTestId, queryByTestId, findByTestId } = renderGraphPreview(); @@ -268,7 +374,7 @@ describe('<GraphPreviewContainer />', () => { expect(mockUseFetchGraphData.mock.calls[0][0]).toEqual({ req: { query: { - eventIds: [], + originEventIds: [], start: `${timestamp}||-30m`, end: `${timestamp}||+30m`, }, @@ -293,6 +399,7 @@ describe('<GraphPreviewContainer />', () => { timestamp, eventIds: [], hasGraphRepresentation: false, + isAlert: true, }); const { getByTestId, queryByTestId, findByTestId } = renderGraphPreview(); @@ -320,7 +427,7 @@ describe('<GraphPreviewContainer />', () => { expect(mockUseFetchGraphData.mock.calls[0][0]).toEqual({ req: { query: { - eventIds: [], + originEventIds: [], start: `${timestamp}||-30m`, end: `${timestamp}||+30m`, }, diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx index 90a021877854..b4626f93e823 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx @@ -31,6 +31,7 @@ export const GraphPreviewContainer: React.FC = () => { scopeId, isPreview, isPreviewMode, + dataFormattedForFieldBrowser, } = useDocumentDetailsContext(); const [visualizationInFlyoutEnabled] = useUiSetting$<boolean>( @@ -49,16 +50,18 @@ export const GraphPreviewContainer: React.FC = () => { eventIds, timestamp = new Date().toISOString(), hasGraphRepresentation, + isAlert, } = useGraphPreview({ getFieldsData, ecsData: dataAsNestedObject, + dataFormattedForFieldBrowser, }); // TODO: default start and end might not capture the original event const { isLoading, isError, data } = useFetchGraphData({ req: { query: { - eventIds, + originEventIds: eventIds.map((id) => ({ id, isAlert })), start: `${timestamp}||-30m`, end: `${timestamp}||+30m`, }, diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.test.tsx index ff003813f260..e6f88498b260 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.test.tsx @@ -14,11 +14,7 @@ import { } from './test_ids'; import { HighlightedFieldsCell } from './highlighted_fields_cell'; import { DocumentDetailsContext } from '../../shared/context'; -import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; -import { LeftPanelInsightsTab } from '../../left'; import { TestProviders } from '../../../../common/mock'; -import { ENTITIES_TAB_ID } from '../../left/components/entities_details'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { useGetAgentStatus } from '../../../../management/hooks/agents/use_get_agent_status'; import { mockFlyoutApi } from '../../shared/mocks/mock_flyout_context'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; @@ -47,9 +43,6 @@ jest.mock('../../../../common/lib/kibana', () => { const useGetAgentStatusMock = useGetAgentStatus as jest.Mock; -jest.mock('../../../../common/hooks/use_experimental_features'); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; - const panelContextValue = { eventId: 'event id', indexName: 'indexName', @@ -68,7 +61,6 @@ const renderHighlightedFieldsCell = (values: string[], field: string) => describe('<HighlightedFieldsCell />', () => { beforeAll(() => { jest.mocked(useExpandableFlyoutApi).mockReturnValue(mockFlyoutApi); - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); }); it('should render a basic cell', () => { @@ -83,23 +75,7 @@ describe('<HighlightedFieldsCell />', () => { expect(getByTestId(HIGHLIGHTED_FIELDS_BASIC_CELL_TEST_ID)).toBeInTheDocument(); }); - it('should open left panel when clicking on the link within a a link cell when preview is disabled', () => { - const { getByTestId } = renderHighlightedFieldsCell(['value'], 'user.name'); - - getByTestId(HIGHLIGHTED_FIELDS_LINKED_CELL_TEST_ID).click(); - expect(mockFlyoutApi.openLeftPanel).toHaveBeenCalledWith({ - id: DocumentDetailsLeftPanelKey, - path: { tab: LeftPanelInsightsTab, subTab: ENTITIES_TAB_ID }, - params: { - id: panelContextValue.eventId, - indexName: panelContextValue.indexName, - scopeId: panelContextValue.scopeId, - }, - }); - }); - - it('should open host preview when click on host when preview is not disabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('should open host preview when click on host', () => { const { getByTestId } = renderHighlightedFieldsCell(['test host'], 'host.name'); expect(getByTestId(HIGHLIGHTED_FIELDS_LINKED_CELL_TEST_ID)).toBeInTheDocument(); @@ -114,8 +90,7 @@ describe('<HighlightedFieldsCell />', () => { }); }); - it('should open user preview when click on user when preview is not disabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('should open user preview when click on user', () => { const { getByTestId } = renderHighlightedFieldsCell(['test user'], 'user.name'); expect(getByTestId(HIGHLIGHTED_FIELDS_LINKED_CELL_TEST_ID)).toBeInTheDocument(); @@ -130,8 +105,7 @@ describe('<HighlightedFieldsCell />', () => { }); }); - it('should open ip preview when click on ip when preview is not disabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + it('should open ip preview when click on ip', () => { const { getByTestId } = renderHighlightedFieldsCell(['100:XXX:XXX'], 'source.ip'); expect(getByTestId(HIGHLIGHTED_FIELDS_LINKED_CELL_TEST_ID)).toBeInTheDocument(); @@ -204,6 +178,7 @@ describe('<HighlightedFieldsCell />', () => { expect(getByTestId(HIGHLIGHTED_FIELDS_AGENT_STATUS_CELL_TEST_ID)).toBeInTheDocument(); }); + it('should not render if values is null', () => { const { container } = render( <TestProviders> diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.tsx index e11dee4a74ad..b6fc98653f89 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.tsx @@ -5,19 +5,14 @@ * 2.0. */ -import type { VFC } from 'react'; -import React, { useCallback, useMemo } from 'react'; -import { EuiFlexItem, EuiLink } from '@elastic/eui'; -import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import type { FC } from 'react'; +import React, { useMemo } from 'react'; +import { EuiFlexItem } from '@elastic/eui'; import { getAgentTypeForAgentIdField } from '../../../../common/lib/endpoint/utils/get_agent_type_for_agent_id_field'; import type { ResponseActionAgentType } from '../../../../../common/endpoint/service/response_actions/constants'; import { AgentStatus } from '../../../../common/components/endpoint/agents/agent_status'; import { useDocumentDetailsContext } from '../../shared/context'; import { AGENT_STATUS_FIELD_NAME } from '../../../../timelines/components/timeline/body/renderers/constants'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; -import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; -import { LeftPanelInsightsTab } from '../../left'; -import { ENTITIES_TAB_ID } from '../../left/components/entities_details'; import { HIGHLIGHTED_FIELDS_AGENT_STATUS_CELL_TEST_ID, HIGHLIGHTED_FIELDS_BASIC_CELL_TEST_ID, @@ -44,26 +39,12 @@ export interface HighlightedFieldsCellProps { /** * Renders a component in the highlighted fields table cell based on the field name */ -export const HighlightedFieldsCell: VFC<HighlightedFieldsCellProps> = ({ +export const HighlightedFieldsCell: FC<HighlightedFieldsCellProps> = ({ values, field, originalField = '', }) => { - const { scopeId, eventId, indexName } = useDocumentDetailsContext(); - const { openLeftPanel } = useExpandableFlyoutApi(); - const isPreviewEnabled = !useIsExperimentalFeatureEnabled('entityAlertPreviewDisabled'); - - const goToInsightsEntities = useCallback(() => { - openLeftPanel({ - id: DocumentDetailsLeftPanelKey, - path: { tab: LeftPanelInsightsTab, subTab: ENTITIES_TAB_ID }, - params: { - id: eventId, - indexName, - scopeId, - }, - }); - }, [eventId, indexName, openLeftPanel, scopeId]); + const { scopeId } = useDocumentDetailsContext(); const agentType: ResponseActionAgentType = useMemo(() => { return getAgentTypeForAgentIdField(originalField); @@ -79,20 +60,13 @@ export const HighlightedFieldsCell: VFC<HighlightedFieldsCellProps> = ({ key={`${i}-${value}`} data-test-subj={`${value}-${HIGHLIGHTED_FIELDS_CELL_TEST_ID}`} > - {isPreviewEnabled && hasPreview(field) ? ( + {hasPreview(field) ? ( <PreviewLink field={field} value={value} scopeId={scopeId} data-test-subj={HIGHLIGHTED_FIELDS_LINKED_CELL_TEST_ID} /> - ) : hasPreview(field) ? ( - <EuiLink - onClick={goToInsightsEntities} - data-test-subj={HIGHLIGHTED_FIELDS_LINKED_CELL_TEST_ID} - > - {value} - </EuiLink> ) : field === AGENT_STATUS_FIELD_NAME ? ( <AgentStatus agentId={String(value ?? '')} diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx index 4c29a84d431a..0d98dccc91bf 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx @@ -26,11 +26,7 @@ import { DocumentDetailsContext } from '../../shared/context'; import { mockContextValue } from '../../shared/mocks/mock_context'; import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; -import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; import { HostPreviewPanelKey } from '../../../entity_details/host_right'; -import { LeftPanelInsightsTab } from '../../left'; -import { ENTITIES_TAB_ID } from '../../left/components/entities_details'; import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score'; import { mockFlyoutApi } from '../../shared/mocks/mock_flyout_context'; import { createTelemetryServiceMock } from '../../../../common/lib/telemetry/telemetry_service.mock'; @@ -86,9 +82,6 @@ jest.mock('../../../../common/lib/kibana', () => { }; }); -jest.mock('../../../../common/hooks/use_experimental_features'); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; - const mockUseGlobalTime = jest.fn().mockReturnValue({ from, to }); jest.mock('../../../../common/containers/use_global_time', () => { return { @@ -124,7 +117,6 @@ const renderHostEntityContent = () => describe('<HostEntityContent />', () => { beforeAll(() => { jest.mocked(useExpandableFlyoutApi).mockReturnValue(mockFlyoutApi); - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); (useMisconfigurationPreview as jest.Mock).mockReturnValue({}); (useVulnerabilitiesPreview as jest.Mock).mockReturnValue({}); (useAlertsByStatus as jest.Mock).mockReturnValue({ isLoading: false, items: {} }); @@ -204,28 +196,9 @@ describe('<HostEntityContent />', () => { expect(getByTestId(ENTITIES_HOST_OVERVIEW_LAST_SEEN_TEST_ID)).toHaveTextContent('—'); }); - it('should navigate to left panel entities tab when clicking on title when feature flag is off', () => { - mockUseHostDetails.mockReturnValue([false, { hostDetails: hostData }]); - mockUseRiskScore.mockReturnValue({ data: riskLevel, isAuthorized: true }); - - const { getByTestId } = renderHostEntityContent(); - - getByTestId(ENTITIES_HOST_OVERVIEW_LINK_TEST_ID).click(); - expect(mockFlyoutApi.openLeftPanel).toHaveBeenCalledWith({ - id: DocumentDetailsLeftPanelKey, - path: { tab: LeftPanelInsightsTab, subTab: ENTITIES_TAB_ID }, - params: { - id: panelContextValue.eventId, - indexName: panelContextValue.indexName, - scopeId: panelContextValue.scopeId, - }, - }); - }); - - it('should open host preview when clicking on title when feature flag is on', () => { + it('should open host preview when clicking on title', () => { mockUseHostDetails.mockReturnValue([false, { hostDetails: hostData }]); mockUseRiskScore.mockReturnValue({ data: riskLevel, isAuthorized: true }); - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); const { getByTestId } = renderHostEntityContent(); diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx index 9b60eefbb5f6..eb1ca093fbb0 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, - EuiLink, EuiText, EuiIcon, useEuiTheme, @@ -19,8 +18,6 @@ import { import { css } from '@emotion/css'; import { getOr } from 'lodash/fp'; import { i18n } from '@kbn/i18n'; -import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { HOST_NAME_FIELD_NAME } from '../../../../timelines/components/timeline/body/renderers/constants'; import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score'; import { useDocumentDetailsContext } from '../../shared/context'; @@ -44,7 +41,6 @@ import { LAST_SEEN, HOST_RISK_LEVEL, } from '../../../../overview/components/host_overview/translations'; -import { ENTITIES_TAB_ID } from '../../left/components/entities_details'; import { ENTITIES_HOST_OVERVIEW_TEST_ID, ENTITIES_HOST_OVERVIEW_OS_FAMILY_TEST_ID, @@ -56,8 +52,6 @@ import { ENTITIES_HOST_OVERVIEW_MISCONFIGURATIONS_TEST_ID, ENTITIES_HOST_OVERVIEW_VULNERABILITIES_TEST_ID, } from './test_ids'; -import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; -import { LeftPanelInsightsTab } from '../../left'; import { RiskScoreDocTooltip } from '../../../../overview/components/common'; import { PreviewLink } from '../../../shared/components/preview_link'; import { MisconfigurationsInsight } from '../../shared/components/misconfiguration_insight'; @@ -85,21 +79,7 @@ export const HOST_PREVIEW_BANNER = { * Host preview content for the entities preview in right flyout. It contains ip addresses and risk level */ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName }) => { - const { eventId, indexName, scopeId } = useDocumentDetailsContext(); - const { openLeftPanel } = useExpandableFlyoutApi(); - const isPreviewEnabled = !useIsExperimentalFeatureEnabled('entityAlertPreviewDisabled'); - - const goToEntitiesTab = useCallback(() => { - openLeftPanel({ - id: DocumentDetailsLeftPanelKey, - path: { tab: LeftPanelInsightsTab, subTab: ENTITIES_TAB_ID }, - params: { - id: eventId, - indexName, - scopeId, - }, - }); - }, [eventId, openLeftPanel, indexName, scopeId]); + const { scopeId } = useDocumentDetailsContext(); const { from, to } = useGlobalTime(); const { selectedPatterns } = useSourcererDataView(); @@ -212,34 +192,21 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName <EuiIcon type={HOST_ICON} /> </EuiFlexItem> <EuiFlexItem grow={false}> - {isPreviewEnabled ? ( - <PreviewLink - field={HOST_NAME_FIELD_NAME} - value={hostName} - scopeId={scopeId} - data-test-subj={ENTITIES_HOST_OVERVIEW_LINK_TEST_ID} - > - <EuiText - css={css` - font-size: ${xsFontSize}; - font-weight: ${euiTheme.font.weight.bold}; - `} - > - {hostName} - </EuiText> - </PreviewLink> - ) : ( - <EuiLink - data-test-subj={ENTITIES_HOST_OVERVIEW_LINK_TEST_ID} + <PreviewLink + field={HOST_NAME_FIELD_NAME} + value={hostName} + scopeId={scopeId} + data-test-subj={ENTITIES_HOST_OVERVIEW_LINK_TEST_ID} + > + <EuiText css={css` font-size: ${xsFontSize}; font-weight: ${euiTheme.font.weight.bold}; `} - onClick={goToEntitiesTab} > {hostName} - </EuiLink> - )} + </EuiText> + </PreviewLink> </EuiFlexItem> </EuiFlexGroup> </EuiFlexItem> diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/table_field_value_cell.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/table_field_value_cell.test.tsx index eec53dbe3d26..2c7abe5bb1d2 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/table_field_value_cell.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/table_field_value_cell.test.tsx @@ -9,7 +9,6 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; import type { FieldSpec } from '@kbn/data-plugin/common'; import { DocumentDetailsContext } from '../../shared/context'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import type { EventFieldsData } from '../../../../common/components/event_details/types'; import { TableFieldValueCell } from './table_field_value_cell'; @@ -35,9 +34,6 @@ jest.mock('../../../../common/lib/kibana', () => { }; }); -jest.mock('../../../../common/hooks/use_experimental_features'); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; - const panelContextValue = { eventId: 'event id', indexName: 'indexName', @@ -66,7 +62,6 @@ describe('TableFieldValueCell', () => { beforeAll(() => { jest.clearAllMocks(); jest.mocked(useExpandableFlyoutApi).mockReturnValue(mockFlyoutApi); - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); }); describe('common behavior', () => { @@ -213,7 +208,7 @@ describe('TableFieldValueCell', () => { }); }); - it('should open preview when preview is not disabled', () => { + it('should open preview', () => { screen.getByTestId(`${FLYOUT_TABLE_PREVIEW_LINK_FIELD_TEST_ID}-0`).click(); expect(mockFlyoutApi.openPreviewPanel).toHaveBeenCalledWith({ diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/table_field_value_cell.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/table_field_value_cell.tsx index a0095bb8eadf..17ea9980f1ee 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/table_field_value_cell.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/table_field_value_cell.tsx @@ -8,7 +8,6 @@ import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import type { FieldSpec } from '@kbn/data-plugin/common'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { getFieldFormat } from '../utils/get_field_format'; import type { EventFieldsData } from '../../../../common/components/event_details/types'; import { OverflowField } from '../../../../common/components/tables/helpers'; @@ -66,7 +65,6 @@ export const TableFieldValueCell = memo( values, isPreview, }: FieldValueCellProps) => { - const isPreviewEnabled = !useIsExperimentalFeatureEnabled('entityAlertPreviewDisabled'); if (values == null) { return null; } @@ -92,7 +90,7 @@ export const TableFieldValueCell = memo( <EuiFlexItem grow={false} key={`${i}-${value}`}> {data.field === MESSAGE_FIELD_NAME ? ( <OverflowField value={value} /> - ) : isPreviewEnabled && hasPreview(data.field) ? ( + ) : hasPreview(data.field) ? ( <PreviewLink field={data.field} value={value} diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx index 5df159c2e5a2..ad09269a6103 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx @@ -23,12 +23,8 @@ import { useObservedUserDetails } from '../../../../explore/users/containers/use import { mockContextValue } from '../../shared/mocks/mock_context'; import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser'; import { DocumentDetailsContext } from '../../shared/context'; -import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; -import { LeftPanelInsightsTab } from '../../left'; -import { ENTITIES_TAB_ID } from '../../left/components/entities_details'; import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { mockFlyoutApi } from '../../shared/mocks/mock_flyout_context'; import { UserPreviewPanelKey } from '../../../entity_details/user_right'; import { useAlertsByStatus } from '../../../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; @@ -71,9 +67,6 @@ const mockAlertData = { }, }; -jest.mock('../../../../common/hooks/use_experimental_features'); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; - const mockUseGlobalTime = jest.fn().mockReturnValue({ from, to }); jest.mock('../../../../common/containers/use_global_time', () => { return { @@ -109,7 +102,6 @@ const renderUserEntityOverview = () => describe('<UserEntityOverview />', () => { beforeAll(() => { jest.mocked(useExpandableFlyoutApi).mockReturnValue(mockFlyoutApi); - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); (useMisconfigurationPreview as jest.Mock).mockReturnValue({}); (useAlertsByStatus as jest.Mock).mockReturnValue({ isLoading: false, items: {} }); }); @@ -190,34 +182,9 @@ describe('<UserEntityOverview />', () => { expect(queryByTestId(ENTITIES_USER_OVERVIEW_DOMAIN_TEST_ID)).not.toBeInTheDocument(); }); - it('should navigate to left panel entities tab when clicking on title when feature flag is off', () => { - mockUseUserDetails.mockReturnValue([false, { userDetails: userData }]); - mockUseRiskScore.mockReturnValue({ data: riskLevel, isAuthorized: true }); - - const { getByTestId } = render( - <TestProviders> - <DocumentDetailsContext.Provider value={panelContextValue}> - <UserEntityOverview userName={userName} /> - </DocumentDetailsContext.Provider> - </TestProviders> - ); - - getByTestId(ENTITIES_USER_OVERVIEW_LINK_TEST_ID).click(); - expect(mockFlyoutApi.openLeftPanel).toHaveBeenCalledWith({ - id: DocumentDetailsLeftPanelKey, - path: { tab: LeftPanelInsightsTab, subTab: ENTITIES_TAB_ID }, - params: { - id: panelContextValue.eventId, - indexName: panelContextValue.indexName, - scopeId: panelContextValue.scopeId, - }, - }); - }); - - it('should open user preview if feature flag is true', () => { + it('should open user preview', () => { mockUseUserDetails.mockReturnValue([false, { userDetails: userData }]); mockUseRiskScore.mockReturnValue({ data: riskLevel, isAuthorized: true }); - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); const { getByTestId } = render( <TestProviders> diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx index 1008f6139cd6..22f889a61c54 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx @@ -5,13 +5,12 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText, - EuiLink, useEuiTheme, useEuiFontSize, EuiSkeletonText, @@ -19,11 +18,6 @@ import { import { css } from '@emotion/css'; import { getOr } from 'lodash/fp'; import { i18n } from '@kbn/i18n'; -import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; -import { DocumentDetailsLeftPanelKey } from '../../shared/constants/panel_keys'; -import { LeftPanelInsightsTab } from '../../left'; -import { ENTITIES_TAB_ID } from '../../left/components/entities_details'; import { useDocumentDetailsContext } from '../../shared/context'; import type { DescriptionList } from '../../../../../common/utility_types'; import { USER_NAME_FIELD_NAME } from '../../../../timelines/components/timeline/body/renderers/constants'; @@ -83,22 +77,7 @@ export const USER_PREVIEW_BANNER = { * User preview content for the entities preview in right flyout. It contains ip addresses and risk level */ export const UserEntityOverview: React.FC<UserEntityOverviewProps> = ({ userName }) => { - const { eventId, indexName, scopeId } = useDocumentDetailsContext(); - const { openLeftPanel } = useExpandableFlyoutApi(); - - const isPreviewEnabled = !useIsExperimentalFeatureEnabled('entityAlertPreviewDisabled'); - - const goToEntitiesTab = useCallback(() => { - openLeftPanel({ - id: DocumentDetailsLeftPanelKey, - path: { tab: LeftPanelInsightsTab, subTab: ENTITIES_TAB_ID }, - params: { - id: eventId, - indexName, - scopeId, - }, - }); - }, [eventId, openLeftPanel, indexName, scopeId]); + const { scopeId } = useDocumentDetailsContext(); const { from, to } = useGlobalTime(); const { selectedPatterns } = useSourcererDataView(); @@ -210,34 +189,21 @@ export const UserEntityOverview: React.FC<UserEntityOverviewProps> = ({ userName <EuiIcon type={USER_ICON} /> </EuiFlexItem> <EuiFlexItem grow={false}> - {isPreviewEnabled ? ( - <PreviewLink - field={USER_NAME_FIELD_NAME} - value={userName} - scopeId={scopeId} - data-test-subj={ENTITIES_USER_OVERVIEW_LINK_TEST_ID} - > - <EuiText - css={css` - font-size: ${xsFontSize}; - font-weight: ${euiTheme.font.weight.bold}; - `} - > - {userName} - </EuiText> - </PreviewLink> - ) : ( - <EuiLink - data-test-subj={ENTITIES_USER_OVERVIEW_LINK_TEST_ID} + <PreviewLink + field={USER_NAME_FIELD_NAME} + value={userName} + scopeId={scopeId} + data-test-subj={ENTITIES_USER_OVERVIEW_LINK_TEST_ID} + > + <EuiText css={css` font-size: ${xsFontSize}; font-weight: ${euiTheme.font.weight.bold}; `} - onClick={goToEntitiesTab} > {userName} - </EuiLink> - )} + </EuiText> + </PreviewLink> </EuiFlexItem> </EuiFlexGroup> </EuiFlexItem> diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx index 6fb4d5d30b89..dc3b1f00e0d5 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx @@ -106,6 +106,7 @@ describe('<VisualizationsSection />', () => { }); mockUseGraphPreview.mockReturnValue({ hasGraphRepresentation: true, + eventIds: [], }); mockUseFetchGraphData.mockReturnValue({ isLoading: false, diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.tsx index 23bea1f8fecd..467171cd49f2 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.tsx @@ -28,7 +28,8 @@ const KEY = 'visualizations'; */ export const VisualizationsSection = memo(() => { const expanded = useExpandSection({ title: KEY, defaultValue: false }); - const { dataAsNestedObject, getFieldsData } = useDocumentDetailsContext(); + const { dataAsNestedObject, getFieldsData, dataFormattedForFieldBrowser } = + useDocumentDetailsContext(); const [visualizationInFlyoutEnabled] = useUiSetting$<boolean>( ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING @@ -42,6 +43,7 @@ export const VisualizationsSection = memo(() => { const { hasGraphRepresentation } = useGraphPreview({ getFieldsData, ecsData: dataAsNestedObject, + dataFormattedForFieldBrowser, }); const shouldShowGraphPreview = diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_graph_preview.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_graph_preview.test.tsx index 453f897d4e18..cf1ee8207839 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_graph_preview.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_graph_preview.test.tsx @@ -5,15 +5,18 @@ * 2.0. */ -import type { RenderHookResult } from '@testing-library/react'; +import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; import { renderHook } from '@testing-library/react'; -import type { UseGraphPreviewParams, UseGraphPreviewResult } from './use_graph_preview'; +import type { UseGraphPreviewParams } from './use_graph_preview'; import { useGraphPreview } from './use_graph_preview'; import type { GetFieldsData } from './use_get_fields_data'; import { mockFieldData } from '../mocks/mock_get_fields_data'; +import { mockDataFormattedForFieldBrowser } from '../mocks/mock_data_formatted_for_field_browser'; -const mockGetFieldsData: GetFieldsData = (field: string) => { - if (field === 'kibana.alert.original_event.id') { +const alertMockGetFieldsData: GetFieldsData = (field: string) => { + if (field === 'kibana.alert.uuid') { + return 'alertId'; + } else if (field === 'kibana.alert.original_event.id') { return 'eventId'; } else if (field === 'actor.entity.id') { return 'actorId'; @@ -24,18 +27,36 @@ const mockGetFieldsData: GetFieldsData = (field: string) => { return mockFieldData[field]; }; -describe('useGraphPreview', () => { - let hookResult: RenderHookResult<UseGraphPreviewResult, UseGraphPreviewParams>; +const alertMockDataFormattedForFieldBrowser = mockDataFormattedForFieldBrowser; + +const eventMockGetFieldsData: GetFieldsData = (field: string) => { + if (field === 'kibana.alert.uuid') { + return; + } else if (field === 'kibana.alert.original_event.id') { + return; + } else if (field === 'event.id') { + return 'eventId'; + } else if (field === 'actor.entity.id') { + return 'actorId'; + } else if (field === 'target.entity.id') { + return 'targetId'; + } + return mockFieldData[field]; +}; + +const eventMockDataFormattedForFieldBrowser: TimelineEventsDetailsItem[] = []; + +describe('useGraphPreview', () => { it(`should return false when missing actor`, () => { const getFieldsData: GetFieldsData = (field: string) => { if (field === 'actor.entity.id') { return; } - return mockGetFieldsData(field); + return alertMockGetFieldsData(field); }; - hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + const hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { initialProps: { getFieldsData, ecsData: { @@ -44,37 +65,41 @@ describe('useGraphPreview', () => { action: ['action'], }, }, + dataFormattedForFieldBrowser: alertMockDataFormattedForFieldBrowser, }, }); - const { hasGraphRepresentation, timestamp, eventIds, actorIds, action, targetIds } = - hookResult.result.current; - expect(hasGraphRepresentation).toEqual(false); - expect(timestamp).toEqual(mockFieldData['@timestamp'][0]); - expect(eventIds).toEqual(['eventId']); - expect(actorIds).toEqual([]); - expect(targetIds).toEqual(['targetId']); - expect(action).toEqual(['action']); + expect(hookResult.result.current).toStrictEqual({ + hasGraphRepresentation: false, + timestamp: mockFieldData['@timestamp'][0], + eventIds: ['eventId'], + actorIds: [], + action: ['action'], + targetIds: ['targetId'], + isAlert: true, + }); }); it(`should return false when missing event.action`, () => { - hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + const hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { initialProps: { - getFieldsData: mockGetFieldsData, + getFieldsData: alertMockGetFieldsData, ecsData: { _id: 'id', }, + dataFormattedForFieldBrowser: alertMockDataFormattedForFieldBrowser, }, }); - const { hasGraphRepresentation, timestamp, eventIds, actorIds, action, targetIds } = - hookResult.result.current; - expect(hasGraphRepresentation).toEqual(false); - expect(timestamp).toEqual(mockFieldData['@timestamp'][0]); - expect(eventIds).toEqual(['eventId']); - expect(actorIds).toEqual(['actorId']); - expect(targetIds).toEqual(['targetId']); - expect(action).toEqual(undefined); + expect(hookResult.result.current).toStrictEqual({ + hasGraphRepresentation: false, + timestamp: mockFieldData['@timestamp'][0], + eventIds: ['eventId'], + actorIds: ['actorId'], + action: undefined, + targetIds: ['targetId'], + isAlert: true, + }); }); it(`should return false when missing target`, () => { @@ -82,26 +107,28 @@ describe('useGraphPreview', () => { if (field === 'target.entity.id') { return; } - return mockGetFieldsData(field); + return alertMockGetFieldsData(field); }; - hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + const hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { initialProps: { getFieldsData, ecsData: { _id: 'id', }, + dataFormattedForFieldBrowser: alertMockDataFormattedForFieldBrowser, }, }); - const { hasGraphRepresentation, timestamp, eventIds, actorIds, action, targetIds } = - hookResult.result.current; - expect(hasGraphRepresentation).toEqual(false); - expect(timestamp).toEqual(mockFieldData['@timestamp'][0]); - expect(eventIds).toEqual(['eventId']); - expect(actorIds).toEqual(['actorId']); - expect(targetIds).toEqual([]); - expect(action).toEqual(undefined); + expect(hookResult.result.current).toStrictEqual({ + hasGraphRepresentation: false, + timestamp: mockFieldData['@timestamp'][0], + eventIds: ['eventId'], + actorIds: ['actorId'], + action: undefined, + targetIds: [], + isAlert: true, + }); }); it(`should return false when missing original_event.id`, () => { @@ -110,10 +137,10 @@ describe('useGraphPreview', () => { return; } - return mockGetFieldsData(field); + return alertMockGetFieldsData(field); }; - hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + const hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { initialProps: { getFieldsData, ecsData: { @@ -122,17 +149,19 @@ describe('useGraphPreview', () => { action: ['action'], }, }, + dataFormattedForFieldBrowser: alertMockDataFormattedForFieldBrowser, }, }); - const { hasGraphRepresentation, timestamp, eventIds, actorIds, action, targetIds } = - hookResult.result.current; - expect(hasGraphRepresentation).toEqual(false); - expect(timestamp).toEqual(mockFieldData['@timestamp'][0]); - expect(eventIds).toEqual([]); - expect(actorIds).toEqual(['actorId']); - expect(targetIds).toEqual(['targetId']); - expect(action).toEqual(['action']); + expect(hookResult.result.current).toStrictEqual({ + hasGraphRepresentation: false, + timestamp: mockFieldData['@timestamp'][0], + eventIds: [], + actorIds: ['actorId'], + action: ['action'], + targetIds: ['targetId'], + isAlert: true, + }); }); it(`should return false when timestamp is missing`, () => { @@ -141,10 +170,10 @@ describe('useGraphPreview', () => { return; } - return mockGetFieldsData(field); + return alertMockGetFieldsData(field); }; - hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + const hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { initialProps: { getFieldsData, ecsData: { @@ -153,45 +182,53 @@ describe('useGraphPreview', () => { action: ['action'], }, }, + dataFormattedForFieldBrowser: alertMockDataFormattedForFieldBrowser, }, }); - const { hasGraphRepresentation, timestamp, eventIds, actorIds, action, targetIds } = - hookResult.result.current; - expect(hasGraphRepresentation).toEqual(false); - expect(timestamp).toEqual(null); - expect(eventIds).toEqual(['eventId']); - expect(actorIds).toEqual(['actorId']); - expect(targetIds).toEqual(['targetId']); - expect(action).toEqual(['action']); + expect(hookResult.result.current).toStrictEqual({ + hasGraphRepresentation: false, + timestamp: null, + eventIds: ['eventId'], + actorIds: ['actorId'], + action: ['action'], + targetIds: ['targetId'], + isAlert: true, + }); }); - it(`should return true when alert is has graph preview`, () => { - hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + it(`should return true when event has graph graph preview`, () => { + const hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { initialProps: { - getFieldsData: mockGetFieldsData, + getFieldsData: eventMockGetFieldsData, ecsData: { _id: 'id', event: { action: ['action'], }, }, + dataFormattedForFieldBrowser: eventMockDataFormattedForFieldBrowser, }, }); - const { hasGraphRepresentation, timestamp, eventIds, actorIds, action, targetIds } = - hookResult.result.current; - expect(hasGraphRepresentation).toEqual(true); - expect(timestamp).toEqual(mockFieldData['@timestamp'][0]); - expect(eventIds).toEqual(['eventId']); - expect(actorIds).toEqual(['actorId']); - expect(targetIds).toEqual(['targetId']); - expect(action).toEqual(['action']); + expect(hookResult.result.current).toStrictEqual({ + hasGraphRepresentation: true, + timestamp: mockFieldData['@timestamp'][0], + eventIds: ['eventId'], + actorIds: ['actorId'], + action: ['action'], + targetIds: ['targetId'], + isAlert: false, + }); }); - it(`should return true when alert is has graph preview with multiple values`, () => { + it(`should return true when event has graph preview with multiple values`, () => { const getFieldsData: GetFieldsData = (field: string) => { - if (field === 'kibana.alert.original_event.id') { + if (field === 'kibana.alert.uuid') { + return; + } else if (field === 'kibana.alert.original_event.id') { + return; + } else if (field === 'event.id') { return ['id1', 'id2']; } else if (field === 'actor.entity.id') { return ['actorId1', 'actorId2']; @@ -202,7 +239,7 @@ describe('useGraphPreview', () => { return mockFieldData[field]; }; - hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + const hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { initialProps: { getFieldsData, ecsData: { @@ -211,16 +248,82 @@ describe('useGraphPreview', () => { action: ['action1', 'action2'], }, }, + dataFormattedForFieldBrowser: eventMockDataFormattedForFieldBrowser, }, }); - const { hasGraphRepresentation, timestamp, eventIds, actorIds, action, targetIds } = - hookResult.result.current; - expect(hasGraphRepresentation).toEqual(true); - expect(timestamp).toEqual(mockFieldData['@timestamp'][0]); - expect(eventIds).toEqual(['id1', 'id2']); - expect(actorIds).toEqual(['actorId1', 'actorId2']); - expect(action).toEqual(['action1', 'action2']); - expect(targetIds).toEqual(['targetId1', 'targetId2']); + expect(hookResult.result.current).toStrictEqual({ + hasGraphRepresentation: true, + timestamp: mockFieldData['@timestamp'][0], + eventIds: ['id1', 'id2'], + actorIds: ['actorId1', 'actorId2'], + action: ['action1', 'action2'], + targetIds: ['targetId1', 'targetId2'], + isAlert: false, + }); + }); + + it(`should return true when alert has graph preview`, () => { + const hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + initialProps: { + getFieldsData: alertMockGetFieldsData, + ecsData: { + _id: 'id', + event: { + action: ['action'], + }, + }, + dataFormattedForFieldBrowser: alertMockDataFormattedForFieldBrowser, + }, + }); + + expect(hookResult.result.current).toStrictEqual({ + hasGraphRepresentation: true, + timestamp: mockFieldData['@timestamp'][0], + eventIds: ['eventId'], + actorIds: ['actorId'], + action: ['action'], + targetIds: ['targetId'], + isAlert: true, + }); + }); + + it(`should return true when alert has graph preview with multiple values`, () => { + const getFieldsData: GetFieldsData = (field: string) => { + if (field === 'kibana.alert.uuid') { + return 'alertId'; + } else if (field === 'kibana.alert.original_event.id') { + return ['id1', 'id2']; + } else if (field === 'actor.entity.id') { + return ['actorId1', 'actorId2']; + } else if (field === 'target.entity.id') { + return ['targetId1', 'targetId2']; + } + + return mockFieldData[field]; + }; + + const hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + initialProps: { + getFieldsData, + ecsData: { + _id: 'id', + event: { + action: ['action1', 'action2'], + }, + }, + dataFormattedForFieldBrowser: alertMockDataFormattedForFieldBrowser, + }, + }); + + expect(hookResult.result.current).toStrictEqual({ + hasGraphRepresentation: true, + timestamp: mockFieldData['@timestamp'][0], + eventIds: ['id1', 'id2'], + actorIds: ['actorId1', 'actorId2'], + action: ['action1', 'action2'], + targetIds: ['targetId1', 'targetId2'], + isAlert: true, + }); }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_graph_preview.ts b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_graph_preview.ts index 48233afab02d..8f05b87844fb 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_graph_preview.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_graph_preview.ts @@ -5,10 +5,12 @@ * 2.0. */ +import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; import { get } from 'lodash/fp'; import type { GetFieldsData } from './use_get_fields_data'; import { getField, getFieldArray } from '../utils'; +import { useBasicDataFromDetailsData } from './use_basic_data_from_details_data'; export interface UseGraphPreviewParams { /** @@ -20,6 +22,11 @@ export interface UseGraphPreviewParams { * An object with top level fields from the ECS object */ ecsData: Ecs; + + /** + * An array of field objects with category and value + */ + dataFormattedForFieldBrowser: TimelineEventsDetailsItem[]; } /** * Interface for the result of the useGraphPreview hook @@ -54,6 +61,11 @@ export interface UseGraphPreviewResult { * Boolean indicating if the event is has a graph representation (contains event ids, actor ids and action) */ hasGraphRepresentation: boolean; + + /** + * Boolean indicating if the event is an alert or not + */ + isAlert: boolean; } /** @@ -62,6 +74,7 @@ export interface UseGraphPreviewResult { export const useGraphPreview = ({ getFieldsData, ecsData, + dataFormattedForFieldBrowser, }: UseGraphPreviewParams): UseGraphPreviewResult => { const timestamp = getField(getFieldsData('@timestamp')); const originalEventId = getFieldsData('kibana.alert.original_event.id'); @@ -77,6 +90,7 @@ export const useGraphPreview = ({ actorIds.length > 0 && eventIds.length > 0 && targetIds.length > 0; + const { isAlert } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser); - return { timestamp, eventIds, actorIds, action, targetIds, hasGraphRepresentation }; + return { timestamp, eventIds, actorIds, action, targetIds, hasGraphRepresentation, isAlert }; }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/common/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/management/common/translations.ts index 6929452e5d9e..50885a9db6b6 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/common/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/management/common/translations.ts @@ -279,7 +279,7 @@ Command Examples for Running Scripts: 3. Executes a raw script provided entirely within the "--Raw" flag. - runscript --Raw="Get-ChildItem." + runscript --Raw=\`\`\`Get-ChildItem.\`\`\` 4. Executes a script located on the remote host at the specified path with the provided command-line arguments. diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response.tsx b/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response.tsx index 43ff3ffca5a6..09d1e45f24c4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response.tsx @@ -10,6 +10,8 @@ import type { ActionDetails, MaybeImmutable, ResponseActionExecuteOutputContent, + ResponseActionRunScriptOutputContent, + ResponseActionRunScriptParameters, ResponseActionsExecuteParameters, } from '../../../../common/endpoint/types'; import { EXECUTE_FILE_LINK_TITLE } from '../endpoint_response_actions_list/translations'; @@ -18,14 +20,18 @@ import { ExecuteActionHostResponseOutput } from './execute_action_host_response_ export interface ExecuteActionHostResponseProps { action: MaybeImmutable< - ActionDetails<ResponseActionExecuteOutputContent, ResponseActionsExecuteParameters> + | ActionDetails<ResponseActionExecuteOutputContent, ResponseActionsExecuteParameters> + | ActionDetails<ResponseActionRunScriptOutputContent, ResponseActionRunScriptParameters> >; agentId?: string; canAccessFileDownloadLink: boolean; 'data-test-subj'?: string; textSize?: 'xs' | 's'; + hideFile?: boolean; + hideContext?: boolean; } +// Note: also used for RunScript command export const ExecuteActionHostResponse = memo<ExecuteActionHostResponseProps>( ({ action, @@ -33,6 +39,8 @@ export const ExecuteActionHostResponse = memo<ExecuteActionHostResponseProps>( canAccessFileDownloadLink, textSize = 's', 'data-test-subj': dataTestSubj, + hideFile, + hideContext, }) => { const outputContent = useMemo( () => @@ -44,21 +52,24 @@ export const ExecuteActionHostResponse = memo<ExecuteActionHostResponseProps>( return ( <> - <EuiFlexItem> - <ResponseActionFileDownloadLink - action={action} - buttonTitle={EXECUTE_FILE_LINK_TITLE} - canAccessFileDownloadLink={canAccessFileDownloadLink} - data-test-subj={`${dataTestSubj}-getExecuteLink`} - textSize={textSize} - /> - <EuiSpacer size="xxl" /> - </EuiFlexItem> + {!hideFile && ( + <EuiFlexItem> + <ResponseActionFileDownloadLink + action={action} + buttonTitle={EXECUTE_FILE_LINK_TITLE} + canAccessFileDownloadLink={canAccessFileDownloadLink} + data-test-subj={`${dataTestSubj}-getExecuteLink`} + textSize={textSize} + /> + <EuiSpacer size="xxl" /> + </EuiFlexItem> + )} {outputContent && ( <ExecuteActionHostResponseOutput outputContent={outputContent} data-test-subj={`${dataTestSubj}-executeResponseOutput`} textSize={textSize} + hideContext={hideContext} /> )} </> diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.tsx b/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.tsx index f24f18e14939..4e6161b8767d 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.tsx @@ -98,6 +98,7 @@ interface ShellInfoContentProps { textSize?: 's' | 'xs'; title: string; } + const ShellInfoContent = memo<ShellInfoContentProps>(({ content, textSize, title }) => ( <StyledEuiText size={textSize}> <strong> @@ -178,10 +179,12 @@ export interface ExecuteActionHostResponseOutputProps { outputContent: ResponseActionExecuteOutputContent; 'data-test-subj'?: string; textSize?: 's' | 'xs'; + hideContext?: boolean; } +// Note: also used for RunScript command export const ExecuteActionHostResponseOutput = memo<ExecuteActionHostResponseOutputProps>( - ({ outputContent, 'data-test-subj': dataTestSubj, textSize = 'xs' }) => { + ({ outputContent, 'data-test-subj': dataTestSubj, textSize = 'xs', hideContext }) => { const contextContent = useMemo( () => ( <> @@ -216,14 +219,16 @@ export const ExecuteActionHostResponseOutput = memo<ExecuteActionHostResponseOut return ( <> - <EuiFlexItem> - <ExecutionActionOutputAccordion - content={contextContent} - data-test-subj={`${dataTestSubj}-context`} - textSize={textSize} - type="context" - /> - </EuiFlexItem> + {!hideContext && ( + <EuiFlexItem> + <ExecutionActionOutputAccordion + content={contextContent} + data-test-subj={`${dataTestSubj}-context`} + textSize={textSize} + type="context" + /> + </EuiFlexItem> + )} <EuiFlexItem> {outputContent.stderr.length > 0 && ( <> diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/run_script_action.tsx b/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/run_script_action.tsx new file mode 100644 index 000000000000..e2ed35845502 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/run_script_action.tsx @@ -0,0 +1,96 @@ +/* + * 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, { memo, useMemo } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { ExecuteActionHostResponse } from '../../endpoint_execute_action'; +import { useSendRunScriptEndpoint } from '../../../hooks/response_actions/use_send_run_script_endpoint_request'; +import type { RunScriptActionRequestBody } from '../../../../../common/api/endpoint'; +import { useConsoleActionSubmitter } from '../hooks/use_console_action_submitter'; +import type { + ResponseActionRunScriptOutputContent, + ResponseActionRunScriptParameters, +} from '../../../../../common/endpoint/types'; +import type { ActionRequestComponentProps } from '../types'; + +export const RunScriptActionResult = memo< + ActionRequestComponentProps< + { + Raw?: string; + HostPath?: string; + CloudFile?: string; + CommandLine?: string; + Timeout?: number; + comment?: string; + }, + ResponseActionRunScriptOutputContent, + ResponseActionRunScriptParameters + > +>(({ command, setStore, store, status, setStatus, ResultComponent }) => { + const actionCreator = useSendRunScriptEndpoint(); + const actionRequestBody = useMemo<undefined | RunScriptActionRequestBody>(() => { + const { endpointId, agentType } = command.commandDefinition?.meta ?? {}; + + if (!endpointId) { + return; + } + return { + agent_type: agentType, + endpoint_ids: [endpointId], + parameters: { + raw: command.args.args.Raw?.[0], + hostPath: command.args.args.HostPath?.[0], + cloudFile: command.args.args.CloudFile?.[0], + commandLine: command.args.args.CommandLine?.[0], + timeout: command.args.args.Timeout?.[0], + }, + comment: command.args.args?.comment?.[0], + }; + }, [command]); + + const { result, actionDetails: completedActionDetails } = useConsoleActionSubmitter< + RunScriptActionRequestBody, + ResponseActionRunScriptOutputContent, + ResponseActionRunScriptParameters + >({ + ResultComponent, + setStore, + store, + status, + setStatus, + actionCreator, + actionRequestBody, + dataTestSubj: 'runscript', + }); + + if (!completedActionDetails || !completedActionDetails.wasSuccessful) { + return result; + } + + return ( + <ResultComponent + data-test-subj="executeSuccess" + showAs="success" + title={i18n.translate( + 'xpack.securitySolution.endpointResponseActions.runScriptAction.successTitle', + { defaultMessage: 'RunScript was successful.' } + )} + > + <ExecuteActionHostResponse + action={completedActionDetails} + canAccessFileDownloadLink={true} + agentId={command.commandDefinition?.meta?.endpointId} + textSize="s" + data-test-subj="console" + hideFile={true} + hideContext={true} + /> + </ResultComponent> + ); +}); +RunScriptActionResult.displayName = 'RunScriptActionResult'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts b/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts index 8c99186f69d9..22d138dea358 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts @@ -6,6 +6,7 @@ */ import { i18n } from '@kbn/i18n'; +import { RunScriptActionResult } from '../command_render_components/run_script_action'; import type { CommandArgDefinition } from '../../console/types'; import { isAgentTypeAndActionSupported } from '../../../../common/lib/endpoint'; import { getRbacControl } from '../../../../../common/endpoint/service/response_actions/utils'; @@ -531,14 +532,14 @@ export const getEndpointConsoleCommands = ({ aboutInfo: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.about, isSupported: doesEndpointSupportCommand('runscript'), }), - RenderComponent: () => null, + RenderComponent: RunScriptActionResult, meta: { agentType, endpointId: endpointAgentId, capabilities: endpointCapabilities, privileges: endpointPrivileges, }, - exampleUsage: `runscript --Raw="Get-ChildItem ." --CommandLine=""`, + exampleUsage: `runscript --Raw=\`\`\`Get-ChildItem .\`\`\` --CommandLine=""`, helpUsage: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.helpUsage, exampleInstruction: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.about, validate: capabilitiesAndPrivilegesValidator(agentType), diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx b/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx index cba1d0aee41b..53f2c89ac84f 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx @@ -23,6 +23,7 @@ import { isExecuteAction, isGetFileAction, isProcessesAction, + isRunScriptAction, isUploadAction, } from '../../../../../common/endpoint/service/response_actions/type_guards'; import { EndpointUploadActionResult } from '../../endpoint_upload_action_result'; @@ -209,6 +210,30 @@ const OutputContent = memo<{ ); } + if (isRunScriptAction(action)) { + return ( + <EuiFlexGroup direction="column" data-test-subj={getTestId('runScriptDetails')}> + {action.agents.map((agentId) => ( + <div key={agentId}> + {OUTPUT_MESSAGES.wasSuccessful(command)} + <ExecuteActionHostResponse + action={action} + agentId={agentId} + canAccessFileDownloadLink={ + canAccessEndpointActionsLogManagement || canReadActionsLogManagement + } + textSize="xs" + data-test-subj={getTestId('actionsLogTray')} + hideFile={true} + hideContext={true} + /> + </div> + ))} + </EuiFlexGroup> + ); + } + + // CrowdStrike Isolate/Release actions if (action.agentType === 'crowdstrike') { return <>{OUTPUT_MESSAGES.submittedSuccessfully(command)}</>; } diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/hooks/response_actions/use_send_run_script_endpoint_request.test.ts b/x-pack/solutions/security/plugins/security_solution/public/management/hooks/response_actions/use_send_run_script_endpoint_request.test.ts new file mode 100644 index 000000000000..39eb3728162d --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/management/hooks/response_actions/use_send_run_script_endpoint_request.test.ts @@ -0,0 +1,81 @@ +/* + * 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 { useMutation as _useMutation } from '@tanstack/react-query'; +import type { AppContextTestRender } from '../../../common/mock/endpoint'; +import type { RenderHookResult } from '@testing-library/react'; +import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; +import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; +import { RUN_SCRIPT_ROUTE } from '../../../../common/endpoint/constants'; +import type { RunScriptActionRequestBody } from '../../../../common/api/endpoint'; +import type { + RunScriptRequestCustomOptions, + UseSendRunScriptRequestResult, +} from './use_send_run_script_endpoint_request'; +import { useSendRunScriptEndpoint } from './use_send_run_script_endpoint_request'; + +const useMutationMock = _useMutation as jest.Mock; + +jest.mock('@tanstack/react-query', () => { + const actualReactQueryModule = jest.requireActual('@tanstack/react-query'); + + return { + ...actualReactQueryModule, + useMutation: jest.fn((...args) => actualReactQueryModule.useMutation(...args)), + }; +}); + +const runScriptPayload: RunScriptActionRequestBody = { + endpoint_ids: ['test-endpoint-id'], + agent_type: 'crowdstrike', + parameters: { raw: 'ls' }, +}; + +describe('When using the `useSendRunScriptRequest()` hook', () => { + let customOptions: RunScriptRequestCustomOptions; + let http: AppContextTestRender['coreStart']['http']; + let apiMocks: ReturnType<typeof responseActionsHttpMocks>; + let renderHook: () => RenderHookResult< + UseSendRunScriptRequestResult, + RunScriptRequestCustomOptions + >; + + beforeEach(() => { + const testContext = createAppRootMockRenderer(); + + http = testContext.coreStart.http; + apiMocks = responseActionsHttpMocks(http); + customOptions = {}; + + renderHook = () => { + return testContext.renderHook(() => useSendRunScriptEndpoint(customOptions)); + }; + }); + + it('should call the `runScript` API with correct payload', async () => { + const { + result: { + current: { mutateAsync }, + }, + } = renderHook(); + await mutateAsync(runScriptPayload); + + expect(apiMocks.responseProvider.runscript).toHaveBeenCalledWith({ + body: JSON.stringify(runScriptPayload), + path: RUN_SCRIPT_ROUTE, + version: '2023-10-31', + }); + }); + + it('should allow custom options to be passed to ReactQuery', async () => { + customOptions.mutationKey = ['pqr-abc']; + customOptions.cacheTime = 10; + renderHook(); + + expect(useMutationMock).toHaveBeenCalledWith(expect.any(Function), customOptions); + }); +}); diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/hooks/response_actions/use_send_run_script_endpoint_request.ts b/x-pack/solutions/security/plugins/security_solution/public/management/hooks/response_actions/use_send_run_script_endpoint_request.ts new file mode 100644 index 000000000000..f4dd937fe300 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/management/hooks/response_actions/use_send_run_script_endpoint_request.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { useMutation } from '@tanstack/react-query'; +import type { RunScriptActionRequestBody } from '../../../../common/api/endpoint'; +import { KibanaServices } from '../../../common/lib/kibana'; +import { RUN_SCRIPT_ROUTE } from '../../../../common/endpoint/constants'; +import type { ResponseActionApiResponse } from '../../../../common/endpoint/types'; + +export type RunScriptRequestCustomOptions = UseMutationOptions< + ResponseActionApiResponse, + IHttpFetchError, + RunScriptActionRequestBody +>; + +export type UseSendRunScriptRequestResult = UseMutationResult< + ResponseActionApiResponse, + IHttpFetchError, + RunScriptActionRequestBody +>; + +export const useSendRunScriptEndpoint = ( + options?: RunScriptRequestCustomOptions +): UseSendRunScriptRequestResult => { + return useMutation<ResponseActionApiResponse, IHttpFetchError, RunScriptActionRequestBody>( + (runScriptActionReqBody) => { + return KibanaServices.get().http.post<ResponseActionApiResponse>(RUN_SCRIPT_ROUTE, { + body: JSON.stringify(runScriptActionReqBody), + version: '2023-10-31', + }); + }, + options + ); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/mocks/response_actions_http_mocks.ts b/x-pack/solutions/security/plugins/security_solution/public/management/mocks/response_actions_http_mocks.ts index 256484f8d0e9..e8307895c3c4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/mocks/response_actions_http_mocks.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/management/mocks/response_actions_http_mocks.ts @@ -17,6 +17,7 @@ import { GET_PROCESSES_ROUTE, ISOLATE_HOST_ROUTE_V2, KILL_PROCESS_ROUTE, + RUN_SCRIPT_ROUTE, SCAN_ROUTE, SUSPEND_PROCESS_ROUTE, UNISOLATE_HOST_ROUTE_V2, @@ -42,6 +43,8 @@ import type { ResponseActionScanParameters, ResponseActionUploadOutputContent, ResponseActionUploadParameters, + ResponseActionRunScriptOutputContent, + ResponseActionRunScriptParameters, } from '../../../common/endpoint/types'; export type ResponseActionsHttpMocksInterface = ResponseProvidersInterface<{ @@ -73,6 +76,8 @@ export type ResponseActionsHttpMocksInterface = ResponseProvidersInterface<{ >; scan: () => ActionDetailsApiResponse<ResponseActionScanOutputContent>; + + runscript: () => ActionDetailsApiResponse<ResponseActionRunScriptOutputContent>; }>; export const responseActionsHttpMocks = httpHandlerMockFactory<ResponseActionsHttpMocksInterface>([ @@ -273,6 +278,25 @@ export const responseActionsHttpMocks = httpHandlerMockFactory<ResponseActionsHt command: 'scan', }); + return { data: response }; + }, + }, + { + id: 'runscript', + path: RUN_SCRIPT_ROUTE, + method: 'post', + handler: (): ActionDetailsApiResponse< + ResponseActionRunScriptOutputContent, + ResponseActionRunScriptParameters + > => { + const generator = new EndpointActionGenerator('seed'); + const response = generator.generateActionDetails< + ResponseActionRunScriptOutputContent, + ResponseActionRunScriptParameters + >({ + command: 'runscript', + }); + return { data: response }; }, }, diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/hooks/use_url_detail.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/hooks/use_url_detail.ts index 387e9d66865b..444aa7831802 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/hooks/use_url_detail.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/hooks/use_url_detail.ts @@ -28,7 +28,7 @@ const getCardHash = (cardId: OnboardingCardId | null) => (cardId ? `#${cardId}` * This hook manages the expanded card id state in the LocalStorage and the hash in the URL. */ export const useUrlDetail = () => { - const { spaceId, telemetry } = useOnboardingContext(); + const { config, spaceId, telemetry } = useOnboardingContext(); const topicId = useTopicId(); const [storedUrlDetail, setStoredUrlDetail] = useStoredUrlDetails(spaceId); @@ -56,6 +56,14 @@ export const useUrlDetail = () => { const syncUrlDetails = useCallback( (pathTopicId: OnboardingTopicId | null, hashCardId: OnboardingCardId | null) => { + if (storedUrlDetail) { + // If the stored topic is not valid, clear it + const [storedTopicId] = storedUrlDetail.split('#'); + if (storedTopicId && !config.has(storedTopicId as OnboardingTopicId)) { + setStoredUrlDetail(null); + return; + } + } const urlDetail = `${pathTopicId || ''}${hashCardId ? `#${hashCardId}` : ''}`; if (urlDetail && urlDetail !== storedUrlDetail) { if (hashCardId) { @@ -67,7 +75,7 @@ export const useUrlDetail = () => { navigateTo({ deepLinkId: SecurityPageName.landing, path: storedUrlDetail }); } }, - [navigateTo, setStoredUrlDetail, storedUrlDetail, telemetry] + [config, navigateTo, setStoredUrlDetail, storedUrlDetail, telemetry] ); return { setTopicDetail, setCardDetail, syncUrlDetails }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/ai_connector/ai_connector_card.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/ai_connector/ai_connector_card.tsx index e42834e85d48..1786c9cbee85 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/ai_connector/ai_connector_card.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/ai_connector/ai_connector_card.tsx @@ -7,6 +7,7 @@ import React, { useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { CenteredLoadingSpinner } from '../../../../../../common/components/centered_loading_spinner'; import { useKibana } from '../../../../../../common/lib/kibana/kibana_react'; import { useDefinedLocalStorage } from '../../../../hooks/use_stored_state'; import type { OnboardingCardComponent } from '../../../../../types'; @@ -35,9 +36,15 @@ export const AIConnectorCard: OnboardingCardComponent<AIConnectorCardMetadata> = [setComplete, setStoredConnectorId] ); - const connectors = checkCompleteMetadata?.connectors; - const canExecuteConnectors = checkCompleteMetadata?.canExecuteConnectors; - const canCreateConnectors = checkCompleteMetadata?.canCreateConnectors; + if (!checkCompleteMetadata) { + return ( + <OnboardingCardContentPanel> + <CenteredLoadingSpinner /> + </OnboardingCardContentPanel> + ); + } + + const { connectors, canExecuteConnectors, canCreateConnectors } = checkCompleteMetadata; return ( <OnboardingCardContentPanel> diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/context.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/context.tsx deleted file mode 100644 index 49baaba65cac..000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/context.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { createContext, useContext, useMemo, type PropsWithChildren } from 'react'; -import type { RuleMigrationTaskStats } from '../../../../../../../common/siem_migrations/model/rule_migration.gen'; - -interface StartMigrationContextValue { - openFlyout: (migrationStats?: RuleMigrationTaskStats) => void; - closeFlyout: () => void; -} - -const StartMigrationContext = createContext<StartMigrationContextValue | null>(null); - -export const StartMigrationContextProvider: React.FC< - PropsWithChildren<StartMigrationContextValue> -> = React.memo(({ children, openFlyout, closeFlyout }) => { - const value = useMemo<StartMigrationContextValue>( - () => ({ openFlyout, closeFlyout }), - [openFlyout, closeFlyout] - ); - return <StartMigrationContext.Provider value={value}>{children}</StartMigrationContext.Provider>; -}); -StartMigrationContextProvider.displayName = 'StartMigrationContextProvider'; - -export const useStartMigrationContext = (): StartMigrationContextValue => { - const context = useContext(StartMigrationContext); - if (context == null) { - throw new Error('useStartMigrationContext must be used within a StartMigrationContextProvider'); - } - return context; -}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/migration_progress_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/migration_progress_panel.tsx deleted file mode 100644 index 0527e1cfbdf1..000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/migration_progress_panel.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, { useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiText, EuiPanel, EuiProgress } from '@elastic/eui'; -import type { RuleMigrationStats } from '../../../../../../../siem_migrations/rules/types'; -import * as i18n from '../translations'; -import { TITLE_CLASS_NAME } from '../start_migration_card.styles'; - -export interface MigrationProgressPanelProps { - migrationStats: RuleMigrationStats; -} -export const MigrationProgressPanel = React.memo<MigrationProgressPanelProps>( - ({ migrationStats }) => { - const progressValue = useMemo(() => { - const finished = migrationStats.rules.completed + migrationStats.rules.failed; - return (finished / migrationStats.rules.total) * 100; - }, [migrationStats.rules]); - - return ( - <EuiPanel hasShadow={false} hasBorder paddingSize="m"> - <EuiFlexGroup direction="column"> - <EuiFlexItem grow={false}> - <EuiText size="s" className={TITLE_CLASS_NAME}> - <p>{i18n.START_MIGRATION_CARD_MIGRATION_TITLE(migrationStats.number)}</p> - </EuiText> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiText size="s"> - <p>{i18n.START_MIGRATION_CARD_PROGRESS_DESCRIPTION}</p> - </EuiText> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiProgress value={progressValue} max={100} color="success" /> - </EuiFlexItem> - </EuiFlexGroup> - </EuiPanel> - ); - } -); -MigrationProgressPanel.displayName = 'MigrationProgressPanel'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/migration_ready_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/migration_ready_panel.tsx deleted file mode 100644 index 8603511fa2d6..000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/migration_ready_panel.tsx +++ /dev/null @@ -1,68 +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 } from 'react'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiText, - EuiButton, - EuiButtonEmpty, - EuiPanel, -} from '@elastic/eui'; -import { useStartMigration } from '../../../../../../../siem_migrations/rules/service/hooks/use_start_migration'; -import type { RuleMigrationStats } from '../../../../../../../siem_migrations/rules/types'; -import * as i18n from '../translations'; -import { useStartMigrationContext } from '../context'; -import { TITLE_CLASS_NAME } from '../start_migration_card.styles'; - -export interface MigrationReadyPanelProps { - migrationStats: RuleMigrationStats; -} -export const MigrationReadyPanel = React.memo<MigrationReadyPanelProps>(({ migrationStats }) => { - const { openFlyout } = useStartMigrationContext(); - const onOpenFlyout = useCallback<React.MouseEventHandler>(() => { - openFlyout(migrationStats); - }, [openFlyout, migrationStats]); - - const { startMigration, isLoading } = useStartMigration(); - const onStartMigration = useCallback(() => { - startMigration(migrationStats.id); - }, [migrationStats.id, startMigration]); - - return ( - <EuiPanel hasShadow={false} hasBorder paddingSize="m"> - <EuiFlexGroup direction="row" alignItems="center" gutterSize="m"> - <EuiFlexItem> - <EuiFlexGroup direction="column" alignItems="flexStart" gutterSize="s"> - <EuiFlexItem grow={false}> - <EuiText size="s" className={TITLE_CLASS_NAME}> - <p>{i18n.START_MIGRATION_CARD_MIGRATION_TITLE(migrationStats.number)}</p> - </EuiText> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiText size="s" color="subdued"> - <p>{i18n.START_MIGRATION_CARD_MIGRATION_READY_DESCRIPTION}</p> - </EuiText> - </EuiFlexItem> - </EuiFlexGroup> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiButtonEmpty onClick={onStartMigration} isLoading={isLoading}> - {i18n.START_MIGRATION_CARD_TRANSLATE_BUTTON} - </EuiButtonEmpty> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiButton iconType="download" iconSide="right" onClick={onOpenFlyout}> - {i18n.START_MIGRATION_CARD_UPLOAD_MACROS_BUTTON} - </EuiButton> - </EuiFlexItem> - </EuiFlexGroup> - </EuiPanel> - ); -}); -MigrationReadyPanel.displayName = 'MigrationReadyPanel'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/migration_result_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/migration_result_panel.tsx deleted file mode 100644 index b73b3cc8b492..000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/migration_result_panel.tsx +++ /dev/null @@ -1,87 +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 moment from 'moment'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiText, - EuiPanel, - EuiHorizontalRule, - EuiIcon, -} from '@elastic/eui'; -import { SecurityPageName } from '@kbn/security-solution-navigation'; -import { AssistantAvatar } from '@kbn/elastic-assistant/impl/assistant/assistant_avatar/assistant_avatar'; -import { SecuritySolutionLinkButton } from '../../../../../../../common/components/links'; -import type { RuleMigrationStats } from '../../../../../../../siem_migrations/rules/types'; -import * as i18n from '../translations'; -import { TITLE_CLASS_NAME } from '../start_migration_card.styles'; - -export interface MigrationResultPanelProps { - migrationStats: RuleMigrationStats; -} -export const MigrationResultPanel = React.memo<MigrationResultPanelProps>(({ migrationStats }) => { - return ( - <EuiPanel hasShadow={false} hasBorder paddingSize="none"> - <EuiPanel hasShadow={false} hasBorder={false} paddingSize="m"> - <EuiFlexGroup direction="column" alignItems="flexStart" gutterSize="xs"> - <EuiFlexItem grow={false}> - <EuiText size="s" className={TITLE_CLASS_NAME}> - <p>{i18n.START_MIGRATION_CARD_RESULT_TITLE(migrationStats.number)}</p> - </EuiText> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiText size="s" color="subdued"> - <p> - {i18n.START_MIGRATION_CARD_RESULT_DESCRIPTION( - moment(migrationStats.created_at).format('MMMM Do YYYY, h:mm:ss a'), - moment(migrationStats.last_updated_at).fromNow() - )} - </p> - </EuiText> - </EuiFlexItem> - </EuiFlexGroup> - </EuiPanel> - <EuiHorizontalRule margin="none" /> - <EuiPanel hasShadow={false} hasBorder={false} paddingSize="m"> - <EuiFlexGroup direction="column" alignItems="stretch" gutterSize="m"> - <EuiFlexItem grow={false}> - <EuiFlexGroup direction="row" alignItems="center" gutterSize="s"> - <EuiFlexItem grow={false}> - <EuiIcon type={AssistantAvatar} size="m" /> - </EuiFlexItem> - <EuiFlexItem> - <EuiText size="s" className={TITLE_CLASS_NAME}> - <p>{i18n.VIEW_TRANSLATED_RULES_TITLE}</p> - </EuiText> - </EuiFlexItem> - </EuiFlexGroup> - </EuiFlexItem> - <EuiFlexItem> - <EuiPanel hasShadow={false} hasBorder paddingSize="m"> - <EuiFlexGroup direction="column" alignItems="center"> - <EuiFlexItem grow={false}> - <p>{'TODO: chart'}</p> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <SecuritySolutionLinkButton - deepLinkId={SecurityPageName.siemMigrationsRules} - path={migrationStats.id} - > - {i18n.VIEW_TRANSLATED_RULES_BUTTON} - </SecuritySolutionLinkButton> - </EuiFlexItem> - </EuiFlexGroup> - </EuiPanel> - </EuiFlexItem> - </EuiFlexGroup> - </EuiPanel> - </EuiPanel> - ); -}); -MigrationResultPanel.displayName = 'MigrationResultPanel'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/rule_migrations_panels.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/rule_migrations_panels.tsx new file mode 100644 index 000000000000..1dae4d523c95 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/rule_migrations_panels.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { SiemMigrationTaskStatus } from '../../../../../../../common/siem_migrations/constants'; +import type { RuleMigrationStats } from '../../../../../../siem_migrations/rules/types'; +import { UploadRulesPanel } from './upload_rules_panel'; +import { MigrationProgressPanel } from '../../../../../../siem_migrations/rules/components/migration_status_panels/migration_progress_panel'; +import { MigrationResultPanel } from '../../../../../../siem_migrations/rules/components/migration_status_panels/migration_result_panel'; +import { MigrationReadyPanel } from '../../../../../../siem_migrations/rules/components/migration_status_panels/migration_ready_panel'; +import { MissingAIConnectorCallout } from './missing_ai_connector_callout'; + +export interface RuleMigrationsPanelsProps { + migrationsStats: RuleMigrationStats[]; + isConnectorsCardComplete: boolean; + expandConnectorsCard: () => void; +} +export const RuleMigrationsPanels = React.memo<RuleMigrationsPanelsProps>( + ({ migrationsStats, isConnectorsCardComplete, expandConnectorsCard }) => { + if (migrationsStats.length === 0) { + return isConnectorsCardComplete ? ( + <UploadRulesPanel /> + ) : ( + <MissingAIConnectorCallout onExpandAiConnectorsCard={expandConnectorsCard} /> + ); + } + + return ( + <EuiFlexGroup direction="column" gutterSize="m"> + <EuiFlexItem grow={false}> + {isConnectorsCardComplete ? ( + <UploadRulesPanel isUploadMore /> + ) : ( + <MissingAIConnectorCallout onExpandAiConnectorsCard={expandConnectorsCard} /> + )} + </EuiFlexItem> + {migrationsStats.map((migrationStats) => ( + <EuiFlexItem grow={false} key={migrationStats.id}> + {migrationStats.status === SiemMigrationTaskStatus.READY && ( + <MigrationReadyPanel migrationStats={migrationStats} /> + )} + {migrationStats.status === SiemMigrationTaskStatus.RUNNING && ( + <MigrationProgressPanel migrationStats={migrationStats} /> + )} + {migrationStats.status === SiemMigrationTaskStatus.FINISHED && ( + <MigrationResultPanel migrationStats={migrationStats} /> + )} + </EuiFlexItem> + ))} + </EuiFlexGroup> + ); + } +); +RuleMigrationsPanels.displayName = 'RuleMigrationsPanels'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.tsx index a8d7aa78d0c9..baebbde53b4c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/start_migration_card.tsx @@ -5,32 +5,25 @@ * 2.0. */ -import React, { useCallback, useEffect, useState } from 'react'; -import { EuiSpacer, EuiText } from '@elastic/eui'; +import React, { useCallback, useEffect, useMemo } from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { PanelText } from '../../../../../../common/components/panel_text'; +import { RuleMigrationDataInputWrapper } from '../../../../../../siem_migrations/rules/components/data_input_flyout/data_input_wrapper'; import { SiemMigrationTaskStatus } from '../../../../../../../common/siem_migrations/constants'; import { OnboardingCardId } from '../../../../../constants'; -import type { RuleMigrationTaskStats } from '../../../../../../../common/siem_migrations/model/rule_migration.gen'; import { useLatestStats } from '../../../../../../siem_migrations/rules/service/hooks/use_latest_stats'; -import { MigrationDataInputFlyout } from '../../../../../../siem_migrations/rules/components/data_input_flyout'; import { CenteredLoadingSpinner } from '../../../../../../common/components/centered_loading_spinner'; import type { OnboardingCardComponent } from '../../../../../types'; import { OnboardingCardContentPanel } from '../../common/card_content_panel'; -import { UploadRulesPanels } from './upload_rules_panels'; -import { StartMigrationContextProvider } from './context'; +import { RuleMigrationsPanels } from './rule_migrations_panels'; import { useStyles } from './start_migration_card.styles'; import * as i18n from './translations'; -import { MissingAIConnectorCallout } from './missing_ai_connector_callout'; export const StartMigrationCard: OnboardingCardComponent = React.memo( ({ setComplete, isCardComplete, setExpandedCardId }) => { const styles = useStyles(); const { data: migrationsStats, isLoading, refreshStats } = useLatestStats(); - const [isFlyoutOpen, setIsFlyoutOpen] = useState<boolean>(); - const [flyoutMigrationStats, setFlyoutMigrationStats] = useState< - RuleMigrationTaskStats | undefined - >(); - useEffect(() => { // Set card complete if any migration is finished if (!isCardComplete(OnboardingCardId.siemMigrationsStart) && migrationsStats) { @@ -40,44 +33,33 @@ export const StartMigrationCard: OnboardingCardComponent = React.memo( } }, [isCardComplete, migrationsStats, setComplete]); - const closeFlyout = useCallback(() => { - setIsFlyoutOpen(false); - setFlyoutMigrationStats(undefined); - refreshStats(); - }, [refreshStats]); - - const openFlyout = useCallback((migrationStats?: RuleMigrationTaskStats) => { - setFlyoutMigrationStats(migrationStats); - setIsFlyoutOpen(true); - }, []); + const isConnectorsCardComplete = useMemo( + () => isCardComplete(OnboardingCardId.siemMigrationsAiConnectors), + [isCardComplete] + ); - if (!isCardComplete(OnboardingCardId.siemMigrationsAiConnectors)) { - return ( - <MissingAIConnectorCallout - onExpandAiConnectorsCard={() => - setExpandedCardId(OnboardingCardId.siemMigrationsAiConnectors) - } - /> - ); - } + const expandConnectorsCard = useCallback(() => { + setExpandedCardId(OnboardingCardId.siemMigrationsAiConnectors); + }, [setExpandedCardId]); return ( - <StartMigrationContextProvider openFlyout={openFlyout} closeFlyout={closeFlyout}> + <RuleMigrationDataInputWrapper onFlyoutClosed={refreshStats}> <OnboardingCardContentPanel paddingSize="none" className={styles}> {isLoading ? ( <CenteredLoadingSpinner /> ) : ( - <UploadRulesPanels migrationsStats={migrationsStats} /> + <RuleMigrationsPanels + migrationsStats={migrationsStats} + isConnectorsCardComplete={isConnectorsCardComplete} + expandConnectorsCard={expandConnectorsCard} + /> )} <EuiSpacer size="m" /> - <EuiText size="xs" color="subdued"> + <PanelText size="xs" subdued> <p>{i18n.START_MIGRATION_CARD_FOOTER_NOTE}</p> - </EuiText> + </PanelText> </OnboardingCardContentPanel> - {isFlyoutOpen && ( - <MigrationDataInputFlyout onClose={closeFlyout} migrationStats={flyoutMigrationStats} /> - )} - </StartMigrationContextProvider> + </RuleMigrationDataInputWrapper> ); } ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/translations.ts index bdb3f3184254..4073423f1f8a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/translations.ts @@ -55,64 +55,3 @@ export const START_MIGRATION_CARD_UPLOAD_MORE_BUTTON = i18n.translate( 'xpack.securitySolution.onboarding.startMigration.uploadMore.button', { defaultMessage: 'Upload more rules' } ); - -export const START_MIGRATION_CARD_UPLOAD_READ_MORE = i18n.translate( - 'xpack.securitySolution.onboarding.startMigration.upload.readMore', - { defaultMessage: 'Read more about our AI powered translations and other features.' } -); - -export const START_MIGRATION_CARD_UPLOAD_READ_DOCS = i18n.translate( - 'xpack.securitySolution.onboarding.startMigration.upload.readAiDocsLink', - { defaultMessage: 'Read AI docs' } -); - -export const START_MIGRATION_CARD_MIGRATION_READY_DESCRIPTION = i18n.translate( - 'xpack.securitySolution.onboarding.startMigration.ready.description', - { - defaultMessage: - 'Migration is created and ready but the translation has not started yet. You can either upload macros & lookups or start the translation process', - } -); -export const START_MIGRATION_CARD_TRANSLATE_BUTTON = i18n.translate( - 'xpack.securitySolution.onboarding.startMigration.translate.button', - { defaultMessage: 'Start translation' } -); -export const START_MIGRATION_CARD_UPLOAD_MACROS_BUTTON = i18n.translate( - 'xpack.securitySolution.onboarding.startMigration.uploadMacros.button', - { defaultMessage: 'Upload macros' } -); - -export const START_MIGRATION_CARD_MIGRATION_TITLE = (number: number) => - i18n.translate('xpack.securitySolution.onboarding.startMigration.migrationTitle', { - defaultMessage: 'SIEM rules migration #{number}', - values: { number }, - }); - -export const START_MIGRATION_CARD_PROGRESS_DESCRIPTION = i18n.translate( - 'xpack.securitySolution.onboarding.startMigration.progress.description', - { - defaultMessage: `This may take a few minutes & the task will work in the background. Just stay logged in and we'll notify you when done.`, - } -); - -export const START_MIGRATION_CARD_RESULT_TITLE = (number: number) => - i18n.translate('xpack.securitySolution.onboarding.startMigration.result.title', { - defaultMessage: 'SIEM rules migration #{number} complete', - values: { number }, - }); - -export const START_MIGRATION_CARD_RESULT_DESCRIPTION = (createdAt: string, finishedAt: string) => - i18n.translate('xpack.securitySolution.onboarding.startMigration.result.description', { - defaultMessage: 'Export uploaded on {createdAt} and translation finished {finishedAt}.', - values: { createdAt, finishedAt }, - }); - -export const VIEW_TRANSLATED_RULES_TITLE = i18n.translate( - 'xpack.securitySolution.onboarding.startMigration.result.translatedRules.title', - { defaultMessage: 'Translation Summary' } -); - -export const VIEW_TRANSLATED_RULES_BUTTON = i18n.translate( - 'xpack.securitySolution.onboarding.startMigration.result.translatedRules.button', - { defaultMessage: 'View translated rules' } -); diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/upload_rules_panel.styles.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panel.styles.ts similarity index 100% rename from x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/upload_rules_panel.styles.ts rename to x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panel.styles.ts diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/upload_rules_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panel.tsx similarity index 82% rename from x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/upload_rules_panel.tsx rename to x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panel.tsx index edcff3646c5a..1a9bd2d17b94 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/panels/upload_rules_panel.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panel.tsx @@ -15,10 +15,11 @@ import { EuiButtonEmpty, EuiPanel, } from '@elastic/eui'; -import { SiemMigrationsIcon } from '../../../../../../../siem_migrations/common/icon'; -import * as i18n from '../translations'; -import { useStartMigrationContext } from '../context'; -import { TITLE_CLASS_NAME } from '../start_migration_card.styles'; +import { RuleMigrationsReadMore } from '../../../../../../siem_migrations/rules/components/migration_status_panels/read_more'; +import { SiemMigrationsIcon } from '../../../../../../siem_migrations/common/icon'; +import * as i18n from './translations'; +import { TITLE_CLASS_NAME } from './start_migration_card.styles'; +import { useRuleMigrationDataInputContext } from '../../../../../../siem_migrations/rules/components/data_input_flyout/context'; import { useStyles } from './upload_rules_panel.styles'; export interface UploadRulesPanelProps { @@ -26,7 +27,7 @@ export interface UploadRulesPanelProps { } export const UploadRulesPanel = React.memo<UploadRulesPanelProps>(({ isUploadMore = false }) => { const styles = useStyles(isUploadMore); - const { openFlyout } = useStartMigrationContext(); + const { openFlyout } = useRuleMigrationDataInputContext(); const onOpenFlyout = useCallback<React.MouseEventHandler>(() => { openFlyout(); }, [openFlyout]); @@ -55,9 +56,7 @@ export const UploadRulesPanel = React.memo<UploadRulesPanelProps>(({ isUploadMor </EuiText> </EuiFlexItem> <EuiFlexItem grow={false}> - <EuiText size="xs"> - <p>{i18n.START_MIGRATION_CARD_UPLOAD_READ_MORE}</p> - </EuiText> + <RuleMigrationsReadMore /> </EuiFlexItem> </EuiFlexGroup> )} diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panels.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panels.tsx deleted file mode 100644 index 6d011fc5fbb5..000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/start_migration/upload_rules_panels.tsx +++ /dev/null @@ -1,46 +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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { SiemMigrationTaskStatus } from '../../../../../../../common/siem_migrations/constants'; -import type { RuleMigrationStats } from '../../../../../../siem_migrations/rules/types'; -import { UploadRulesPanel } from './panels/upload_rules_panel'; -import { MigrationProgressPanel } from './panels/migration_progress_panel'; -import { MigrationResultPanel } from './panels/migration_result_panel'; -import { MigrationReadyPanel } from './panels/migration_ready_panel'; - -export interface UploadRulesPanelsProps { - migrationsStats: RuleMigrationStats[]; -} -export const UploadRulesPanels = React.memo<UploadRulesPanelsProps>(({ migrationsStats }) => { - if (migrationsStats.length === 0) { - return <UploadRulesPanel />; - } - - return ( - <EuiFlexGroup direction="column" gutterSize="m"> - <EuiFlexItem grow={false}> - <UploadRulesPanel isUploadMore /> - </EuiFlexItem> - {migrationsStats.map((migrationStats) => ( - <EuiFlexItem grow={false} key={migrationStats.id}> - {migrationStats.status === SiemMigrationTaskStatus.READY && ( - <MigrationReadyPanel migrationStats={migrationStats} /> - )} - {migrationStats.status === SiemMigrationTaskStatus.RUNNING && ( - <MigrationProgressPanel migrationStats={migrationStats} /> - )} - {migrationStats.status === SiemMigrationTaskStatus.FINISHED && ( - <MigrationResultPanel migrationStats={migrationStats} /> - )} - </EuiFlexItem> - ))} - </EuiFlexGroup> - ); -}); -UploadRulesPanels.displayName = 'UploadRulesPanels'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/context.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/context.tsx new file mode 100644 index 000000000000..bc37df49415c --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/context.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { createContext, useContext, useMemo, type PropsWithChildren } from 'react'; +import type { RuleMigrationTaskStats } from '../../../../../common/siem_migrations/model/rule_migration.gen'; + +interface RuleMigrationDataInputContextValue { + openFlyout: (migrationStats?: RuleMigrationTaskStats) => void; + closeFlyout: () => void; +} + +const RuleMigrationDataInputContext = createContext<RuleMigrationDataInputContextValue | null>( + null +); + +export const RuleMigrationDataInputContextProvider: React.FC< + PropsWithChildren<RuleMigrationDataInputContextValue> +> = React.memo(({ children, openFlyout, closeFlyout }) => { + const value = useMemo<RuleMigrationDataInputContextValue>( + () => ({ openFlyout, closeFlyout }), + [openFlyout, closeFlyout] + ); + return ( + <RuleMigrationDataInputContext.Provider value={value}> + {children} + </RuleMigrationDataInputContext.Provider> + ); +}); +RuleMigrationDataInputContextProvider.displayName = 'RuleMigrationDataInputContextProvider'; + +export const useRuleMigrationDataInputContext = (): RuleMigrationDataInputContextValue => { + const context = useContext(RuleMigrationDataInputContext); + if (context == null) { + throw new Error( + 'useRuleMigrationDataInputContext must be used within a RuleMigrationDataInputContextProvider' + ); + } + return context; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/data_input_flyout.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/data_input_flyout.tsx index ffc40c59d495..9062e3a6b21e 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/data_input_flyout.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/data_input_flyout.tsx @@ -15,6 +15,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiButton, + EuiButtonEmpty, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { @@ -23,8 +24,9 @@ import type { } from '../../../../../common/siem_migrations/model/rule_migration.gen'; import { RulesDataInput } from './steps/rules/rules_data_input'; import { useStartMigration } from '../../service/hooks/use_start_migration'; -import { DataInputStep } from './types'; +import { DataInputStep } from './steps/constants'; import { MacrosDataInput } from './steps/macros/macros_data_input'; +import { LookupsDataInput } from './steps/lookups/lookups_data_input'; interface MissingResourcesIndexed { macros: string[]; @@ -84,8 +86,8 @@ export const MigrationDataInputFlyout = React.memo<MigrationDataInputFlyoutProps [] ); - const onMacrosCreated = useCallback(() => { - setDataInputStep(DataInputStep.Lookups); + const onAllLookupsCreated = useCallback(() => { + setDataInputStep(DataInputStep.End); }, []); return ( @@ -121,21 +123,28 @@ export const MigrationDataInputFlyout = React.memo<MigrationDataInputFlyoutProps dataInputStep={dataInputStep} missingMacros={missingResourcesIndexed?.macros} migrationStats={migrationStats} - onMacrosCreated={onMacrosCreated} onMissingResourcesFetched={onMissingResourcesFetched} /> </EuiFlexItem> + <EuiFlexItem> + <LookupsDataInput + dataInputStep={dataInputStep} + missingLookups={missingResourcesIndexed?.lookups} + migrationStats={migrationStats} + onAllLookupsCreated={onAllLookupsCreated} + /> + </EuiFlexItem> </EuiFlexGroup> </EuiFlyoutBody> <EuiFlyoutFooter> <EuiFlexGroup justifyContent="spaceBetween"> <EuiFlexItem grow={false}> - <EuiButton fill onClick={onClose}> + <EuiButtonEmpty onClick={onClose}> <FormattedMessage id="xpack.securitySolution.siemMigrations.rules.dataInputFlyout.closeButton" defaultMessage="Close" /> - </EuiButton> + </EuiButtonEmpty> </EuiFlexItem> <EuiFlexItem grow={false}> <EuiButton diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/data_input_wrapper.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/data_input_wrapper.tsx new file mode 100644 index 000000000000..e1adf1081600 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/data_input_wrapper.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { PropsWithChildren } from 'react'; +import React, { useCallback, useState } from 'react'; +import type { RuleMigrationTaskStats } from '../../../../../common/siem_migrations/model/rule_migration.gen'; +import { RuleMigrationDataInputContextProvider } from './context'; +import { MigrationDataInputFlyout } from './data_input_flyout'; + +interface RuleMigrationDataInputWrapperProps { + onFlyoutClosed: () => void; +} +export const RuleMigrationDataInputWrapper = React.memo< + PropsWithChildren<RuleMigrationDataInputWrapperProps> +>(({ children, onFlyoutClosed }) => { + const [isFlyoutOpen, setIsFlyoutOpen] = useState<boolean>(); + const [flyoutMigrationStats, setFlyoutMigrationStats] = useState< + RuleMigrationTaskStats | undefined + >(); + + const closeFlyout = useCallback(() => { + setIsFlyoutOpen(false); + setFlyoutMigrationStats(undefined); + onFlyoutClosed?.(); + }, [onFlyoutClosed]); + + const openFlyout = useCallback((migrationStats?: RuleMigrationTaskStats) => { + setFlyoutMigrationStats(migrationStats); + setIsFlyoutOpen(true); + }, []); + + return ( + <RuleMigrationDataInputContextProvider openFlyout={openFlyout} closeFlyout={closeFlyout}> + {children} + {isFlyoutOpen && ( + <MigrationDataInputFlyout onClose={closeFlyout} migrationStats={flyoutMigrationStats} /> + )} + </RuleMigrationDataInputContextProvider> + ); +}); +RuleMigrationDataInputWrapper.displayName = 'RuleMigrationDataInputWrapper'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/common/sub_step.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/common/sub_step.tsx new file mode 100644 index 000000000000..bb76ea749e15 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/common/sub_step.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiPanel, EuiSteps, type EuiStepProps } from '@elastic/eui'; +import { css } from '@emotion/css'; +import React from 'react'; + +const style = css` + .euiStep__title { + font-size: 14px; + } +`; + +export const SubSteps = React.memo<{ steps: EuiStepProps[] }>(({ steps }) => { + return ( + <EuiPanel hasShadow={false} paddingSize="xs" className={style}> + <EuiSteps titleSize="xxs" steps={steps} /> + </EuiPanel> + ); +}); +SubSteps.displayName = 'SubSteps'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/common/sub_step_wrapper.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/common/sub_step_wrapper.tsx deleted file mode 100644 index fc0bd0e8c3b4..000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/common/sub_step_wrapper.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 { EuiPanel } from '@elastic/eui'; -import { css } from '@emotion/css'; -import type { PropsWithChildren } from 'react'; -import React from 'react'; - -const style = css` - .euiStep__title { - font-size: 14px; - } -`; - -export const SubStepsWrapper = React.memo<PropsWithChildren<{}>>(({ children }) => { - return ( - <EuiPanel hasShadow={false} paddingSize="xs" className={style}> - {children} - </EuiPanel> - ); -}); -SubStepsWrapper.displayName = 'SubStepsWrapper'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/common/use_parse_file_input.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/common/use_parse_file_input.ts index b99cf826194f..54622191b6d6 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/common/use_parse_file_input.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/common/use_parse_file_input.ts @@ -26,7 +26,7 @@ export const useParseFileInput = (onFileParsed: OnFileParsed) => { setError(undefined); - const rulesFile = files[0]; + const file = files[0]; const reader = new FileReader(); reader.onloadstart = () => setIsParsing(true); @@ -68,7 +68,7 @@ export const useParseFileInput = (onFileParsed: OnFileParsed) => { reader.onerror = handleReaderError; reader.onabort = handleReaderError; - reader.readAsText(rulesFile); + reader.readAsText(file); }, [onFileParsed] ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/constants.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/constants.ts new file mode 100644 index 000000000000..c0586108b0a1 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/constants.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 DataInputStep { + Rules = 1, + Macros = 2, + Lookups = 3, + End = 10, +} diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/lookups_data_input.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/lookups_data_input.tsx new file mode 100644 index 000000000000..a8fca750ce5d --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/lookups_data_input.tsx @@ -0,0 +1,143 @@ +/* + * 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 { EuiStepProps } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiStepNumber, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { EMPTY_RESOURCE_PLACEHOLDER } from '../../../../../../../common/siem_migrations/constants'; +import type { + RuleMigrationResourceData, + RuleMigrationTaskStats, +} from '../../../../../../../common/siem_migrations/model/rule_migration.gen'; +import type { OnResourcesCreated } from '../../types'; +import { getStatus } from '../common/get_status'; +import * as i18n from './translations'; +import { DataInputStep } from '../constants'; +import { SubSteps } from '../common/sub_step'; +import { useMissingLookupsListStep } from './sub_steps/missing_lookups_list'; +import { useLookupsFileUploadStep } from './sub_steps/lookups_file_upload'; + +export type UploadedLookups = Record<string, string>; +export type AddUploadedLookups = (lookups: RuleMigrationResourceData[]) => void; + +interface LookupsDataInputSubStepsProps { + migrationStats: RuleMigrationTaskStats; + missingLookups: string[]; + onAllLookupsCreated: OnResourcesCreated; +} +interface LookupsDataInputProps + extends Omit<LookupsDataInputSubStepsProps, 'migrationStats' | 'missingLookups'> { + dataInputStep: DataInputStep; + migrationStats?: RuleMigrationTaskStats; + missingLookups?: string[]; +} +export const LookupsDataInput = React.memo<LookupsDataInputProps>( + ({ dataInputStep, migrationStats, missingLookups, onAllLookupsCreated }) => { + const dataInputStatus = useMemo( + () => getStatus(DataInputStep.Lookups, dataInputStep), + [dataInputStep] + ); + + return ( + <EuiPanel hasShadow={false} hasBorder> + <EuiFlexGroup direction="column"> + <EuiFlexItem> + <EuiFlexGroup direction="row" justifyContent="center" gutterSize="m"> + <EuiFlexItem grow={false}> + <EuiStepNumber + titleSize="xs" + number={DataInputStep.Lookups} + status={dataInputStatus} + /> + </EuiFlexItem> + <EuiFlexItem> + <EuiTitle size="xs"> + <b>{i18n.LOOKUPS_DATA_INPUT_TITLE}</b> + </EuiTitle> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlexItem> + {dataInputStatus === 'current' && migrationStats && missingLookups && ( + <> + <EuiFlexItem> + <EuiText size="s" color="subdued"> + {i18n.LOOKUPS_DATA_INPUT_DESCRIPTION} + </EuiText> + </EuiFlexItem> + <EuiFlexItem> + <LookupsDataInputSubSteps + migrationStats={migrationStats} + missingLookups={missingLookups} + onAllLookupsCreated={onAllLookupsCreated} + /> + </EuiFlexItem> + </> + )} + </EuiFlexGroup> + </EuiPanel> + ); + } +); +LookupsDataInput.displayName = 'LookupsDataInput'; + +const END = 10 as const; +type SubStep = 1 | 2 | typeof END; +export const LookupsDataInputSubSteps = React.memo<LookupsDataInputSubStepsProps>( + ({ migrationStats, missingLookups, onAllLookupsCreated }) => { + const [subStep, setSubStep] = useState<SubStep>(1); + const [uploadedLookups, setUploadedLookups] = useState<UploadedLookups>({}); + + const addUploadedLookups = useCallback<AddUploadedLookups>((lookups) => { + setUploadedLookups((prevUploadedLookups) => ({ + ...prevUploadedLookups, + ...Object.fromEntries( + lookups.map((lookup) => [lookup.name, lookup.content ?? EMPTY_RESOURCE_PLACEHOLDER]) + ), + })); + }, []); + + useEffect(() => { + if (missingLookups.every((lookupName) => uploadedLookups[lookupName])) { + setSubStep(END); + onAllLookupsCreated(); + } + }, [uploadedLookups, missingLookups, onAllLookupsCreated]); + + // Copy query step + const onCopied = useCallback(() => { + setSubStep(2); + }, []); + const copyStep = useMissingLookupsListStep({ + status: getStatus(1, subStep), + migrationStats, + missingLookups, + uploadedLookups, + addUploadedLookups, + onCopied, + }); + + // Upload macros step + const uploadStep = useLookupsFileUploadStep({ + status: getStatus(2, subStep), + migrationStats, + missingLookups, + addUploadedLookups, + }); + + const steps = useMemo<EuiStepProps[]>(() => [copyStep, uploadStep], [copyStep, uploadStep]); + + return <SubSteps steps={steps} />; + } +); +LookupsDataInputSubSteps.displayName = 'LookupsDataInputActive'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/lookups_file_upload/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/lookups_file_upload/index.tsx new file mode 100644 index 000000000000..f15413768b9a --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/lookups_file_upload/index.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import type { EuiStepProps, EuiStepStatus } from '@elastic/eui'; +import { useUpsertResources } from '../../../../../../service/hooks/use_upsert_resources'; +import type { + RuleMigrationResourceData, + RuleMigrationTaskStats, +} from '../../../../../../../../../common/siem_migrations/model/rule_migration.gen'; +import type { AddUploadedLookups } from '../../lookups_data_input'; +import * as i18n from './translations'; +import { LookupsFileUpload } from './lookups_file_upload'; + +export interface RulesFileUploadStepProps { + status: EuiStepStatus; + migrationStats: RuleMigrationTaskStats; + missingLookups: string[]; + addUploadedLookups: AddUploadedLookups; +} +export const useLookupsFileUploadStep = ({ + status, + migrationStats, + addUploadedLookups, +}: RulesFileUploadStepProps): EuiStepProps => { + const { upsertResources, isLoading, error } = useUpsertResources(addUploadedLookups); + + const upsertMigrationResources = useCallback( + (lookupsFromFile: RuleMigrationResourceData[]) => { + if (lookupsFromFile.length === 0) { + return; // No lookups provided + } + upsertResources(migrationStats.id, lookupsFromFile); + }, + [upsertResources, migrationStats] + ); + + const uploadStepStatus = useMemo(() => { + if (isLoading) { + return 'loading'; + } + if (error) { + return 'danger'; + } + return status; + }, [isLoading, error, status]); + + return { + title: i18n.LOOKUPS_DATA_INPUT_FILE_UPLOAD_TITLE, + status: uploadStepStatus, + children: ( + <LookupsFileUpload + createResources={upsertMigrationResources} + isLoading={isLoading} + apiError={error?.message} + /> + ), + }; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/lookups_file_upload/lookups_file_upload.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/lookups_file_upload/lookups_file_upload.tsx new file mode 100644 index 000000000000..6ea9562f24cc --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/lookups_file_upload/lookups_file_upload.tsx @@ -0,0 +1,163 @@ +/* + * 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, useMemo, useRef, useState } from 'react'; +import { + EuiButton, + EuiFilePicker, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiText, +} from '@elastic/eui'; +import type { + EuiFilePickerClass, + EuiFilePickerProps, +} from '@elastic/eui/src/components/form/file_picker/file_picker'; +import type { RuleMigrationResourceData } from '../../../../../../../../../common/siem_migrations/model/rule_migration.gen'; +import { FILE_UPLOAD_ERROR } from '../../../../translations'; +import * as i18n from './translations'; + +export interface LookupsFileUploadProps { + createResources: (resources: RuleMigrationResourceData[]) => void; + apiError?: string; + isLoading?: boolean; +} +export const LookupsFileUpload = React.memo<LookupsFileUploadProps>( + ({ createResources, apiError, isLoading }) => { + const [lookupResources, setLookupResources] = useState<RuleMigrationResourceData[]>([]); + const filePickerRef = useRef<EuiFilePickerClass>(null); + + const createLookups = useCallback(() => { + filePickerRef.current?.removeFiles(); + createResources(lookupResources); + }, [createResources, lookupResources]); + + const [isParsing, setIsParsing] = useState<boolean>(false); + const [fileErrors, setErrors] = useState<string[]>([]); + const addError = useCallback((error: string) => { + setErrors((current) => [...current, error]); + }, []); + + const parseFile = useCallback( + async (files: FileList | null) => { + setErrors([]); + setLookupResources([]); + + if (!files?.length) { + return; + } + + const lookups = await Promise.all( + Array.from(files).map((file) => { + return new Promise<RuleMigrationResourceData>((resolve) => { + const reader = new FileReader(); + + reader.onloadstart = () => setIsParsing(true); + reader.onloadend = () => setIsParsing(false); + + reader.onload = function (e) { + // We can safely cast to string since we call `readAsText` to load the file. + const content = e.target?.result as string | undefined; + + if (content == null) { + addError(FILE_UPLOAD_ERROR.CAN_NOT_READ); + return; + } + + if (content === '' && e.loaded > 100000) { + // V8-based browsers can't handle large files and return an empty string + // instead of an error; see https://stackoverflow.com/a/61316641 + addError(FILE_UPLOAD_ERROR.TOO_LARGE_TO_PARSE); + return; + } + + const name = file.name.replace(/\.[^/.]+$/, '').trim(); + resolve({ type: 'list', name, content }); + }; + + const handleReaderError = function () { + const message = reader.error?.message; + if (message) { + addError(FILE_UPLOAD_ERROR.CAN_NOT_READ_WITH_REASON(message)); + } else { + addError(FILE_UPLOAD_ERROR.CAN_NOT_READ); + } + }; + + reader.onerror = handleReaderError; + reader.onabort = handleReaderError; + + reader.readAsText(file); + }); + }) + ).catch((e) => { + addError(e.message); + return []; + }); + // Set the loaded lookups to the state + setLookupResources((current) => [...current, ...lookups]); + }, + [addError] + ); + + const errors = useMemo(() => { + if (apiError) { + return [apiError]; + } + return fileErrors; + }, [apiError, fileErrors]); + + return ( + <EuiFlexGroup direction="column"> + <EuiFlexItem> + <EuiFormRow + helpText={errors.map((error) => ( + <EuiText color="danger" size="xs"> + {error} + </EuiText> + ))} + isInvalid={errors.length > 0} + fullWidth + > + <EuiFilePicker + id="lookupsFilePicker" + ref={filePickerRef as React.Ref<Omit<EuiFilePickerProps, 'stylesMemoizer'>>} + fullWidth + initialPromptText={ + <> + <EuiText size="s" textAlign="center"> + {i18n.LOOKUPS_DATA_INPUT_FILE_UPLOAD_PROMPT} + </EuiText> + </> + } + accept="application/text" + onChange={parseFile} + multiple + display="large" + aria-label="Upload lookups files" + isLoading={isParsing || isLoading} + disabled={isParsing || isLoading} + data-test-subj="lookupsFilePicker" + data-loading={isParsing} + /> + </EuiFormRow> + </EuiFlexItem> + <EuiFlexItem> + <EuiFlexGroup justifyContent="flexEnd" gutterSize="none"> + <EuiFlexItem grow={false}> + <EuiButton onClick={createLookups} isLoading={isLoading} color="success"> + {i18n.LOOKUPS_DATA_INPUT_FILE_UPLOAD_BUTTON} + </EuiButton> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlexItem> + </EuiFlexGroup> + ); + } +); +LookupsFileUpload.displayName = 'LookupsFileUpload'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/lookups_file_upload/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/lookups_file_upload/translations.ts new file mode 100644 index 000000000000..492f51309ca5 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/lookups_file_upload/translations.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 { i18n } from '@kbn/i18n'; + +export const LOOKUPS_DATA_INPUT_FILE_UPLOAD_TITLE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.lookupsFileUpload.title', + { defaultMessage: 'Update your lookups export' } +); +export const LOOKUPS_DATA_INPUT_FILE_UPLOAD_PROMPT = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.lookupsFileUpload.prompt', + { defaultMessage: 'Select or drag and drop the exported lookup files' } +); +export const LOOKUPS_DATA_INPUT_FILE_UPLOAD_NOT_UPLOADED_TITLE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.lookupsFileUpload.notUploadedTitle', + { defaultMessage: 'Lookups not uploaded' } +); +export const LOOKUPS_DATA_INPUT_FILE_UPLOAD_NOT_UPLOADED = (lookupsNames: string) => + i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.lookupsFileUpload.notUploaded', + { + defaultMessage: 'The following files did not match any missing lookup: {lookupsNames}', + values: { lookupsNames }, + } + ); + +export const LOOKUPS_DATA_INPUT_FILE_UPLOAD_BUTTON = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.lookupsFileUpload.button', + { defaultMessage: 'Upload' } +); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/missing_lookups_list/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/missing_lookups_list/index.tsx new file mode 100644 index 000000000000..ae1dbc0a03b3 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/missing_lookups_list/index.tsx @@ -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 React, { useCallback, useMemo } from 'react'; +import type { EuiStepProps, EuiStepStatus } from '@elastic/eui'; +import { EMPTY_RESOURCE_PLACEHOLDER } from '../../../../../../../../../common/siem_migrations/constants'; +import { useUpsertResources } from '../../../../../../service/hooks/use_upsert_resources'; +import type { RuleMigrationTaskStats } from '../../../../../../../../../common/siem_migrations/model/rule_migration.gen'; +import type { UploadedLookups, AddUploadedLookups } from '../../lookups_data_input'; +import * as i18n from './translations'; +import { MissingLookupsList } from './missing_lookups_list'; + +export interface MissingLookupsListStepProps { + status: EuiStepStatus; + migrationStats: RuleMigrationTaskStats; + missingLookups: string[]; + uploadedLookups: UploadedLookups; + addUploadedLookups: AddUploadedLookups; + onCopied: () => void; +} +export const useMissingLookupsListStep = ({ + status, + migrationStats, + missingLookups, + uploadedLookups, + addUploadedLookups, + onCopied, +}: MissingLookupsListStepProps): EuiStepProps => { + const { upsertResources, isLoading, error } = useUpsertResources(addUploadedLookups); + + const clearLookup = useCallback( + (lookupName: string) => { + upsertResources(migrationStats.id, [ + { type: 'list', name: lookupName, content: EMPTY_RESOURCE_PLACEHOLDER }, + ]); + }, + [upsertResources, migrationStats] + ); + + const listStepStatus = useMemo(() => { + if (isLoading) { + return 'loading'; + } + if (error) { + return 'danger'; + } + return status; + }, [isLoading, error, status]); + + return { + title: i18n.LOOKUPS_DATA_INPUT_COPY_TITLE, + status: listStepStatus, + children: ( + <MissingLookupsList + onCopied={onCopied} + missingLookups={missingLookups} + uploadedLookups={uploadedLookups} + clearLookup={clearLookup} + /> + ), + }; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/missing_lookups_list/missing_lookups_list.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/missing_lookups_list/missing_lookups_list.tsx new file mode 100644 index 000000000000..cd462a41bb6c --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/missing_lookups_list/missing_lookups_list.tsx @@ -0,0 +1,161 @@ +/* + * 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, useMemo, useState } from 'react'; +import { css } from '@emotion/css'; +import { + EuiButtonIcon, + EuiCopy, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + EuiSpacer, + EuiText, + EuiToolTip, + useEuiTheme, +} from '@elastic/eui'; +import { EMPTY_RESOURCE_PLACEHOLDER } from '../../../../../../../../../common/siem_migrations/constants'; +import type { UploadedLookups } from '../../lookups_data_input'; +import * as i18n from './translations'; + +const scrollPanelCss = css` + max-height: 200px; + overflow-y: auto; +`; + +interface MissingLookupsListProps { + missingLookups: string[]; + uploadedLookups: UploadedLookups; + clearLookup: (lookupsName: string) => void; + onCopied: () => void; +} +export const MissingLookupsList = React.memo<MissingLookupsListProps>( + ({ missingLookups, uploadedLookups, clearLookup, onCopied }) => { + const { euiTheme } = useEuiTheme(); + return ( + <> + <EuiPanel hasShadow={false} hasBorder className={scrollPanelCss}> + <EuiFlexGroup direction="column" gutterSize="s"> + {missingLookups.map((lookupName) => { + const isMarkedAsEmpty = uploadedLookups[lookupName] === EMPTY_RESOURCE_PLACEHOLDER; + return ( + <EuiFlexItem key={lookupName}> + <EuiFlexGroup + direction="row" + gutterSize="s" + alignItems="center" + justifyContent="flexStart" + > + <EuiFlexItem grow={false}> + {uploadedLookups[lookupName] ? ( + <EuiIcon type="checkInCircleFilled" color={euiTheme.colors.success} /> + ) : ( + <EuiIcon type="dot" /> + )} + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiText + size="s" + style={isMarkedAsEmpty ? { textDecoration: 'line-through' } : {}} + > + {lookupName} + </EuiText> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiCopy textToCopy={lookupName}> + {(copy) => ( + <CopyLookupNameButton + lookupName={lookupName} + onCopied={onCopied} + copy={copy} + /> + )} + </EuiCopy> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <ClearLookupButton + lookupName={lookupName} + clearLookup={clearLookup} + isDisabled={isMarkedAsEmpty} + /> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlexItem> + ); + })} + </EuiFlexGroup> + </EuiPanel> + <EuiSpacer size="s" /> + <EuiText size="s" color="subdued"> + {i18n.MISSING_LOOKUPS_DESCRIPTION} + </EuiText> + </> + ); + } +); +MissingLookupsList.displayName = 'MissingLookupsList'; + +interface CopyLookupNameButtonProps { + lookupName: string; + onCopied: () => void; + copy: () => void; +} +const CopyLookupNameButton = React.memo<CopyLookupNameButtonProps>( + ({ lookupName, onCopied, copy }) => { + const onClick = useCallback(() => { + copy(); + onCopied(); + }, [copy, onCopied]); + return ( + <EuiToolTip content={i18n.COPY_LOOKUP_NAME_TOOLTIP}> + <EuiButtonIcon + onClick={onClick} + iconType="copyClipboard" + color="text" + aria-label={`${i18n.COPY_LOOKUP_NAME_TOOLTIP} ${lookupName}`} + data-test-subj="lookupNameCopy" + /> + </EuiToolTip> + ); + } +); +CopyLookupNameButton.displayName = 'CopyLookupNameButton'; + +interface ClearLookupButtonProps { + lookupName: string; + clearLookup: (lookupName: string) => void; + isDisabled: boolean; +} +const ClearLookupButton = React.memo<ClearLookupButtonProps>( + ({ lookupName, clearLookup, isDisabled: isDisabledDefault }) => { + const [isDisabled, setIsDisabled] = useState(isDisabledDefault); + const onClick = useCallback(() => { + setIsDisabled(true); + clearLookup(lookupName); + }, [clearLookup, lookupName]); + + const button = useMemo( + () => ( + <EuiButtonIcon + onClick={onClick} + iconType="cross" + color="text" + aria-label={i18n.CLEAR_EMPTY_LOOKUP_TOOLTIP} + data-test-subj="lookupNameClear" + isDisabled={isDisabled} + /> + ), + [onClick, isDisabled] + ); + if (isDisabled) { + return button; + } + return <EuiToolTip content={i18n.CLEAR_EMPTY_LOOKUP_TOOLTIP}>{button}</EuiToolTip>; + } +); +ClearLookupButton.displayName = 'ClearLookupButton'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/missing_lookups_list/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/missing_lookups_list/translations.ts new file mode 100644 index 000000000000..123e54118206 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/sub_steps/missing_lookups_list/translations.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 { i18n } from '@kbn/i18n'; + +export const LOOKUPS_DATA_INPUT_COPY_TITLE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.missingLookupsList.title', + { defaultMessage: 'Lookups found in your rules' } +); + +export const MISSING_LOOKUPS_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.missingLookupsList.description', + { + defaultMessage: + 'For your lookups, go to your admin Splunk account and the Search and Reporting app Lookups page. Download the following lookups individually and upload below.', + } +); + +export const COPY_LOOKUP_NAME_TOOLTIP = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.missingLookupsList.copyLookupNameTooltip', + { defaultMessage: 'Copy lookup name' } +); +export const CLEAR_EMPTY_LOOKUP_TOOLTIP = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.missingLookupsList.clearEmptyLookupTooltip', + { defaultMessage: 'Mark the lookup as empty' } +); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/translations.ts new file mode 100644 index 000000000000..970bff4785c8 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/lookups/translations.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 { i18n } from '@kbn/i18n'; + +export const LOOKUPS_DATA_INPUT_TITLE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.title', + { defaultMessage: 'Upload identified lookups' } +); +export const LOOKUPS_DATA_INPUT_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.lookups.description', + { + defaultMessage: `We've also found lookups within your rules. To fully translate those rules containing these lookups, follow the step-by-step guide to export and upload them all.`, + } +); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/macros_data_input.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/macros_data_input.tsx index f19e704b9671..ebbffb6d7f6d 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/macros_data_input.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/macros_data_input.tsx @@ -6,30 +6,21 @@ */ import type { EuiStepProps } from '@elastic/eui'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - EuiStepNumber, - EuiSteps, - EuiTitle, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStepNumber, EuiTitle } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; import type { RuleMigrationTaskStats } from '../../../../../../../common/siem_migrations/model/rule_migration.gen'; -import { SubStepsWrapper } from '../common/sub_step_wrapper'; -import type { OnResourcesCreated, OnMissingResourcesFetched, DataInputStep } from '../../types'; +import type { OnResourcesCreated, OnMissingResourcesFetched } from '../../types'; import { getStatus } from '../common/get_status'; +import * as i18n from './translations'; +import { DataInputStep } from '../constants'; +import { SubSteps } from '../common/sub_step'; import { useCopyExportQueryStep } from './sub_steps/copy_export_query'; import { useMacrosFileUploadStep } from './sub_steps/macros_file_upload'; -import * as i18n from './translations'; import { useCheckResourcesStep } from './sub_steps/check_resources'; -const DataInputStepNumber: DataInputStep = 2; - interface MacrosDataInputSubStepsProps { migrationStats: RuleMigrationTaskStats; missingMacros: string[]; - onMacrosCreated: OnResourcesCreated; onMissingResourcesFetched: OnMissingResourcesFetched; } interface MacrosDataInputProps @@ -39,15 +30,9 @@ interface MacrosDataInputProps missingMacros?: string[]; } export const MacrosDataInput = React.memo<MacrosDataInputProps>( - ({ - dataInputStep, - migrationStats, - missingMacros, - onMacrosCreated, - onMissingResourcesFetched, - }) => { + ({ dataInputStep, migrationStats, missingMacros, onMissingResourcesFetched }) => { const dataInputStatus = useMemo( - () => getStatus(DataInputStepNumber, dataInputStep), + () => getStatus(DataInputStep.Macros, dataInputStep), [dataInputStep] ); @@ -59,7 +44,7 @@ export const MacrosDataInput = React.memo<MacrosDataInputProps>( <EuiFlexItem grow={false}> <EuiStepNumber titleSize="xs" - number={DataInputStepNumber} + number={DataInputStep.Macros} status={dataInputStatus} /> </EuiFlexItem> @@ -75,7 +60,6 @@ export const MacrosDataInput = React.memo<MacrosDataInputProps>( <MacrosDataInputSubSteps migrationStats={migrationStats} missingMacros={missingMacros} - onMacrosCreated={onMacrosCreated} onMissingResourcesFetched={onMissingResourcesFetched} /> </EuiFlexItem> @@ -90,7 +74,7 @@ MacrosDataInput.displayName = 'MacrosDataInput'; const END = 10 as const; type SubStep = 1 | 2 | 3 | typeof END; export const MacrosDataInputSubSteps = React.memo<MacrosDataInputSubStepsProps>( - ({ migrationStats, missingMacros, onMacrosCreated, onMissingResourcesFetched }) => { + ({ migrationStats, missingMacros, onMissingResourcesFetched }) => { const [subStep, setSubStep] = useState<SubStep>(missingMacros.length ? 1 : 3); // Copy query step @@ -101,9 +85,8 @@ export const MacrosDataInputSubSteps = React.memo<MacrosDataInputSubStepsProps>( // Upload macros step const onMacrosCreatedStep = useCallback<OnResourcesCreated>(() => { - onMacrosCreated(); setSubStep(3); - }, [onMacrosCreated]); + }, []); const uploadStep = useMacrosFileUploadStep({ status: getStatus(2, subStep), migrationStats, @@ -130,11 +113,7 @@ export const MacrosDataInputSubSteps = React.memo<MacrosDataInputSubStepsProps>( [copyStep, uploadStep, resourcesStep] ); - return ( - <SubStepsWrapper> - <EuiSteps titleSize="xxs" steps={steps} /> - </SubStepsWrapper> - ); + return <SubSteps steps={steps} />; } ); MacrosDataInputSubSteps.displayName = 'MacrosDataInputActive'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/copy_export_query.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/copy_export_query.tsx index 93f2ce715184..9988be28ee84 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/copy_export_query.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/copy_export_query.tsx @@ -42,7 +42,7 @@ export const CopyExportQuery = React.memo<CopyExportQueryProps>(({ onCopied }) = id="xpack.securitySolution.siemMigrations.rules.dataInputFlyout.rules.copyExportQuery.description" defaultMessage="From you admin Splunk account, go to the {section} app and run the above query. Export your results as {format}." values={{ - section: <b>{i18n.RULES_DATA_INPUT_COPY_DESCRIPTION_SECTION}</b>, + section: <b>{i18n.MACROS_DATA_INPUT_COPY_DESCRIPTION_SECTION}</b>, format: <b>{'JSON'}</b>, }} /> diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/index.tsx index 3d2adcc78857..ac8ff1521c5a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/index.tsx @@ -19,7 +19,7 @@ export const useCopyExportQueryStep = ({ onCopied, }: CopyExportQueryStepProps): EuiStepProps => { return { - title: i18n.RULES_DATA_INPUT_COPY_TITLE, + title: i18n.MACROS_DATA_INPUT_COPY_TITLE, status, children: <CopyExportQuery onCopied={onCopied} />, }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/translations.ts index 71466a54dd13..89364dbfefd3 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/copy_export_query/translations.ts @@ -7,12 +7,12 @@ import { i18n } from '@kbn/i18n'; -export const RULES_DATA_INPUT_COPY_TITLE = i18n.translate( +export const MACROS_DATA_INPUT_COPY_TITLE = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.macros.copyExportQuery.title', { defaultMessage: 'Copy macros query' } ); -export const RULES_DATA_INPUT_COPY_DESCRIPTION_SECTION = i18n.translate( +export const MACROS_DATA_INPUT_COPY_DESCRIPTION_SECTION = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.macros.copyExportQuery.description.section', { defaultMessage: 'Search and Reporting' } ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/index.tsx index f2353e3f0276..3906ac6ca8a0 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/index.tsx @@ -81,7 +81,7 @@ export const useMacrosFileUploadStep = ({ }, [isLoading, error, status]); return { - title: i18n.RULES_DATA_INPUT_FILE_UPLOAD_TITLE, + title: i18n.MACROS_DATA_INPUT_FILE_UPLOAD_TITLE, status: uploadStepStatus, children: ( <MacrosFileUpload diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/macros_file_upload.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/macros_file_upload.tsx index b8d7022d9b45..5cea4afdb853 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/macros_file_upload.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/macros_file_upload.tsx @@ -25,8 +25,8 @@ export const MacrosFileUpload = React.memo<MacrosFileUploadProps>( ({ createResources, apiError, isLoading }) => { const onFileParsed = useCallback( (content: Array<SplunkRow<SplunkMacroResult>>) => { - const rules = content.map(formatMacroRow); - createResources(rules); + const macros = content.map(formatMacroRow); + createResources(macros); }, [createResources] ); @@ -56,14 +56,14 @@ export const MacrosFileUpload = React.memo<MacrosFileUploadProps>( initialPromptText={ <> <EuiText size="s" textAlign="center"> - {i18n.RULES_DATA_INPUT_FILE_UPLOAD_PROMPT} + {i18n.MACROS_DATA_INPUT_FILE_UPLOAD_PROMPT} </EuiText> </> } accept="application/json, application/x-ndjson" onChange={parseFile} display="large" - aria-label="Upload logs sample file" + aria-label="Upload macros file" isLoading={isParsing || isLoading} disabled={isParsing || isLoading} data-test-subj="macrosFilePicker" diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/translations.ts index 25b64787d6dc..6625b271d0f4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/macros/sub_steps/macros_file_upload/translations.ts @@ -7,20 +7,11 @@ import { i18n } from '@kbn/i18n'; -export const RULES_DATA_INPUT_FILE_UPLOAD_TITLE = i18n.translate( +export const MACROS_DATA_INPUT_FILE_UPLOAD_TITLE = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.macros.macrosFileUpload.title', { defaultMessage: 'Update your macros export' } ); -export const RULES_DATA_INPUT_FILE_UPLOAD_PROMPT = i18n.translate( +export const MACROS_DATA_INPUT_FILE_UPLOAD_PROMPT = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.macros.macrosFileUpload.prompt', { defaultMessage: 'Select or drag and drop the exported JSON file' } ); - -export const RULES_DATA_INPUT_CREATE_MIGRATION_SUCCESS = i18n.translate( - 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.macros.macrosFileUpload.createSuccess', - { defaultMessage: 'Macros uploaded successfully' } -); -export const RULES_DATA_INPUT_CREATE_MIGRATION_ERROR = i18n.translate( - 'xpack.securitySolution.siemMigrations.rules.dataInputFlyout.macros.macrosFileUpload.createError', - { defaultMessage: 'Failed to upload macros file' } -); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/rules/rules_data_input.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/rules/rules_data_input.tsx index acc22a030b02..0c919a2db7a5 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/rules/rules_data_input.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/steps/rules/rules_data_input.tsx @@ -6,25 +6,17 @@ */ import type { EuiStepProps } from '@elastic/eui'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - EuiStepNumber, - EuiSteps, - EuiTitle, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStepNumber, EuiTitle } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; import type { RuleMigrationTaskStats } from '../../../../../../../common/siem_migrations/model/rule_migration.gen'; -import { SubStepsWrapper } from '../common/sub_step_wrapper'; -import type { OnMigrationCreated, OnMissingResourcesFetched, DataInputStep } from '../../types'; +import type { OnMigrationCreated, OnMissingResourcesFetched } from '../../types'; +import * as i18n from './translations'; +import { DataInputStep } from '../constants'; +import { getStatus } from '../common/get_status'; +import { SubSteps } from '../common/sub_step'; import { useCopyExportQueryStep } from './sub_steps/copy_export_query'; import { useRulesFileUploadStep } from './sub_steps/rules_file_upload'; -import * as i18n from './translations'; import { useCheckResourcesStep } from './sub_steps/check_resources'; -import { getStatus } from '../common/get_status'; - -const DataInputStepNumber: DataInputStep = 1; interface RulesDataInputSubStepsProps { migrationStats?: RuleMigrationTaskStats; @@ -37,7 +29,7 @@ interface RulesDataInputProps extends RulesDataInputSubStepsProps { export const RulesDataInput = React.memo<RulesDataInputProps>( ({ dataInputStep, migrationStats, onMigrationCreated, onMissingResourcesFetched }) => { const dataInputStatus = useMemo( - () => getStatus(DataInputStepNumber, dataInputStep), + () => getStatus(DataInputStep.Rules, dataInputStep), [dataInputStep] ); @@ -49,7 +41,7 @@ export const RulesDataInput = React.memo<RulesDataInputProps>( <EuiFlexItem grow={false}> <EuiStepNumber titleSize="xs" - number={DataInputStepNumber} + number={DataInputStep.Rules} status={dataInputStatus} /> </EuiFlexItem> @@ -121,11 +113,7 @@ export const RulesDataInputSubSteps = React.memo<RulesDataInputSubStepsProps>( [copyStep, uploadStep, resourcesStep] ); - return ( - <SubStepsWrapper> - <EuiSteps titleSize="xxs" steps={steps} /> - </SubStepsWrapper> - ); + return <SubSteps steps={steps} />; } ); RulesDataInputSubSteps.displayName = 'RulesDataInputActive'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/types.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/types.ts index b293a9394ba5..1e5a8a0f7028 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/types.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/data_input_flyout/types.ts @@ -13,10 +13,3 @@ import type { export type OnMigrationCreated = (migrationStats: RuleMigrationTaskStats) => void; export type OnResourcesCreated = () => void; export type OnMissingResourcesFetched = (missingResources: RuleMigrationResourceData[]) => void; - -export enum DataInputStep { - Rules = 1, - Macros = 2, - Lookups = 3, - End = 10, -} diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_progress_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_progress_panel.tsx new file mode 100644 index 000000000000..0be6fa7b75f5 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_progress_panel.tsx @@ -0,0 +1,86 @@ +/* + * 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 { + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiPanel, + EuiProgress, + EuiLoadingSpinner, + EuiIcon, + EuiSpacer, +} from '@elastic/eui'; +import { AssistantIcon } from '@kbn/ai-assistant-icon'; +import { PanelText } from '../../../../common/components/panel_text'; +import type { RuleMigrationStats } from '../../types'; +import * as i18n from './translations'; +import { RuleMigrationsReadMore } from './read_more'; + +export interface MigrationProgressPanelProps { + migrationStats: RuleMigrationStats; +} +export const MigrationProgressPanel = React.memo<MigrationProgressPanelProps>( + ({ migrationStats }) => { + const finishedCount = migrationStats.rules.completed + migrationStats.rules.failed; + const progressValue = (finishedCount / migrationStats.rules.total) * 100; + + const preparing = migrationStats.rules.pending === migrationStats.rules.total; + + return ( + <EuiPanel hasShadow={false} hasBorder paddingSize="m"> + <EuiFlexGroup direction="column" gutterSize="m"> + <EuiFlexItem grow={false}> + <PanelText size="s" semiBold> + <p>{i18n.RULE_MIGRATION_TITLE(migrationStats.number)}</p> + </PanelText> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiText size="s"> + {i18n.RULE_MIGRATION_PROGRESS_DESCRIPTION(migrationStats.rules.total)} + </EuiText> + </EuiFlexItem> + + <EuiFlexItem grow={false}> + <EuiFlexGroup + direction="row" + justifyContent="flexStart" + alignItems="center" + gutterSize="s" + > + <EuiFlexItem grow={false}> + <EuiIcon size="m" type={AssistantIcon} /> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <PanelText size="s" subdued> + {preparing ? i18n.RULE_MIGRATION_PREPARING : i18n.RULE_MIGRATION_TRANSLATING} + </PanelText> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiLoadingSpinner size="s" /> + </EuiFlexItem> + </EuiFlexGroup> + {!preparing && ( + <> + <EuiProgress + value={progressValue} + valueText={`${Math.floor(progressValue)}%`} + max={100} + color="success" + /> + <EuiSpacer size="xs" /> + <RuleMigrationsReadMore /> + </> + )} + </EuiFlexItem> + </EuiFlexGroup> + </EuiPanel> + ); + } +); +MigrationProgressPanel.displayName = 'MigrationProgressPanel'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_ready_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_ready_panel.tsx new file mode 100644 index 000000000000..3c230cba4c34 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_ready_panel.tsx @@ -0,0 +1,88 @@ +/* + * 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 } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiPanel } from '@elastic/eui'; +import { CenteredLoadingSpinner } from '../../../../common/components/centered_loading_spinner'; +import type { RuleMigrationResourceData } from '../../../../../common/siem_migrations/model/rule_migration.gen'; +import { PanelText } from '../../../../common/components/panel_text'; +import { useStartMigration } from '../../service/hooks/use_start_migration'; +import type { RuleMigrationStats } from '../../types'; +import { useRuleMigrationDataInputContext } from '../data_input_flyout/context'; +import * as i18n from './translations'; +import { useGetMissingResources } from '../../service/hooks/use_get_missing_resources'; + +export interface MigrationReadyPanelProps { + migrationStats: RuleMigrationStats; +} +export const MigrationReadyPanel = React.memo<MigrationReadyPanelProps>(({ migrationStats }) => { + const { openFlyout } = useRuleMigrationDataInputContext(); + const [missingResources, setMissingResources] = React.useState<RuleMigrationResourceData[]>([]); + const { getMissingResources, isLoading } = useGetMissingResources(setMissingResources); + + useEffect(() => { + getMissingResources(migrationStats.id); + }, [getMissingResources, migrationStats.id]); + + const onOpenFlyout = useCallback<React.MouseEventHandler>(() => { + openFlyout(migrationStats); + }, [openFlyout, migrationStats]); + + return ( + <EuiPanel hasShadow={false} hasBorder paddingSize="m"> + <EuiFlexGroup direction="row" gutterSize="m" alignItems="flexEnd"> + <EuiFlexItem> + <EuiFlexGroup direction="column" gutterSize="s"> + <EuiFlexItem> + <PanelText size="s" semiBold> + <p>{i18n.RULE_MIGRATION_TITLE(migrationStats.number)}</p> + </PanelText> + </EuiFlexItem> + <EuiFlexItem> + <PanelText size="s" subdued> + {i18n.RULE_MIGRATION_READY_DESCRIPTION( + migrationStats.rules.total, + !isLoading && missingResources.length > 0 + ? i18n.RULE_MIGRATION_READY_MISSING_RESOURCES + : '' + )} + </PanelText> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlexItem> + {isLoading ? ( + <CenteredLoadingSpinner /> + ) : ( + <EuiFlexItem grow={false}> + {missingResources.length > 0 ? ( + <EuiButton fill iconType="download" iconSide="right" onClick={onOpenFlyout} size="s"> + {i18n.RULE_MIGRATION_UPLOAD_BUTTON} + </EuiButton> + ) : ( + <StartTranslationButton migrationId={migrationStats.id} /> + )} + </EuiFlexItem> + )} + </EuiFlexGroup> + </EuiPanel> + ); +}); +MigrationReadyPanel.displayName = 'MigrationReadyPanel'; + +const StartTranslationButton = React.memo<{ migrationId: string }>(({ migrationId }) => { + const { startMigration, isLoading } = useStartMigration(); + const onStartMigration = useCallback(() => { + startMigration(migrationId); + }, [migrationId, startMigration]); + + return ( + <EuiButton fill onClick={onStartMigration} isLoading={isLoading} size="s"> + {i18n.RULE_MIGRATION_START_TRANSLATION_BUTTON} + </EuiButton> + ); +}); +StartTranslationButton.displayName = 'StartTranslationButton'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_result_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_result_panel.tsx new file mode 100644 index 000000000000..cce11abcd8eb --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/migration_result_panel.tsx @@ -0,0 +1,227 @@ +/* + * 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 moment from 'moment'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiHorizontalRule, + EuiIcon, + EuiBasicTable, + EuiHealth, + EuiText, + useEuiTheme, +} from '@elastic/eui'; +import { Chart, BarSeries, Settings, ScaleType, DARK_THEME, LIGHT_THEME } from '@elastic/charts'; +import { SecurityPageName } from '@kbn/security-solution-navigation'; +import { AssistantIcon } from '@kbn/ai-assistant-icon'; +import { PanelText } from '../../../../common/components/panel_text'; +import { + convertTranslationResultIntoText, + useResultVisColors, +} from '../../utils/translation_results'; +import type { RuleMigrationTranslationStats } from '../../../../../common/siem_migrations/model/rule_migration.gen'; +import { useGetMigrationTranslationStats } from '../../logic/use_get_migration_translation_stats'; +import { CenteredLoadingSpinner } from '../../../../common/components/centered_loading_spinner'; +import { SecuritySolutionLinkButton } from '../../../../common/components/links'; +import type { RuleMigrationStats } from '../../types'; +import { RuleTranslationResult } from '../../../../../common/siem_migrations/constants'; +import * as i18n from './translations'; + +export interface MigrationResultPanelProps { + migrationStats: RuleMigrationStats; +} +export const MigrationResultPanel = React.memo<MigrationResultPanelProps>(({ migrationStats }) => { + const { data: translationStats, isLoading: isLoadingTranslationStats } = + useGetMigrationTranslationStats(migrationStats.id); + return ( + <EuiPanel hasShadow={false} hasBorder paddingSize="none"> + <EuiPanel hasShadow={false} hasBorder={false} paddingSize="m"> + <EuiFlexGroup direction="column" alignItems="flexStart" gutterSize="xs"> + <EuiFlexItem grow={false}> + <PanelText size="s" semiBold> + <p>{i18n.RULE_MIGRATION_COMPLETE_TITLE(migrationStats.number)}</p> + </PanelText> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <PanelText size="s" subdued> + <p> + {i18n.RULE_MIGRATION_COMPLETE_DESCRIPTION( + moment(migrationStats.created_at).format('MMMM Do YYYY, h:mm:ss a'), + moment(migrationStats.last_updated_at).fromNow() + )} + </p> + </PanelText> + </EuiFlexItem> + </EuiFlexGroup> + </EuiPanel> + <EuiHorizontalRule margin="none" /> + <EuiPanel hasShadow={false} hasBorder={false} paddingSize="m"> + <EuiFlexGroup direction="column" alignItems="stretch" gutterSize="m"> + <EuiFlexItem grow={false}> + <EuiFlexGroup direction="row" alignItems="center" gutterSize="s"> + <EuiFlexItem grow={false}> + <EuiIcon type={AssistantIcon} size="m" /> + </EuiFlexItem> + <EuiFlexItem> + <PanelText size="s" semiBold> + <p>{i18n.RULE_MIGRATION_SUMMARY_TITLE}</p> + </PanelText> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlexItem> + <EuiFlexItem> + <EuiPanel hasShadow={false} hasBorder paddingSize="m"> + <EuiFlexGroup direction="column" alignItems="stretch" justifyContent="center"> + <EuiFlexItem> + {isLoadingTranslationStats ? ( + <CenteredLoadingSpinner /> + ) : ( + translationStats && ( + <> + <EuiText size="m" style={{ textAlign: 'center' }}> + <b>{i18n.RULE_MIGRATION_SUMMARY_CHART_TITLE}</b> + </EuiText> + <TranslationResultsChart translationStats={translationStats} /> + <TranslationResultsTable translationStats={translationStats} /> + </> + ) + )} + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiFlexGroup direction="column" alignItems="center"> + <EuiFlexItem> + <SecuritySolutionLinkButton + deepLinkId={SecurityPageName.siemMigrationsRules} + path={migrationStats.id} + > + {i18n.RULE_MIGRATION_VIEW_TRANSLATED_RULES_BUTTON} + </SecuritySolutionLinkButton> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlexItem> + </EuiFlexGroup> + </EuiPanel> + </EuiFlexItem> + </EuiFlexGroup> + {/* TODO: uncomment when retry API is ready <RuleMigrationsUploadMissingPanel migrationStats={migrationStats} spacerSizeTop="s" /> */} + </EuiPanel> + </EuiPanel> + ); +}); +MigrationResultPanel.displayName = 'MigrationResultPanel'; + +const TranslationResultsChart = React.memo<{ + translationStats: RuleMigrationTranslationStats; +}>(({ translationStats }) => { + const { colorMode } = useEuiTheme(); + const translationResultColors = useResultVisColors(); + const data = [ + { + category: 'Results', + type: convertTranslationResultIntoText(RuleTranslationResult.FULL), + value: translationStats.rules.success.result.full, + }, + { + category: 'Results', + type: convertTranslationResultIntoText(RuleTranslationResult.PARTIAL), + value: translationStats.rules.success.result.partial, + }, + { + category: 'Results', + type: convertTranslationResultIntoText(RuleTranslationResult.UNTRANSLATABLE), + value: translationStats.rules.success.result.untranslatable, + }, + { + category: 'Results', + type: i18n.RULE_MIGRATION_TRANSLATION_FAILED, + value: translationStats.rules.failed, + }, + ]; + + const colors = [ + translationResultColors[RuleTranslationResult.FULL], + translationResultColors[RuleTranslationResult.PARTIAL], + translationResultColors[RuleTranslationResult.UNTRANSLATABLE], + translationResultColors.error, + ]; + + return ( + <Chart size={{ height: 130 }}> + <Settings + showLegend={false} + rotation={90} + baseTheme={colorMode === 'DARK' ? DARK_THEME : LIGHT_THEME} + /> + <BarSeries + id="results" + name="Results" + data={data} + xAccessor="category" + yAccessors={['value']} + splitSeriesAccessors={['type']} + stackAccessors={['category']} + xScaleType={ScaleType.Ordinal} + yScaleType={ScaleType.Linear} + color={colors} + /> + </Chart> + ); +}); +TranslationResultsChart.displayName = 'TranslationResultsChart'; + +const TranslationResultsTable = React.memo<{ + translationStats: RuleMigrationTranslationStats; +}>(({ translationStats }) => { + const translationResultColors = useResultVisColors(); + const items = useMemo(() => { + return [ + { + title: convertTranslationResultIntoText(RuleTranslationResult.FULL), + value: translationStats.rules.success.result.full, + color: translationResultColors[RuleTranslationResult.FULL], + }, + { + title: convertTranslationResultIntoText(RuleTranslationResult.PARTIAL), + value: translationStats.rules.success.result.partial, + color: translationResultColors[RuleTranslationResult.PARTIAL], + }, + { + title: convertTranslationResultIntoText(RuleTranslationResult.UNTRANSLATABLE), + value: translationStats.rules.success.result.untranslatable, + color: translationResultColors[RuleTranslationResult.UNTRANSLATABLE], + }, + { + title: i18n.RULE_MIGRATION_TRANSLATION_FAILED, + value: translationStats.rules.failed, + color: translationResultColors.error, + }, + ]; + }, [translationStats, translationResultColors]); + + return ( + <EuiBasicTable + items={items} + compressed + columns={[ + { + field: 'title', + name: i18n.RULE_MIGRATION_TABLE_COLUMN_RESULT, + render: (value: string, { color }) => <EuiHealth color={color}>{value}</EuiHealth>, + }, + { + field: 'value', + name: i18n.RULE_MIGRATION_TABLE_COLUMN_RULES, + align: 'right', + }, + ]} + /> + ); +}); +TranslationResultsTable.displayName = 'TranslationResultsTable'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/read_more.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/read_more.tsx new file mode 100644 index 000000000000..4567026f3cc0 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/read_more.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiLink } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { PanelText } from '../../../../common/components/panel_text'; +import { useKibana } from '../../../../common/lib/kibana/kibana_react'; + +export const RuleMigrationsReadMore = React.memo(() => { + const docLink = useKibana().services.docLinks.links.siem.gettingStarted; + return ( + <PanelText size="xs" subdued> + <p> + <FormattedMessage + id="xpack.securitySolution.siemMigrations.rules.panel.help.readMore" + defaultMessage="Read more about our AI powered translations and other features. {readMore}" + values={{ + readMore: ( + <EuiLink href={docLink} target="_blank"> + <FormattedMessage + id="xpack.securitySolution.siemMigrations.rules.panel.help.readDocs" + defaultMessage="Read AI docs" + /> + </EuiLink> + ), + }} + /> + </p> + </PanelText> + ); +}); +RuleMigrationsReadMore.displayName = 'RuleMigrationsReadMore'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/translations.ts new file mode 100644 index 000000000000..55e73bca32b5 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/translations.ts @@ -0,0 +1,101 @@ +/* + * 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'; + +export const RULE_MIGRATION_READY_DESCRIPTION = ( + totalRules: number, + missingResourcesText: string +) => + i18n.translate('xpack.securitySolution.siemMigrations.rules.panel.ready.description', { + defaultMessage: + 'Migration of {totalRules} rules is created but the translation has not started yet. {missingResourcesText}', + values: { totalRules, missingResourcesText }, + }); +export const RULE_MIGRATION_READY_MISSING_RESOURCES = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.ready.missingResources', + { defaultMessage: 'Upload macros & lookups and start the translation process' } +); + +export const RULE_MIGRATION_START_TRANSLATION_BUTTON = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.translate.button', + { defaultMessage: 'Start translation' } +); +export const RULE_MIGRATION_TITLE = (number: number) => + i18n.translate('xpack.securitySolution.siemMigrations.rules.panel.migrationTitle', { + defaultMessage: 'SIEM rules migration #{number}', + values: { number }, + }); + +export const RULE_MIGRATION_PROGRESS_DESCRIPTION = (totalRules: number) => + i18n.translate('xpack.securitySolution.siemMigrations.rules.panel.progress.description', { + defaultMessage: `Processing migration of {totalRules} rules.`, + values: { totalRules }, + }); +export const RULE_MIGRATION_PREPARING = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.preparing', + { defaultMessage: `Preparing environment for the AI powered translation.` } +); +export const RULE_MIGRATION_TRANSLATING = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.translating', + { defaultMessage: `Translating rules` } +); + +export const RULE_MIGRATION_COMPLETE_TITLE = (number: number) => + i18n.translate('xpack.securitySolution.siemMigrations.rules.panel.result.title', { + defaultMessage: 'SIEM rules migration #{number} complete', + values: { number }, + }); + +export const RULE_MIGRATION_COMPLETE_DESCRIPTION = (createdAt: string, finishedAt: string) => + i18n.translate('xpack.securitySolution.siemMigrations.rules.panel.result.description', { + defaultMessage: 'Export uploaded on {createdAt} and translation finished {finishedAt}.', + values: { createdAt, finishedAt }, + }); + +export const RULE_MIGRATION_SUMMARY_TITLE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.result.summary.title', + { defaultMessage: 'Translation Summary' } +); + +export const RULE_MIGRATION_SUMMARY_CHART_TITLE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.result.summary.chartTitle', + { defaultMessage: 'Rules by translation status' } +); + +export const RULE_MIGRATION_VIEW_TRANSLATED_RULES_BUTTON = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.result.summary.button', + { defaultMessage: 'View translated rules' } +); + +export const RULE_MIGRATION_TRANSLATION_FAILED = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.result.summary.failed', + { defaultMessage: 'Failed' } +); + +export const RULE_MIGRATION_TABLE_COLUMN_RESULT = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.result.summary.tableColumn.result', + { defaultMessage: 'Result' } +); +export const RULE_MIGRATION_TABLE_COLUMN_RULES = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.result.summary.tableColumn.rules', + { defaultMessage: 'Rules' } +); + +export const RULE_MIGRATION_UPLOAD_MISSING_RESOURCES_TITLE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.uploadMissingResources', + { defaultMessage: 'Upload missing Macros and Lookups.' } +); +export const RULE_MIGRATION_UPLOAD_MISSING_RESOURCES_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.uploadMissingResourcesDescription', + { defaultMessage: 'Click upload for step-by-step guidance to finish partially translated rules.' } +); + +export const RULE_MIGRATION_UPLOAD_BUTTON = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.panel.uploadMacros.button', + { defaultMessage: 'Upload' } +); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/upload_missing_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/upload_missing_panel.tsx new file mode 100644 index 000000000000..f1c6bdd71613 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/migration_status_panels/upload_missing_panel.tsx @@ -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 React, { useCallback, useEffect } from 'react'; +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiSpacer, + useEuiTheme, +} from '@elastic/eui'; +import { AssistantIcon } from '@kbn/ai-assistant-icon'; +import type { SpacerSize } from '@elastic/eui/src/components/spacer/spacer'; +import type { RuleMigrationResourceData } from '../../../../../common/siem_migrations/model/rule_migration.gen'; +import { PanelText } from '../../../../common/components/panel_text'; +import { useGetMissingResources } from '../../service/hooks/use_get_missing_resources'; +import * as i18n from './translations'; +import { useRuleMigrationDataInputContext } from '../data_input_flyout/context'; +import type { RuleMigrationStats } from '../../types'; + +interface RuleMigrationsUploadMissingPanelProps { + migrationStats: RuleMigrationStats; + spacerSizeTop?: SpacerSize; +} +export const RuleMigrationsUploadMissingPanel = React.memo<RuleMigrationsUploadMissingPanelProps>( + ({ migrationStats, spacerSizeTop }) => { + const { euiTheme } = useEuiTheme(); + const { openFlyout } = useRuleMigrationDataInputContext(); + const [missingResources, setMissingResources] = React.useState<RuleMigrationResourceData[]>([]); + const { getMissingResources, isLoading } = useGetMissingResources(setMissingResources); + + useEffect(() => { + getMissingResources(migrationStats.id); + }, [getMissingResources, migrationStats.id]); + + const onOpenFlyout = useCallback(() => { + openFlyout(migrationStats); + }, [migrationStats, openFlyout]); + + if (isLoading || missingResources.length === 0) { + return null; + } + return ( + <> + {spacerSizeTop && <EuiSpacer size={spacerSizeTop} />} + <EuiPanel + hasShadow={false} + hasBorder + paddingSize="s" + style={{ backgroundColor: euiTheme.colors.backgroundBasePrimary }} + > + <EuiFlexGroup direction="row" gutterSize="s" alignItems="center"> + <EuiFlexItem grow={false}> + <AssistantIcon /> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <PanelText size="s" semiBold> + {i18n.RULE_MIGRATION_UPLOAD_MISSING_RESOURCES_TITLE} + </PanelText> + </EuiFlexItem> + <EuiFlexItem> + <PanelText size="s" subdued> + {i18n.RULE_MIGRATION_UPLOAD_MISSING_RESOURCES_DESCRIPTION} + </PanelText> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiButton + fill + color="primary" + onClick={onOpenFlyout} + iconType="download" + iconSide="right" + size="s" + > + {i18n.RULE_MIGRATION_UPLOAD_BUTTON} + </EuiButton> + </EuiFlexItem> + </EuiFlexGroup> + </EuiPanel> + </> + ); + } +); +RuleMigrationsUploadMissingPanel.displayName = 'RuleMigrationsUploadMissingPanel'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/index.tsx index 4328e1b888df..9184c48ff75b 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/index.tsx @@ -26,7 +26,7 @@ import * as i18n from './translations'; import { convertTranslationResultIntoColor, convertTranslationResultIntoText, -} from '../../../../utils/helpers'; +} from '../../../../utils/translation_results'; import { TranslationCallOut } from './callout'; interface TranslationTabProps { diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx index 07ba44d4d167..b883934a0bdc 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx @@ -31,7 +31,7 @@ import { useGetMigrationPrebuiltRules } from '../../logic/use_get_migration_preb import * as logicI18n from '../../logic/translations'; import { BulkActions } from './bulk_actions'; import { SearchField } from './search_field'; -import { SiemMigrationRuleTranslationResult } from '../../../../../common/siem_migrations/constants'; +import { RuleTranslationResult } from '../../../../../common/siem_migrations/constants'; import * as i18n from './translations'; const DEFAULT_PAGE_SIZE = 10; @@ -80,10 +80,7 @@ export const MigrationRulesTable: React.FC<MigrationRulesTableProps> = React.mem const tableSelection: EuiTableSelectionType<RuleMigration> = useMemo( () => ({ selectable: (item: RuleMigration) => { - return ( - !item.elastic_rule?.id && - item.translation_result === SiemMigrationRuleTranslationResult.FULL - ); + return !item.elastic_rule?.id && item.translation_result === RuleTranslationResult.FULL; }, selectableMessage: (selectable: boolean, item: RuleMigration) => { if (selectable) { @@ -190,7 +187,7 @@ export const MigrationRulesTable: React.FC<MigrationRulesTableProps> = React.mem const canMigrationRuleBeInstalled = !isLoading && !ruleMigration.elastic_rule?.id && - ruleMigration.translation_result === SiemMigrationRuleTranslationResult.FULL; + ruleMigration.translation_result === RuleTranslationResult.FULL; return ( <EuiFlexGroup> <EuiFlexItem> @@ -271,7 +268,7 @@ export const MigrationRulesTable: React.FC<MigrationRulesTableProps> = React.mem <EuiFlexItem grow={false}> <BulkActions isTableLoading={isLoading} - numberOfTranslatedRules={translationStats?.rules.installable ?? 0} + numberOfTranslatedRules={translationStats?.rules.success.installable ?? 0} numberOfSelectedRules={selectedRuleMigrations.length} installTranslatedRule={installTranslatedRules} installSelectedRule={installSelectedRule} diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.tsx index f1f435c7e14a..867c5034ba9e 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.tsx @@ -6,16 +6,18 @@ */ import React from 'react'; -import { euiLightVars } from '@kbn/ui-theme'; import { EuiFlexGroup, EuiFlexItem, EuiHealth, EuiIcon, EuiToolTip } from '@elastic/eui'; import { css } from '@emotion/css'; +import { RuleTranslationResult } from '../../../../../common/siem_migrations/constants'; +import { + convertTranslationResultIntoText, + useResultVisColors, +} from '../../utils/translation_results'; import { RuleMigrationStatusEnum, type RuleMigration, - type RuleMigrationTranslationResult, } from '../../../../../common/siem_migrations/model/rule_migration.gen'; -import { convertTranslationResultIntoText } from '../../utils/helpers'; import * as i18n from './translations'; const statusTextWrapperClassName = css` @@ -23,13 +25,6 @@ const statusTextWrapperClassName = css` display: inline-grid; `; -const { euiColorVis0, euiColorVis7, euiColorVis9 } = euiLightVars; -const statusToColorMap: Record<RuleMigrationTranslationResult, string> = { - full: euiColorVis0, - partial: euiColorVis7, - untranslatable: euiColorVis9, -}; - interface StatusBadgeProps { migrationRule: RuleMigration; 'data-test-subj'?: string; @@ -37,13 +32,14 @@ interface StatusBadgeProps { export const StatusBadge: React.FC<StatusBadgeProps> = React.memo( ({ migrationRule, 'data-test-subj': dataTestSubj = 'translation-result' }) => { + const colors = useResultVisColors(); // Installed if (migrationRule.elastic_rule?.id) { return ( <EuiToolTip content={i18n.RULE_STATUS_INSTALLED}> <EuiFlexGroup gutterSize="xs" alignItems="center"> <EuiFlexItem grow={false}> - <EuiIcon type="check" color={statusToColorMap.full} /> + <EuiIcon type="check" color={colors[RuleTranslationResult.FULL]} /> </EuiFlexItem> <EuiFlexItem grow={false}>{i18n.RULE_STATUS_INSTALLED}</EuiFlexItem> </EuiFlexGroup> @@ -67,7 +63,7 @@ export const StatusBadge: React.FC<StatusBadgeProps> = React.memo( const translationResult = migrationRule.translation_result ?? 'untranslatable'; const displayValue = convertTranslationResultIntoText(translationResult); - const color = statusToColorMap[translationResult]; + const color = colors[translationResult]; return ( <EuiToolTip content={displayValue}> diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/hooks/use_upsert_resources.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/hooks/use_upsert_resources.ts index eab3888422ba..5ed8541a4af4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/hooks/use_upsert_resources.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/hooks/use_upsert_resources.ts @@ -20,7 +20,7 @@ export type UpsertResources = ( migrationId: string, data: UpsertRuleMigrationResourcesRequestBody ) => void; -export type OnSuccess = () => void; +export type OnSuccess = (data: UpsertRuleMigrationResourcesRequestBody) => void; export const useUpsertResources = (onSuccess: OnSuccess) => { const { siemMigrations, notifications } = useKibana().services; @@ -33,7 +33,7 @@ export const useUpsertResources = (onSuccess: OnSuccess) => { dispatch({ type: 'start' }); await siemMigrations.rules.upsertMigrationResources(migrationId, data); - onSuccess(); + onSuccess(data); dispatch({ type: 'success' }); } catch (err) { const apiError = err.body ?? err; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts index 75b7887db652..83ead556b09c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts @@ -45,7 +45,7 @@ import * as i18n from './translations'; const NAMESPACE_TRACE_OPTIONS_SESSION_STORAGE_KEY = `${DEFAULT_ASSISTANT_NAMESPACE}.${TRACE_OPTIONS_SESSION_STORAGE_KEY}` as const; -const REQUEST_POLLING_INTERVAL_MS = 5000 as const; +const REQUEST_POLLING_INTERVAL_SECONDS = 10 as const; const CREATE_MIGRATION_BODY_BATCH_SIZE = 50 as const; export class SiemRulesMigrationsService { @@ -213,7 +213,7 @@ export class SiemRulesMigrationsService { } } - await new Promise((resolve) => setTimeout(resolve, REQUEST_POLLING_INTERVAL_MS)); + await new Promise((resolve) => setTimeout(resolve, REQUEST_POLLING_INTERVAL_SECONDS * 1000)); } while (pendingMigrationIds.length > 0); } } diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/helpers.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/helpers.tsx deleted file mode 100644 index fe3fbf994507..000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/helpers.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 { - RuleMigrationTranslationResultEnum, - type RuleMigrationTranslationResult, -} from '../../../../common/siem_migrations/model/rule_migration.gen'; -import * as i18n from './translations'; - -export const convertTranslationResultIntoColor = (status?: RuleMigrationTranslationResult) => { - switch (status) { - case RuleMigrationTranslationResultEnum.full: - return 'primary'; - - case RuleMigrationTranslationResultEnum.partial: - return 'warning'; - - case RuleMigrationTranslationResultEnum.untranslatable: - return 'danger'; - - default: - throw new Error(i18n.SIEM_TRANSLATION_RESULT_UNKNOWN_ERROR(status)); - } -}; - -export const convertTranslationResultIntoText = (status?: RuleMigrationTranslationResult) => { - switch (status) { - case RuleMigrationTranslationResultEnum.full: - return i18n.SIEM_TRANSLATION_RESULT_FULL_LABEL; - - case RuleMigrationTranslationResultEnum.partial: - return i18n.SIEM_TRANSLATION_RESULT_PARTIAL_LABEL; - - case RuleMigrationTranslationResultEnum.untranslatable: - return i18n.SIEM_TRANSLATION_RESULT_UNTRANSLATABLE_LABEL; - - default: - throw new Error(i18n.SIEM_TRANSLATION_RESULT_UNKNOWN_ERROR(status)); - } -}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translation_results/index.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translation_results/index.ts new file mode 100644 index 000000000000..d25c252fb8fe --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translation_results/index.ts @@ -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 { useEuiTheme } from '@elastic/eui'; +import { RuleTranslationResult } from '../../../../../common/siem_migrations/constants'; +import type { RuleMigrationTranslationResult } from '../../../../../common/siem_migrations/model/rule_migration.gen'; +import * as i18n from './translations'; + +export const useResultVisColors = () => { + const { euiTheme } = useEuiTheme(); + return { + [RuleTranslationResult.FULL]: euiTheme.colors.vis.euiColorVis0, + [RuleTranslationResult.PARTIAL]: euiTheme.colors.vis.euiColorVis5, + [RuleTranslationResult.UNTRANSLATABLE]: euiTheme.colors.vis.euiColorVis7, + error: euiTheme.colors.vis.euiColorVis9, + }; +}; + +export const convertTranslationResultIntoColor = (status?: RuleMigrationTranslationResult) => { + switch (status) { + case RuleTranslationResult.FULL: + return 'primary'; + case RuleTranslationResult.PARTIAL: + return 'warning'; + case RuleTranslationResult.UNTRANSLATABLE: + return 'danger'; + default: + return 'subdued'; + } +}; + +export const convertTranslationResultIntoText = (status?: RuleMigrationTranslationResult) => { + switch (status) { + case RuleTranslationResult.FULL: + return i18n.SIEM_TRANSLATION_RESULT_FULL_LABEL; + case RuleTranslationResult.PARTIAL: + return i18n.SIEM_TRANSLATION_RESULT_PARTIAL_LABEL; + case RuleTranslationResult.UNTRANSLATABLE: + return i18n.SIEM_TRANSLATION_RESULT_UNTRANSLATABLE_LABEL; + default: + return i18n.SIEM_TRANSLATION_RESULT_UNKNOWN_LABEL; + } +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translation_results/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translation_results/translations.ts new file mode 100644 index 000000000000..f0f38cfc6148 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translation_results/translations.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 { i18n } from '@kbn/i18n'; + +export const SIEM_TRANSLATION_RESULT_FULL_LABEL = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.translationResult.full', + { + defaultMessage: 'Translated', + } +); + +export const SIEM_TRANSLATION_RESULT_PARTIAL_LABEL = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.translationResult.partially', + { + defaultMessage: 'Partially translated', + } +); + +export const SIEM_TRANSLATION_RESULT_UNTRANSLATABLE_LABEL = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.translationResult.untranslatable', + { + defaultMessage: 'Needs manual translation', + } +); + +export const SIEM_TRANSLATION_RESULT_UNKNOWN_LABEL = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.translationResult.unknown', + { + defaultMessage: 'Unknown', + } +); + +export const SIEM_TRANSLATION_RESULT_UNKNOWN_ERROR = (status?: string) => { + return i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.translationResult.unknownError', + { + defaultMessage: 'Unknown translation result status: ({status})', + values: { status }, + } + ); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translations.ts deleted file mode 100644 index 03f76cb83381..000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translations.ts +++ /dev/null @@ -1,46 +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'; - -export const SIEM_TRANSLATION_RESULT_FULL_LABEL = i18n.translate( - 'xpack.securitySolution.siemMigrations.rules.translationResult.full', - { - defaultMessage: 'Translated', - } -); - -export const SIEM_TRANSLATION_RESULT_PARTIAL_LABEL = i18n.translate( - 'xpack.securitySolution.siemMigrations.rules.translationResult.partially', - { - defaultMessage: 'Partially translated', - } -); - -export const SIEM_TRANSLATION_RESULT_UNTRANSLATABLE_LABEL = i18n.translate( - 'xpack.securitySolution.siemMigrations.rules.translationResult.untranslatable', - { - defaultMessage: 'Not translated', - } -); - -export const SIEM_TRANSLATION_RESULT_UNKNOWN_LABEL = i18n.translate( - 'xpack.securitySolution.siemMigrations.rules.translationResult.unknown', - { - defaultMessage: 'Unknown', - } -); - -export const SIEM_TRANSLATION_RESULT_UNKNOWN_ERROR = (status?: string) => { - return i18n.translate( - 'xpack.securitySolution.siemMigrations.rules.translationResult.unknownError', - { - defaultMessage: 'Unknown translation result status: ({status})', - values: { status }, - } - ); -}; diff --git a/x-pack/solutions/security/plugins/security_solution/scripts/endpoint/common/response_actions.ts b/x-pack/solutions/security/plugins/security_solution/scripts/endpoint/common/response_actions.ts index 1987a019f888..d54df140b3b4 100644 --- a/x-pack/solutions/security/plugins/security_solution/scripts/endpoint/common/response_actions.ts +++ b/x-pack/solutions/security/plugins/security_solution/scripts/endpoint/common/response_actions.ts @@ -27,6 +27,7 @@ import type { ResponseActionGetFileParameters, EndpointActionResponseDataOutput, ResponseActionScanOutputContent, + ResponseActionRunScriptOutputContent, } from '../../../common/endpoint/types'; import { getFileDownloadId } from '../../../common/endpoint/service/response_actions/get_file_download_id'; import { @@ -138,6 +139,15 @@ export const sendEndpointActionResponse = async ( .content as unknown as ResponseActionExecuteOutputContent ).stderr = 'execute command timed out'; } + if ( + endpointResponse.EndpointActions.data.command === 'runscript' && + endpointResponse.EndpointActions.data.output + ) { + ( + endpointResponse.EndpointActions.data.output + .content as unknown as ResponseActionRunScriptOutputContent + ).stderr = 'runscript command timed out'; + } } await esClient.index({ diff --git a/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts b/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts index 0c505b12c129..25fcb30be494 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts @@ -11,8 +11,13 @@ import { CROWDSTRIKE_CONNECTOR_ID, } from '@kbn/stack-connectors-plugin/common/crowdstrike/constants'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; -import type { CrowdstrikeBaseApiResponse } from '@kbn/stack-connectors-plugin/common/crowdstrike/types'; +import type { + CrowdstrikeBaseApiResponse, + CrowdStrikeExecuteRTRResponse, +} from '@kbn/stack-connectors-plugin/common/crowdstrike/types'; import { v4 as uuidv4 } from 'uuid'; + +import { mapParametersToCrowdStrikeArguments } from './utils'; import type { CrowdstrikeActionRequestCommonMeta } from '../../../../../../common/endpoint/types/crowdstrike'; import type { CommonResponseActionMethodOptions, @@ -305,15 +310,99 @@ export class CrowdstrikeActionsClient extends ResponseActionsClientImpl { ): Promise< ActionDetails<ResponseActionRunScriptOutputContent, ResponseActionRunScriptParameters> > { - // TODO: just a placeholder for now - return Promise.resolve({ output: 'runscript', code: 200 }) as never as ActionDetails< - ResponseActionRunScriptOutputContent, - ResponseActionRunScriptParameters - >; + const reqIndexOptions: ResponseActionsClientWriteActionRequestToEndpointIndexOptions = { + ...actionRequest, + ...this.getMethodOptions(options), + command: 'runscript', + }; + + let actionResponse: ActionTypeExecutorResult<CrowdStrikeExecuteRTRResponse> | undefined; + if (!reqIndexOptions.error) { + let error = (await this.validateRequest(reqIndexOptions)).error; + if (!error) { + if (!reqIndexOptions.actionId) { + reqIndexOptions.actionId = uuidv4(); + } + + try { + actionResponse = (await this.sendAction(SUB_ACTION.EXECUTE_ADMIN_RTR, { + actionParameters: { comment: this.buildExternalComment(reqIndexOptions) }, + command: mapParametersToCrowdStrikeArguments('runscript', actionRequest.parameters), + endpoint_ids: actionRequest.endpoint_ids, + })) as ActionTypeExecutorResult<CrowdStrikeExecuteRTRResponse>; + } catch (err) { + error = err; + } + } + + reqIndexOptions.error = error?.message; + + if (!this.options.isAutomated && error) { + throw error; + } + } + + const actionRequestDoc = await this.writeActionRequestToEndpointIndex(reqIndexOptions); + + // Ensure actionResponse is assigned before using it + if (actionResponse) { + await this.completeCrowdstrikeBatchAction(actionResponse, actionRequestDoc); + } + + await this.updateCases({ + command: reqIndexOptions.command, + caseIds: reqIndexOptions.case_ids, + alertIds: reqIndexOptions.alert_ids, + actionId: actionRequestDoc.EndpointActions.action_id, + hosts: actionRequest.endpoint_ids.map((agentId) => { + return { + hostId: agentId, + hostname: actionRequestDoc.EndpointActions.data.hosts?.[agentId].name ?? '', + }; + }), + comment: reqIndexOptions.comment, + }); + + return this.fetchActionDetails(actionRequestDoc.EndpointActions.action_id); + } + + private async completeCrowdstrikeBatchAction( + actionResponse: ActionTypeExecutorResult<CrowdStrikeExecuteRTRResponse>, + doc: LogsEndpointAction + ): Promise<void> { + const agentId = doc.agent.id as string; + const stdout = actionResponse.data?.combined.resources[agentId].stdout || ''; + const stderr = actionResponse.data?.combined.resources[agentId].stderr || ''; + const error = actionResponse.data?.combined.resources[agentId].errors?.[0]; + const options = { + actionId: doc.EndpointActions.action_id, + agentId, + data: { + ...doc.EndpointActions.data, + output: { + content: { + stdout, + stderr, + code: '200', + }, + type: 'text' as const, + }, + }, + ...(error + ? { + error: { + code: error.code, + message: `Crowdstrike action failed: ${error.message}`, + }, + } + : {}), + }; + + await this.writeActionResponseToEndpointIndex(options); } private async completeCrowdstrikeAction( - actionResponse: ActionTypeExecutorResult<CrowdstrikeBaseApiResponse> | undefined, + actionResponse: ActionTypeExecutorResult<CrowdstrikeBaseApiResponse>, doc: LogsEndpointAction ): Promise<void> { const options = { diff --git a/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/utils.test.ts b/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/utils.test.ts new file mode 100644 index 000000000000..f961c7a3d1d6 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/utils.test.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mapParametersToCrowdStrikeArguments } from './utils'; + +describe('mapParametersToCrowdStrikeArguments', () => { + it('returns command with single word parameter as is', () => { + const result = mapParametersToCrowdStrikeArguments('runscript', { raw: 'echo Hello' }); + expect(result).toBe('runscript --Raw=```echo Hello```'); + }); + + it('wraps multi-word parameter in triple backticks', () => { + const result = mapParametersToCrowdStrikeArguments('runscript', { + commandLine: 'echo Hello World', + }); + expect(result).toBe('runscript --CommandLine=```echo Hello World```'); + }); + + it('leaves parameter already wrapped in triple backticks unchanged', () => { + const result = mapParametersToCrowdStrikeArguments('runscript', { + commandLine: '```echo Hello World```', + }); + expect(result).toBe('runscript --CommandLine=```echo Hello World```'); + }); + + it('trims spaces from parameter values', () => { + const result = mapParametersToCrowdStrikeArguments('runscript', { raw: ' echo Hello ' }); + expect(result).toBe('runscript --Raw=```echo Hello```'); + }); + + it('handles multiple parameters correctly', () => { + const result = mapParametersToCrowdStrikeArguments('runscript', { + raw: 'echo Hello', + commandLine: 'echo Hello World', + hostPath: '/home/user', + cloudFile: 'file.txt', + }); + expect(result).toBe( + 'runscript --Raw=```echo Hello``` --CommandLine=```echo Hello World``` --HostPath=/home/user --CloudFile=file.txt' + ); + }); + + it('returns command with no parameters correctly', () => { + const result = mapParametersToCrowdStrikeArguments('runscript', {}); + expect(result).toBe('runscript '); + }); +}); diff --git a/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/utils.ts b/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/utils.ts new file mode 100644 index 000000000000..2ec2ec2bb0cf --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/utils.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { upperFirst } from 'lodash'; +import type { RunScriptActionRequestBody } from '../../../../../../common/api/endpoint'; + +export const mapParametersToCrowdStrikeArguments = ( + commandName: string, + parameters: RunScriptActionRequestBody['parameters'] +): string => { + // Map each parameter to the required syntax and join them with spaces + // In short: this function has to transform the parameters object into a string that can be used as a CS command + // One word commands eg. 'ls' can go as it is, but if there are more elements eg. 'ls -l', they have to be wrapped in triple backticks + const commandParts = Object.entries(parameters).map(([key, value]) => { + // Check and process the parameter value + let sanitizedValue; + if (typeof value === 'string') { + if (/^```.*```$/.test(value)) { + // If already wrapped in triple backticks, leave unchanged + sanitizedValue = value; + } else { + const strippedValue = value.trim(); // Remove spaces at the beginning and end + if (strippedValue.split(/\s+/).length === 1) { + // If it's a single element (no spaces), use it as-is + sanitizedValue = strippedValue; + } else { + // If it contains multiple elements (spaces), wrap in ``` + sanitizedValue = `\`\`\`${strippedValue}\`\`\``; + } + } + } else { + sanitizedValue = value; + } + return `--${upperFirst(key)}=${sanitizedValue}`; + }); + + // Combine the base command with the constructed parameters + return `${commandName} ${commandParts.join(' ')}`; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts b/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts index 6360ceba71ce..00b4774d9489 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts @@ -248,7 +248,7 @@ const createRunScriptOptionsMock = ( const options: RunScriptActionRequestBody = { ...createNoParamsResponseActionOptionsMock(), parameters: { - Raw: 'ls', + raw: 'ls', }, }; return merge(options, overrides); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/README.md b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/README.md index f159a2c5b248..b60033b73177 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/README.md +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/README.md @@ -172,5 +172,5 @@ To test out the functionality of large lists with rules, the user will need to i * First, set the appropriate env var in order to enable exceptions features`export ELASTIC_XPACK_SECURITY_SOLUTION_LISTS_FEATURE=true` and `export ELASTIC_XPACK_SECURITY_SOLUTION_EXCEPTIONS_LISTS=true` and start kibana * Second, import a list of ips from a file called `ci-badguys.txt`. The command should look like this: -`cd $HOME/kibana/x-pack/plugins/lists/server/scripts && ./import_list_items_by_filename.sh ip ~/ci-badguys.txt` +`cd $HOME/kibana/x-pack/solutions/security/plugins/lists/server/scripts && ./import_list_items_by_filename.sh ip ~/ci-badguys.txt` * Then, from the detection engine scripts folder (`cd kibana/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/scripts`) run `./post_rule.sh rules/queries/lists/query_with_list_plugin.json` diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts index 86ac7d136d22..b7cacea3c375 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts @@ -17,6 +17,18 @@ import type { DataViewsService } from '@kbn/data-views-plugin/common'; import type { AppClient } from '../../..'; import type { EntityStoreConfig } from './types'; import { mockGlobalState } from '../../../../public/common/mock'; +import type { EntityDefinition } from '@kbn/entities-schema'; +import { getUnitedEntityDefinition } from './united_entity_definitions'; + +const unitedDefinition = getUnitedEntityDefinition({ + entityType: 'host', + namespace: 'test', + fieldHistoryLength: 10, + indexPatterns: [], + syncDelay: '1m', + frequency: '1m', +}); +const definition: EntityDefinition = unitedDefinition.entityManagerDefinition; describe('EntityStoreDataClient', () => { const mockSavedObjectClient = savedObjectsClientMock.create(); @@ -182,4 +194,137 @@ describe('EntityStoreDataClient', () => { expect(response.records[0]).toEqual(fakeEntityRecord); }); }); + + describe('getComponentFromEntityDefinition', () => { + it('returns installed false if no definition is provided', () => { + const result = dataClient.getComponentFromEntityDefinition('security_host_test', undefined); + expect(result).toEqual([ + { + id: 'security_host_test', + installed: false, + resource: 'entity_definition', + }, + ]); + }); + + it('returns correct components for EntityDefinitionWithState', () => { + const definitionWithState = { + ...definition, + state: { + installed: true, + running: true, + components: { + transforms: [ + { + id: 'transforms_id', + installed: true, + running: true, + }, + ], + ingestPipelines: [ + { + id: 'pipeline-1', + installed: true, + }, + ], + indexTemplates: [ + { + id: 'indexTemplates_id', + installed: true, + }, + ], + }, + }, + }; + + const result = dataClient.getComponentFromEntityDefinition( + 'security_host_test', + definitionWithState + ); + expect(result).toEqual([ + { + id: 'security_host_test', + installed: true, + resource: 'entity_definition', + }, + { + id: 'security_host_test', + resource: 'transform', + installed: true, + health: 'unknown', + errors: undefined, + }, + { + resource: 'ingest_pipeline', + id: 'pipeline-1', + installed: true, + }, + { + id: 'security_host_test', + installed: true, + resource: 'index_template', + }, + ]); + }); + + it('returns empty array for EntityDefinition without state', () => { + const result = dataClient.getComponentFromEntityDefinition('security_host_test', definition); + expect(result).toEqual([]); + }); + + it('handles transform health issues correctly', () => { + const definitionWithState = { + ...definition, + state: { + installed: true, + components: { + transforms: [ + { + installed: true, + stats: { + health: { + status: 'yellow', + issues: [ + { + type: 'issue-type', + issue: 'issue-message', + details: 'issue-details', + count: 1, + }, + ], + }, + }, + }, + ], + ingestPipelines: [], + indexTemplates: [], + }, + }, + }; + + const result = dataClient.getComponentFromEntityDefinition( + 'security_host_test', + definitionWithState + ); + expect(result).toEqual([ + { + id: 'security_host_test', + installed: true, + resource: 'entity_definition', + }, + { + id: 'security_host_test', + resource: 'transform', + installed: true, + health: 'yellow', + errors: [ + { + title: 'issue-message', + message: 'issue-details', + }, + ], + }, + ]); + }); + }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts index c18dc1863a8d..faf2edc15419 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts @@ -15,7 +15,7 @@ import type { AnalyticsServiceSetup, } from '@kbn/core/server'; import { EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client'; -import type { SortOrder } from '@elastic/elasticsearch/lib/api/types'; +import type { HealthStatus, SortOrder } from '@elastic/elasticsearch/lib/api/types'; import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; import type { DataViewsService } from '@kbn/data-views-plugin/common'; import { isEqual } from 'lodash/fp'; @@ -465,7 +465,7 @@ export class EntityStoreDataClient { public getComponentFromEntityDefinition( id: string, - definition: EntityDefinitionWithState | EntityDefinition + definition: EntityDefinitionWithState | EntityDefinition | undefined ): EngineComponentStatus[] { if (!definition) { return [ @@ -478,16 +478,22 @@ export class EntityStoreDataClient { } if ('state' in definition) { + const transformHealthToComponentHealth = ( + health: HealthStatus | undefined + ): EngineComponentStatus['health'] => + health ? (health.toLowerCase() as Lowercase<HealthStatus>) : 'unknown'; + return [ { id: definition.id, installed: definition.state.installed, resource: EngineComponentResourceEnum.entity_definition, }, - ...definition.state.components.transforms.map(({ installed, running, stats }) => ({ + ...definition.state.components.transforms.map(({ installed, stats }) => ({ id, resource: EngineComponentResourceEnum.transform, installed, + health: transformHealthToComponentHealth(stats?.health?.status), errors: (stats?.health as TransformHealth)?.issues?.map(({ issue, details }) => ({ title: issue, message: details, diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/resources/upsert.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/resources/upsert.ts index 9557c5cfd652..fde332aefbd3 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/resources/upsert.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/resources/upsert.ts @@ -27,6 +27,7 @@ export const registerSiemRuleMigrationsResourceUpsertRoute = ( path: SIEM_RULE_MIGRATION_RESOURCES_PATH, access: 'internal', security: { authz: { requiredPrivileges: ['securitySolution'] } }, + options: { body: { maxBytes: 26214400 } }, // rise payload limit to 25MB }) .addVersion( { diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/stats.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/stats.ts index 5fb7d9e0525c..4657f2516181 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/stats.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/stats.ts @@ -41,6 +41,9 @@ export const registerSiemRuleMigrationsStatsRoute = ( const stats = await ruleMigrationsClient.task.getStats(migrationId); + if (stats.rules.total === 0) { + return res.noContent(); + } return res.ok({ body: stats }); } catch (err) { logger.error(err); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/translation_stats.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/translation_stats.ts index 4f9d12385e32..ede4ccbeaa6d 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/translation_stats.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/translation_stats.ts @@ -45,6 +45,9 @@ export const registerSiemRuleMigrationsTranslationStatsRoute = ( const stats = await ruleMigrationsClient.data.rules.getTranslationStats(migrationId); + if (stats.rules.total === 0) { + return res.noContent(); + } return res.ok({ body: stats }); } catch (err) { logger.error(err); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts index b483b3bdd4fb..47bcd56e6433 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts @@ -16,7 +16,10 @@ import type { Duration, } from '@elastic/elasticsearch/lib/api/types'; import type { StoredRuleMigration } from '../types'; -import { SiemMigrationStatus } from '../../../../../common/siem_migrations/constants'; +import { + SiemMigrationStatus, + RuleTranslationResult, +} from '../../../../../common/siem_migrations/constants'; import { type RuleMigration, type RuleMigrationTaskStats, @@ -128,19 +131,14 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient /** Retrieves an array of rule documents of a specific migrations */ async get( migrationId: string, - { filters = {}, sort = {}, from, size }: RuleMigrationGetOptions = {} + { filters = {}, sort: sortParam = {}, from, size }: RuleMigrationGetOptions = {} ): Promise<{ total: number; data: StoredRuleMigration[] }> { const index = await this.getIndexName(); const query = this.getFilterQuery(migrationId, filters); + const sort = sortParam.sortField ? getSortingOptions(sortParam) : undefined; const result = await this.esClient - .search<RuleMigration>({ - index, - query, - sort: sort.sortField ? getSortingOptions(sort) : undefined, - from, - size, - }) + .search<RuleMigration>({ index, query, sort, from, size }) .catch((error) => { this.logger.error(`Error searching rule migrations: ${error.message}`); throw error; @@ -268,8 +266,15 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient const query = this.getFilterQuery(migrationId); const aggregations = { - prebuilt: { filter: searchConditions.isPrebuilt() }, - installable: { filter: { bool: { must: searchConditions.isInstallable() } } }, + success: { + filter: { term: { status: SiemMigrationStatus.COMPLETED } }, + aggs: { + result: { terms: { field: 'translation_result' } }, + installable: { filter: { bool: { must: searchConditions.isInstallable() } } }, + prebuilt: { filter: searchConditions.isPrebuilt() }, + }, + }, + failed: { filter: { term: { status: SiemMigrationStatus.FAILED } } }, }; const result = await this.esClient .search({ index, query, aggregations, _source: false }) @@ -278,16 +283,22 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient throw error; }); - const bucket = result.aggregations ?? {}; + const aggs = result.aggregations ?? {}; const total = this.getTotalHits(result); - const prebuilt = (bucket.prebuilt as AggregationsFilterAggregate)?.doc_count ?? 0; + const successAgg = aggs.success as AggregationsFilterAggregate; + const translationResultsAgg = successAgg.result as AggregationsStringTermsAggregate; + return { id: migrationId, rules: { total, - prebuilt, - custom: total - prebuilt, - installable: (bucket.installable as AggregationsFilterAggregate)?.doc_count ?? 0, + success: { + total: (successAgg as AggregationsFilterAggregate)?.doc_count ?? 0, + result: this.translationResultAggCount(translationResultsAgg), + installable: (successAgg.installable as AggregationsFilterAggregate)?.doc_count ?? 0, + prebuilt: (successAgg.prebuilt as AggregationsFilterAggregate)?.doc_count ?? 0, + }, + failed: (aggs.failed as AggregationsFilterAggregate)?.doc_count ?? 0, }, }; } @@ -297,10 +308,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient const index = await this.getIndexName(); const query = this.getFilterQuery(migrationId); const aggregations = { - pending: { filter: { term: { status: SiemMigrationStatus.PENDING } } }, - processing: { filter: { term: { status: SiemMigrationStatus.PROCESSING } } }, - completed: { filter: { term: { status: SiemMigrationStatus.COMPLETED } } }, - failed: { filter: { term: { status: SiemMigrationStatus.FAILED } } }, + status: { terms: { field: 'status' } }, createdAt: { min: { field: '@timestamp' } }, lastUpdatedAt: { max: { field: 'updated_at' } }, }; @@ -311,18 +319,16 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient throw error; }); - const bucket = result.aggregations ?? {}; + const aggs = result.aggregations ?? {}; + return { id: migrationId, rules: { total: this.getTotalHits(result), - pending: (bucket.pending as AggregationsFilterAggregate)?.doc_count ?? 0, - processing: (bucket.processing as AggregationsFilterAggregate)?.doc_count ?? 0, - completed: (bucket.completed as AggregationsFilterAggregate)?.doc_count ?? 0, - failed: (bucket.failed as AggregationsFilterAggregate)?.doc_count ?? 0, + ...this.statusAggCounts(aggs.status as AggregationsStringTermsAggregate), }, - created_at: (bucket.createdAt as AggregationsMinAggregate)?.value_as_string ?? '', - last_updated_at: (bucket.lastUpdatedAt as AggregationsMaxAggregate)?.value_as_string ?? '', + created_at: (aggs.createdAt as AggregationsMinAggregate)?.value_as_string ?? '', + last_updated_at: (aggs.lastUpdatedAt as AggregationsMaxAggregate)?.value_as_string ?? '', }; } @@ -331,12 +337,9 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient const index = await this.getIndexName(); const aggregations: { migrationIds: AggregationsAggregationContainer } = { migrationIds: { - terms: { field: 'migration_id', order: { createdAt: 'asc' } }, + terms: { field: 'migration_id', order: { createdAt: 'asc' }, size: 10000 }, aggregations: { - pending: { filter: { term: { status: SiemMigrationStatus.PENDING } } }, - processing: { filter: { term: { status: SiemMigrationStatus.PROCESSING } } }, - completed: { filter: { term: { status: SiemMigrationStatus.COMPLETED } } }, - failed: { filter: { term: { status: SiemMigrationStatus.FAILED } } }, + status: { terms: { field: 'status' } }, createdAt: { min: { field: '@timestamp' } }, lastUpdatedAt: { max: { field: 'updated_at' } }, }, @@ -355,16 +358,43 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient id: bucket.key, rules: { total: bucket.doc_count, - pending: bucket.pending?.doc_count ?? 0, - processing: bucket.processing?.doc_count ?? 0, - completed: bucket.completed?.doc_count ?? 0, - failed: bucket.failed?.doc_count ?? 0, + ...this.statusAggCounts(bucket.status), }, created_at: bucket.createdAt?.value_as_string, last_updated_at: bucket.lastUpdatedAt?.value_as_string, })); } + private statusAggCounts( + statusAgg: AggregationsStringTermsAggregate + ): Record<SiemMigrationStatus, number> { + const buckets = statusAgg.buckets as AggregationsStringTermsBucket[]; + return { + [SiemMigrationStatus.PENDING]: + buckets.find(({ key }) => key === SiemMigrationStatus.PENDING)?.doc_count ?? 0, + [SiemMigrationStatus.PROCESSING]: + buckets.find(({ key }) => key === SiemMigrationStatus.PROCESSING)?.doc_count ?? 0, + [SiemMigrationStatus.COMPLETED]: + buckets.find(({ key }) => key === SiemMigrationStatus.COMPLETED)?.doc_count ?? 0, + [SiemMigrationStatus.FAILED]: + buckets.find(({ key }) => key === SiemMigrationStatus.FAILED)?.doc_count ?? 0, + }; + } + + private translationResultAggCount( + resultAgg: AggregationsStringTermsAggregate + ): Record<RuleTranslationResult, number> { + const buckets = resultAgg.buckets as AggregationsStringTermsBucket[]; + return { + [RuleTranslationResult.FULL]: + buckets.find(({ key }) => key === RuleTranslationResult.FULL)?.doc_count ?? 0, + [RuleTranslationResult.PARTIAL]: + buckets.find(({ key }) => key === RuleTranslationResult.PARTIAL)?.doc_count ?? 0, + [RuleTranslationResult.UNTRANSLATABLE]: + buckets.find(({ key }) => key === RuleTranslationResult.UNTRANSLATABLE)?.doc_count ?? 0, + }; + } + private getFilterQuery( migrationId: string, { status, ids, installable, prebuilt, searchTerm }: RuleMigrationFilters = {} diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/search.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/search.ts index 282f783671fd..3bd8da066a45 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/search.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/search.ts @@ -6,11 +6,11 @@ */ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { SiemMigrationRuleTranslationResult } from '../../../../../common/siem_migrations/constants'; +import { RuleTranslationResult } from '../../../../../common/siem_migrations/constants'; export const conditions = { isFullyTranslated(): QueryDslQueryContainer { - return { term: { translation_result: SiemMigrationRuleTranslationResult.FULL } }; + return { term: { translation_result: RuleTranslationResult.FULL } }; }, isNotInstalled(): QueryDslQueryContainer { return { diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/graph.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/graph.ts index 942adcfcc89e..b1165ce98229 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/graph.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/graph.ts @@ -6,7 +6,7 @@ */ import { END, START, StateGraph } from '@langchain/langgraph'; -import { SiemMigrationRuleTranslationResult } from '../../../../../../common/siem_migrations/constants'; +import { RuleTranslationResult } from '../../../../../../common/siem_migrations/constants'; import { getCreateSemanticQueryNode } from './nodes/create_semantic_query'; import { getMatchPrebuiltRuleNode } from './nodes/match_prebuilt_rule'; import { getProcessQueryNode } from './nodes/process_query'; @@ -62,7 +62,7 @@ const matchedPrebuiltRuleConditional = (state: MigrateRuleState) => { if (state.elastic_rule?.prebuilt_rule_id) { return END; } - if (state.translation_result === SiemMigrationRuleTranslationResult.UNTRANSLATABLE) { + if (state.translation_result === RuleTranslationResult.UNTRANSLATABLE) { return END; } return 'processQuery'; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/nodes/match_prebuilt_rule/match_prebuilt_rule.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/nodes/match_prebuilt_rule/match_prebuilt_rule.ts index e4b2162249ca..d7537fdc72dd 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/nodes/match_prebuilt_rule/match_prebuilt_rule.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/nodes/match_prebuilt_rule/match_prebuilt_rule.ts @@ -7,7 +7,7 @@ import type { Logger } from '@kbn/core/server'; import { JsonOutputParser } from '@langchain/core/output_parsers'; -import { SiemMigrationRuleTranslationResult } from '../../../../../../../../common/siem_migrations/constants'; +import { RuleTranslationResult } from '../../../../../../../../common/siem_migrations/constants'; import type { RuleMigrationsRetriever } from '../../../retrievers'; import type { ChatModel } from '../../../util/actions_client_chat'; import type { GraphNode } from '../../types'; @@ -68,7 +68,7 @@ export const getMatchPrebuiltRuleNode = ({ id: matchedRule.installedRuleId, prebuilt_rule_id: matchedRule.rule_id, }, - translation_result: SiemMigrationRuleTranslationResult.FULL, + translation_result: RuleTranslationResult.FULL, }; } } @@ -80,7 +80,7 @@ export const getMatchPrebuiltRuleNode = ({ logger.debug( `Rule: ${state.original_rule?.title} did not match any prebuilt rule, but contains inputlookup, dropping` ); - return { translation_result: SiemMigrationRuleTranslationResult.UNTRANSLATABLE }; + return { translation_result: RuleTranslationResult.UNTRANSLATABLE }; } return {}; }; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/state.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/state.ts index a9047c9dc543..66b3c0c8e7a7 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/state.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/state.ts @@ -7,7 +7,7 @@ import type { BaseMessage } from '@langchain/core/messages'; import { Annotation, messagesStateReducer } from '@langchain/langgraph'; -import type { SiemMigrationRuleTranslationResult } from '../../../../../../common/siem_migrations/constants'; +import type { RuleTranslationResult } from '../../../../../../common/siem_migrations/constants'; import type { ElasticRule, OriginalRule, @@ -31,7 +31,7 @@ export const migrateRuleState = Annotation.Root({ reducer: (current, value) => value ?? current, default: () => '', }), - translation_result: Annotation<SiemMigrationRuleTranslationResult>(), + translation_result: Annotation<RuleTranslationResult>(), comments: Annotation<RuleMigration['comments']>({ reducer: (current, value) => (value ? (current ?? []).concat(value) : current), default: () => [], diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/graph.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/graph.ts index 463de671552c..99d7d1439d63 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/graph.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/graph.ts @@ -7,7 +7,7 @@ import { END, START, StateGraph } from '@langchain/langgraph'; import { isEmpty } from 'lodash/fp'; -import { SiemMigrationRuleTranslationResult } from '../../../../../../../../common/siem_migrations/constants'; +import { RuleTranslationResult } from '../../../../../../../../common/siem_migrations/constants'; import { getEcsMappingNode } from './nodes/ecs_mapping'; import { getFilterIndexPatternsNode } from './nodes/filter_index_patterns'; import { getFixQueryErrorsNode } from './nodes/fix_query_errors'; @@ -67,7 +67,7 @@ export function getTranslateRuleGraph({ const validationRouter = (state: TranslateRuleState) => { if ( state.validation_errors.iterations <= MAX_VALIDATION_ITERATIONS && - state.translation_result === SiemMigrationRuleTranslationResult.FULL + state.translation_result === RuleTranslationResult.FULL ) { if (!isEmpty(state.validation_errors?.esql_errors)) { return 'fixQueryErrors'; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/ecs_mapping/ecs_mapping.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/ecs_mapping/ecs_mapping.ts index 6acc45a95d17..07753432e5db 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/ecs_mapping/ecs_mapping.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/ecs_mapping/ecs_mapping.ts @@ -7,7 +7,7 @@ import type { Logger } from '@kbn/core/server'; import type { InferenceClient } from '@kbn/inference-plugin/server'; -import { SiemMigrationRuleTranslationResult } from '../../../../../../../../../../common/siem_migrations/constants'; +import { RuleTranslationResult } from '../../../../../../../../../../common/siem_migrations/constants'; import { getEsqlKnowledgeBase } from '../../../../../util/esql_knowledge_base_caller'; import type { GraphNode } from '../../types'; import { SIEM_RULE_MIGRATION_CIM_ECS_MAP } from './cim_ecs_map'; @@ -58,9 +58,9 @@ export const getEcsMappingNode = ({ }; }; -const getTranslationResult = (esqlQuery: string): SiemMigrationRuleTranslationResult => { +const getTranslationResult = (esqlQuery: string): RuleTranslationResult => { if (esqlQuery.match(/\[(macro):[\s\S]*\]/)) { - return SiemMigrationRuleTranslationResult.PARTIAL; + return RuleTranslationResult.PARTIAL; } - return SiemMigrationRuleTranslationResult.FULL; + return RuleTranslationResult.FULL; }; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/filter_index_patterns/filter_index_patterns.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/filter_index_patterns/filter_index_patterns.ts index bb1e086bf493..b7cbcabff2ca 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/filter_index_patterns/filter_index_patterns.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/filter_index_patterns/filter_index_patterns.ts @@ -6,7 +6,7 @@ */ import type { Logger } from '@kbn/core/server'; -import { SiemMigrationRuleTranslationResult } from '../../../../../../../../../../common/siem_migrations/constants'; +import { RuleTranslationResult } from '../../../../../../../../../../common/siem_migrations/constants'; import type { GraphNode } from '../../types'; interface GetFilterIndexPatternsNodeParams { @@ -30,7 +30,7 @@ export const getFilterIndexPatternsNode = ({ elastic_rule: { ...state.elastic_rule, query: newQuery, - translation_result: SiemMigrationRuleTranslationResult.PARTIAL, + translation_result: RuleTranslationResult.PARTIAL, }, }; } diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/translate_rule.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/translate_rule.ts index d613da223038..346df02714b6 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/translate_rule.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/translate_rule.ts @@ -7,7 +7,7 @@ import type { Logger } from '@kbn/core/server'; import type { InferenceClient } from '@kbn/inference-plugin/server'; -import { SiemMigrationRuleTranslationResult } from '../../../../../../../../../../common/siem_migrations/constants'; +import { RuleTranslationResult } from '../../../../../../../../../../common/siem_migrations/constants'; import { getEsqlKnowledgeBase } from '../../../../../util/esql_knowledge_base_caller'; import type { GraphNode } from '../../types'; import { ESQL_SYNTAX_TRANSLATION_PROMPT } from './prompts'; @@ -63,9 +63,9 @@ export const getTranslateRuleNode = ({ }; }; -const getTranslationResult = (esqlQuery: string): SiemMigrationRuleTranslationResult => { +const getTranslationResult = (esqlQuery: string): RuleTranslationResult => { if (esqlQuery.match(/\[(macro):[\s\S]*\]/)) { - return SiemMigrationRuleTranslationResult.PARTIAL; + return RuleTranslationResult.PARTIAL; } - return SiemMigrationRuleTranslationResult.FULL; + return RuleTranslationResult.FULL; }; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/state.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/state.ts index ea4623800217..873f1880d225 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/state.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/state.ts @@ -7,7 +7,7 @@ import type { BaseMessage } from '@langchain/core/messages'; import { Annotation, messagesStateReducer } from '@langchain/langgraph'; -import { SiemMigrationRuleTranslationResult } from '../../../../../../../../common/siem_migrations/constants'; +import { RuleTranslationResult } from '../../../../../../../../common/siem_migrations/constants'; import type { ElasticRule, OriginalRule, @@ -46,9 +46,9 @@ export const translateRuleState = Annotation.Root({ reducer: (current, value) => value ?? current, default: () => ({ iterations: 0 } as TranslateRuleValidationErrors), }), - translation_result: Annotation<SiemMigrationRuleTranslationResult>({ + translation_result: Annotation<RuleTranslationResult>({ reducer: (current, value) => value ?? current, - default: () => SiemMigrationRuleTranslationResult.UNTRANSLATABLE, + default: () => RuleTranslationResult.UNTRANSLATABLE, }), comments: Annotation<RuleMigration['comments']>({ reducer: (current, value) => (value ? (current ?? []).concat(value) : current), diff --git a/x-pack/solutions/security/plugins/security_solution/tsconfig.json b/x-pack/solutions/security/plugins/security_solution/tsconfig.json index 82bde9dc795d..7729f14bca7e 100644 --- a/x-pack/solutions/security/plugins/security_solution/tsconfig.json +++ b/x-pack/solutions/security/plugins/security_solution/tsconfig.json @@ -234,6 +234,7 @@ "@kbn/react-hooks", "@kbn/index-adapter", "@kbn/core-http-server-utils", - "@kbn/llm-tasks-plugin" + "@kbn/llm-tasks-plugin", + "@kbn/ai-assistant-icon" ] } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/transform_rule_types/transform_health/rule.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/transform_rule_types/transform_health/rule.ts index f760ac26f40c..ca0ba9a92f64 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/transform_rule_types/transform_health/rule.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/transform_rule_types/transform_health/rule.ts @@ -82,7 +82,6 @@ export default function ruleTests({ getService }: FtrProviderContext) { const objectRemover = new ObjectRemover(supertest); let connectorId: string; const transformId = 'test_transform_01'; - const destinationIndex = generateDestIndex(transformId); beforeEach(async () => { await esTestIndexTool.destroy(); @@ -98,8 +97,11 @@ export default function ruleTests({ getService }: FtrProviderContext) { connectorId = await createConnector(); - await transform.api.createIndices(destinationIndex); await createTransform(transformId); + + // Create additional transforms to exclude from the rule + await createTransform('exclude_transform_01'); + await createTransform('exclude_transform_02'); }); afterEach(async () => { @@ -112,10 +114,12 @@ export default function ruleTests({ getService }: FtrProviderContext) { it('runs correctly', async () => { await stopTransform(transformId); + await stopTransform('exclude_transform_01'); const ruleId = await createRule({ name: 'Test all transforms', includeTransforms: ['*'], + excludeTransforms: ['exclude_transform_*'], }); log.debug('Checking created alerts...'); @@ -160,6 +164,8 @@ export default function ruleTests({ getService }: FtrProviderContext) { } async function createTransform(id: string) { + const destinationIndex = generateDestIndex(id); + await transform.api.createIndices(destinationIndex); const config = generateTransformConfig(id); await transform.api.createAndRunTransform(id, config); } @@ -183,20 +189,20 @@ export default function ruleTests({ getService }: FtrProviderContext) { }, }; + const { name, ...transformHealthRuleParams } = params; + const { status, body: createdRule } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send({ - name: params.name, + name, consumer: 'alerts', enabled: true, rule_type_id: RULE_TYPE_ID, schedule: { interval: '1d' }, actions: [action], notify_when: 'onActiveAlert', - params: { - includeTransforms: params.includeTransforms, - }, + params: transformHealthRuleParams, }); // will print the error body, if an error occurred diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/graph.ts b/x-pack/test/api_integration/apis/cloud_security_posture/graph.ts index 4ff483bff343..4823c500a358 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/graph.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/graph.ts @@ -40,7 +40,7 @@ export default function (providerContext: FtrProviderContext) { it('should return 404 when feature flag is not toggled', async () => { await postGraph(supertest, { query: { - eventIds: [], + originEventIds: [], start: 'now-1d/d', end: 'now/d', }, diff --git a/x-pack/test/api_integration/apis/upgrade_assistant/es_deprecation_logs.helpers.ts b/x-pack/test/api_integration/apis/upgrade_assistant/es_deprecation_logs.helpers.ts index 59077c9ac216..ec2454fdea14 100644 --- a/x-pack/test/api_integration/apis/upgrade_assistant/es_deprecation_logs.helpers.ts +++ b/x-pack/test/api_integration/apis/upgrade_assistant/es_deprecation_logs.helpers.ts @@ -18,7 +18,7 @@ const CHARS_POOL = 'abcdefghijklmnopqrstuvwxyz'; const getRandomString = () => `${chance.string({ pool: CHARS_POOL })}-${Date.now()}`; const deprecationMock = { - 'event.dataset': 'deprecation.elasticsearch', + 'event.dataset': 'elasticsearch.deprecation', '@timestamp': '2021-12-06T16:28:11,104Z', 'log.level': 'CRITICAL', 'log.logger': @@ -30,7 +30,7 @@ const deprecationMock = { message: '[types removal] Specifying include_type_name in get index template requests is deprecated.', 'data_stream.type': 'logs', - 'data_stream.dataset': 'deprecation.elasticsearch', + 'data_stream.dataset': 'elasticsearch.deprecation', 'data_stream.namespace': 'default', 'ecs.version': '1.7', 'elasticsearch.event.category': 'types', diff --git a/x-pack/test/cloud_security_posture_api/config.ts b/x-pack/test/cloud_security_posture_api/config.ts index 4e0ecd1f26e4..212abc50fc9a 100644 --- a/x-pack/test/cloud_security_posture_api/config.ts +++ b/x-pack/test/cloud_security_posture_api/config.ts @@ -5,7 +5,7 @@ * 2.0. */ import { resolve } from 'path'; -import type { FtrConfigProviderContext } from '@kbn/test'; +import { getKibanaCliLoggers, type FtrConfigProviderContext } from '@kbn/test'; import { CLOUD_SECURITY_PLUGIN_VERSION } from '@kbn/cloud-security-posture-plugin/common/constants'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { @@ -21,6 +21,14 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...xPackAPITestsConfig.get('kbnTestServer'), serverArgs: [ ...xPackAPITestsConfig.get('kbnTestServer.serverArgs'), + `--logging.loggers=${JSON.stringify([ + ...getKibanaCliLoggers(xPackAPITestsConfig.get('kbnTestServer.serverArgs')), + { + name: 'plugins.cloudSecurityPosture', + level: 'all', + appenders: ['default'], + }, + ])}`, /** * Package version is fixed (not latest) so FTR won't suddenly break when package is changed. * diff --git a/x-pack/test/cloud_security_posture_api/routes/graph.ts b/x-pack/test/cloud_security_posture_api/routes/graph.ts index 08adf73839ea..e2be81a7d40e 100644 --- a/x-pack/test/cloud_security_posture_api/routes/graph.ts +++ b/x-pack/test/cloud_security_posture_api/routes/graph.ts @@ -48,7 +48,7 @@ export default function (providerContext: FtrProviderContext) { supertestWithoutAuth, { query: { - eventIds: [], + originEventIds: [], start: 'now-1d/d', end: 'now/d', }, @@ -88,7 +88,7 @@ export default function (providerContext: FtrProviderContext) { it('should return 400 when missing `esQuery` field is not of type bool', async () => { await postGraph(supertest, { query: { - eventIds: [], + originEventIds: [], start: 'now-1d/d', end: 'now/d', esQuery: { @@ -102,7 +102,7 @@ export default function (providerContext: FtrProviderContext) { it('should return 400 with unsupported `esQuery`', async () => { await postGraph(supertest, { query: { - eventIds: [], + originEventIds: [], start: 'now-1d/d', end: 'now/d', esQuery: { @@ -122,7 +122,7 @@ export default function (providerContext: FtrProviderContext) { it('should return an empty graph / should return 200 when missing `esQuery` field', async () => { const response = await postGraph(supertest, { query: { - eventIds: [], + originEventIds: [], start: 'now-1d/d', end: 'now/d', }, @@ -136,7 +136,7 @@ export default function (providerContext: FtrProviderContext) { it('should return a graph with nodes and edges by actor', async () => { const response = await postGraph(supertest, { query: { - eventIds: [], + originEventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', esQuery: { @@ -177,7 +177,7 @@ export default function (providerContext: FtrProviderContext) { it('should return a graph with nodes and edges by alert', async () => { const response = await postGraph(supertest, { query: { - eventIds: ['kabcd1234efgh5678'], + originEventIds: [{ id: 'kabcd1234efgh5678', isAlert: true }], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', }, @@ -204,10 +204,40 @@ export default function (providerContext: FtrProviderContext) { }); }); + it('should return a graph with nodes and edges by origin event', async () => { + const response = await postGraph(supertest, { + query: { + originEventIds: [{ id: 'kabcd1234efgh5678', isAlert: false }], + start: '2024-09-01T00:00:00Z', + end: '2024-09-02T00:00:00Z', + }, + }).expect(result(200)); + + expect(response.body).to.have.property('nodes').length(3); + expect(response.body).to.have.property('edges').length(2); + expect(response.body).not.to.have.property('messages'); + + response.body.nodes.forEach((node: any) => { + expect(node).to.have.property('color'); + expect(node.color).equal( + 'primary', + `node color mismatched [node: ${node.id}] [actual: ${node.color}]` + ); + }); + + response.body.edges.forEach((edge: any) => { + expect(edge).to.have.property('color'); + expect(edge.color).equal( + 'primary', + `edge color mismatched [edge: ${edge.id}] [actual: ${edge.color}]` + ); + }); + }); + it('color of alert of failed event should be danger', async () => { const response = await postGraph(supertest, { query: { - eventIds: ['failed-event'], + originEventIds: [{ id: 'failed-event', isAlert: true }], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', }, @@ -237,7 +267,7 @@ export default function (providerContext: FtrProviderContext) { it('color of event of failed event should be warning', async () => { const response = await postGraph(supertest, { query: { - eventIds: [], + originEventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', esQuery: { @@ -279,7 +309,7 @@ export default function (providerContext: FtrProviderContext) { it('2 grouped events, 1 failed, 1 success', async () => { const response = await postGraph(supertest, { query: { - eventIds: [], + originEventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', esQuery: { @@ -327,7 +357,10 @@ export default function (providerContext: FtrProviderContext) { it('should support more than 1 eventIds', async () => { const response = await postGraph(supertest, { query: { - eventIds: ['kabcd1234efgh5678', 'failed-event'], + originEventIds: [ + { id: 'kabcd1234efgh5678', isAlert: true }, + { id: 'failed-event', isAlert: true }, + ], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', }, @@ -357,7 +390,7 @@ export default function (providerContext: FtrProviderContext) { it('should return a graph with nodes and edges by alert and actor', async () => { const response = await postGraph(supertest, { query: { - eventIds: ['kabcd1234efgh5678'], + originEventIds: [{ id: 'kabcd1234efgh5678', isAlert: true }], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', esQuery: { @@ -402,7 +435,7 @@ export default function (providerContext: FtrProviderContext) { it('should filter unknown targets', async () => { const response = await postGraph(supertest, { query: { - eventIds: [], + originEventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', esQuery: { @@ -428,7 +461,7 @@ export default function (providerContext: FtrProviderContext) { const response = await postGraph(supertest, { showUnknownTarget: true, query: { - eventIds: [], + originEventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', esQuery: { @@ -454,7 +487,7 @@ export default function (providerContext: FtrProviderContext) { const response = await postGraph(supertest, { nodesLimit: 1, query: { - eventIds: [], + originEventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', esQuery: { @@ -480,7 +513,7 @@ export default function (providerContext: FtrProviderContext) { it('should support date math', async () => { const response = await postGraph(supertest, { query: { - eventIds: ['kabcd1234efgh5678'], + originEventIds: [{ id: 'kabcd1234efgh5678', isAlert: true }], start: '2024-09-01T12:30:00.000Z||-30m', end: '2024-09-01T12:30:00.000Z||+30m', }, diff --git a/x-pack/test/upgrade_assistant_integration/config.ts b/x-pack/test/upgrade_assistant_integration/config.ts index 0794f4d0b9ad..df798a701b6b 100644 --- a/x-pack/test/upgrade_assistant_integration/config.ts +++ b/x-pack/test/upgrade_assistant_integration/config.ts @@ -38,8 +38,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { }, esTestCluster: { ...xPackFunctionalTestsConfig.get('esTestCluster'), - // this archive can not be loaded into 8.0+ - // dataArchive: path.resolve(__dirname, './fixtures/data_archives/upgrade_assistant.zip'), + dataArchive: path.resolve(__dirname, './fixtures/data_archives/upgrade_assistant.zip'), }, }; } diff --git a/x-pack/test/upgrade_assistant_integration/fixtures/data_archives/upgrade_assistant.zip b/x-pack/test/upgrade_assistant_integration/fixtures/data_archives/upgrade_assistant.zip index bf2104fc5995..bc0208152dae 100644 Binary files a/x-pack/test/upgrade_assistant_integration/fixtures/data_archives/upgrade_assistant.zip and b/x-pack/test/upgrade_assistant_integration/fixtures/data_archives/upgrade_assistant.zip differ diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js deleted file mode 100644 index b8f689edb6a3..000000000000 --- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js +++ /dev/null @@ -1,252 +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 { ReindexStatus, REINDEX_OP_TYPE } from '@kbn/upgrade-assistant-plugin/common/types'; -import { generateNewIndexName } from '@kbn/upgrade-assistant-plugin/server/lib/reindexing/index_settings'; -import { getIndexState } from '@kbn/upgrade-assistant-plugin/common/get_index_state'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - const es = getService('es'); - - // Utility function that keeps polling API until reindex operation has completed or failed. - const waitForReindexToComplete = async (indexName) => { - console.log(`Waiting for reindex to complete...`); - let lastState; - - while (true) { - lastState = (await supertest.get(`/api/upgrade_assistant/reindex/${indexName}`).expect(200)) - .body.reindexOp; - // Once the operation is completed or failed and unlocked, stop polling. - if (lastState.status !== ReindexStatus.inProgress && lastState.locked === null) { - break; - } - await new Promise((resolve) => setTimeout(resolve, 1000)); - } - - return lastState; - }; - - describe.skip('reindexing', () => { - afterEach(() => { - // Cleanup saved objects - return es.deleteByQuery({ - index: '.kibana', - refresh: true, - body: { - query: { - simple_query_string: { - query: REINDEX_OP_TYPE, - fields: ['type'], - }, - }, - }, - }); - }); - - it('should create a new index with the same documents', async () => { - await esArchiver.load('x-pack/test/functional/es_archives/upgrade_assistant/reindex'); - const { body } = await supertest - .post(`/api/upgrade_assistant/reindex/dummydata`) - .set('kbn-xsrf', 'xxx') - .expect(200); - - expect(body.indexName).to.equal('dummydata'); - expect(body.status).to.equal(ReindexStatus.inProgress); - - const lastState = await waitForReindexToComplete('dummydata'); - expect(lastState.errorMessage).to.equal(null); - expect(lastState.status).to.equal(ReindexStatus.completed); - - const { newIndexName } = lastState; - const indexSummary = await es.indices.get({ index: 'dummydata' }); - - // The new index was created - expect(indexSummary[newIndexName]).to.be.an('object'); - // The original index name is aliased to the new one - expect(indexSummary[newIndexName].aliases.dummydata).to.be.an('object'); - // Verify mappings exist on new index - expect(indexSummary[newIndexName].mappings.properties).to.be.an('object'); - // The number of documents in the new index matches what we expect - expect((await es.count({ index: lastState.newIndexName })).count).to.be(3); - - // Cleanup newly created index - await es.indices.delete({ - index: lastState.newIndexName, - }); - }); - - it('can resume after reindexing was stopped right after creating the new index', async () => { - await esArchiver.load('x-pack/test/functional/es_archives/upgrade_assistant/reindex'); - - // This new index is the new soon to be created reindexed index. We create it - // upfront to simulate a situation in which the user restarted kibana half - // way through the reindex process and ended up with an extra index. - await es.indices.create({ index: 'reindexed-v7-dummydata' }); - - const { body } = await supertest - .post(`/api/upgrade_assistant/reindex/dummydata`) - .set('kbn-xsrf', 'xxx') - .expect(200); - - expect(body.indexName).to.equal('dummydata'); - expect(body.status).to.equal(ReindexStatus.inProgress); - - const lastState = await waitForReindexToComplete('dummydata'); - expect(lastState.errorMessage).to.equal(null); - expect(lastState.status).to.equal(ReindexStatus.completed); - - // Cleanup newly created index - await es.indices.delete({ - index: lastState.newIndexName, - }); - }); - - it('should update any aliases', async () => { - await esArchiver.load('x-pack/test/functional/es_archives/upgrade_assistant/reindex'); - - // Add aliases and ensure each returns the right number of docs - await es.indices.updateAliases({ - body: { - actions: [ - { add: { index: 'dummydata', alias: 'myAlias' } }, - { add: { index: 'dummy*', alias: 'wildcardAlias' } }, - { - add: { index: 'dummydata', alias: 'myHttpsAlias', filter: { term: { https: true } } }, - }, - ], - }, - }); - expect((await es.count({ index: 'myAlias' })).count).to.be(3); - expect((await es.count({ index: 'wildcardAlias' })).count).to.be(3); - expect((await es.count({ index: 'myHttpsAlias' })).count).to.be(2); - - // Reindex - await supertest - .post(`/api/upgrade_assistant/reindex/dummydata`) - .set('kbn-xsrf', 'xxx') - .expect(200); - const lastState = await waitForReindexToComplete('dummydata'); - - // The regular aliases should still return 3 docs - expect((await es.count({ index: 'myAlias' })).count).to.be(3); - expect((await es.count({ index: 'wildcardAlias' })).count).to.be(3); - // The filtered alias should still return 2 docs - expect((await es.count({ index: 'myHttpsAlias' })).count).to.be(2); - - // Cleanup newly created index - await es.indices.delete({ - index: lastState.newIndexName, - }); - }); - - it('shows no warnings', async () => { - const resp = await supertest.get(`/api/upgrade_assistant/reindex/7.0-data`); - // By default all reindexing operations will replace an index with an alias (with the same name) - // pointing to a newly created "reindexed" index. - expect(resp.body.warnings.length).to.be(1); - expect(resp.body.warnings[0].warningType).to.be('replaceIndexWithAlias'); - }); - - it('reindexes old 7.0 index', async () => { - const { body } = await supertest - .post(`/api/upgrade_assistant/reindex/7.0-data`) - .set('kbn-xsrf', 'xxx') - .expect(200); - - expect(body.indexName).to.equal('7.0-data'); - expect(body.status).to.equal(ReindexStatus.inProgress); - - const lastState = await waitForReindexToComplete('7.0-data'); - expect(lastState.errorMessage).to.equal(null); - expect(lastState.status).to.equal(ReindexStatus.completed); - }); - - it('should reindex a batch in order and report queue state', async () => { - const assertQueueState = async (firstInQueueIndexName, queueLength) => { - const response = await supertest - .get(`/api/upgrade_assistant/reindex/batch/queue`) - .set('kbn-xsrf', 'xxx') - .expect(200); - - const { queue } = response.body; - - const [firstInQueue] = queue; - - if (!firstInQueueIndexName) { - expect(firstInQueueIndexName).to.be(undefined); - } else { - expect(firstInQueue.indexName).to.be(firstInQueueIndexName); - } - - expect(queue.length).to.be(queueLength); - }; - - const test1 = 'batch-reindex-test1'; - const test2 = 'batch-reindex-test2'; - const test3 = 'batch-reindex-test3'; - - const cleanupReindex = async (indexName) => { - try { - await es.indices.delete({ index: generateNewIndexName(indexName) }); - } catch (e) { - try { - await es.indices.delete({ index: indexName }); - } catch (e) { - // Ignore - } - } - }; - - try { - // Set up indices for the batch - await es.indices.create({ index: test1 }); - await es.indices.create({ index: test2 }); - await es.indices.create({ index: test3 }); - - await es.indices.close({ index: test1 }); - - const result = await supertest - .post(`/api/upgrade_assistant/reindex/batch`) - .set('kbn-xsrf', 'xxx') - .send({ indexNames: [test1, test2, test3] }) - .expect(200); - - expect(result.body.enqueued.length).to.equal(3); - expect(result.body.errors.length).to.equal(0); - - const [{ newIndexName: nameOfIndexThatShouldBeClosed }] = result.body.enqueued; - - await assertQueueState(test1, 3); - await waitForReindexToComplete(test1); - - await assertQueueState(test2, 2); - await waitForReindexToComplete(test2); - - await assertQueueState(test3, 1); - await waitForReindexToComplete(test3); - - await assertQueueState(undefined, 0); - - // Check that the closed index is still closed after reindexing - const resolvedIndices = await es.indices.resolveIndex({ - name: nameOfIndexThatShouldBeClosed, - }); - - const test1ReindexedState = getIndexState(nameOfIndexThatShouldBeClosed, resolvedIndices); - expect(test1ReindexedState).to.be('closed'); - } finally { - await cleanupReindex(test1); - await cleanupReindex(test2); - await cleanupReindex(test3); - } - }); - }); -} diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.ts b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.ts new file mode 100644 index 000000000000..f78ac7cb2521 --- /dev/null +++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.ts @@ -0,0 +1,262 @@ +/* + * 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 { + ReindexStatus, + REINDEX_OP_TYPE, + type ResolveIndexResponseFromES, +} from '@kbn/upgrade-assistant-plugin/common/types'; +import { generateNewIndexName } from '@kbn/upgrade-assistant-plugin/server/lib/reindexing/index_settings'; +import { getIndexState } from '@kbn/upgrade-assistant-plugin/common/get_index_state'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + + // Utility function that keeps polling API until reindex operation has completed or failed. + const waitForReindexToComplete = async (indexName: string) => { + let lastState; + + while (true) { + lastState = (await supertest.get(`/api/upgrade_assistant/reindex/${indexName}`).expect(200)) + .body.reindexOp; + // Once the operation is completed or failed and unlocked, stop polling. + if (lastState.status !== ReindexStatus.inProgress && lastState.locked === null) { + break; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + return lastState; + }; + + describe('reindexing', () => { + afterEach(() => { + // Cleanup saved objects + return es.deleteByQuery({ + index: '.kibana', + refresh: true, + body: { + query: { + simple_query_string: { + query: REINDEX_OP_TYPE, + fields: ['type'], + }, + }, + }, + }); + }); + + it('should create a new index with the same documents', async () => { + await esArchiver.load('x-pack/test/functional/es_archives/upgrade_assistant/reindex'); + const { body } = await supertest + .post(`/api/upgrade_assistant/reindex/dummydata`) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(body.indexName).to.equal('dummydata'); + expect(body.status).to.equal(ReindexStatus.inProgress); + + const lastState = await waitForReindexToComplete('dummydata'); + expect(lastState.errorMessage).to.equal(null); + expect(lastState.status).to.equal(ReindexStatus.completed); + + const { newIndexName } = lastState; + const indexSummary = await es.indices.get({ index: 'dummydata' }); + + // The new index was created + expect(indexSummary[newIndexName]).to.be.an('object'); + // The original index name is aliased to the new one + expect(indexSummary[newIndexName].aliases?.dummydata).to.be.an('object'); + // Verify mappings exist on new index + expect(indexSummary[newIndexName].mappings?.properties).to.be.an('object'); + // The number of documents in the new index matches what we expect + expect((await es.count({ index: lastState.newIndexName })).count).to.be(3); + + // Cleanup newly created index + await es.indices.delete({ + index: lastState.newIndexName, + }); + }); + + it('can resume after reindexing was stopped right after creating the new index', async () => { + await esArchiver.load('x-pack/test/functional/es_archives/upgrade_assistant/reindex'); + + // This new index is the new soon to be created reindexed index. We create it + // upfront to simulate a situation in which the user restarted kibana half + // way through the reindex process and ended up with an extra index. + await es.indices.create({ index: 'reindexed-v9-dummydata' }); + + const { body } = await supertest + .post(`/api/upgrade_assistant/reindex/dummydata`) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(body.indexName).to.equal('dummydata'); + expect(body.status).to.equal(ReindexStatus.inProgress); + + const lastState = await waitForReindexToComplete('dummydata'); + expect(lastState.errorMessage).to.equal(null); + expect(lastState.status).to.equal(ReindexStatus.completed); + + // Cleanup newly created index + await es.indices.delete({ + index: lastState.newIndexName, + }); + }); + + it('should update any aliases', async () => { + await esArchiver.load('x-pack/test/functional/es_archives/upgrade_assistant/reindex'); + + // Add aliases and ensure each returns the right number of docs + await es.indices.updateAliases({ + body: { + actions: [ + { add: { index: 'dummydata', alias: 'myAlias' } }, + { add: { index: 'dummy*', alias: 'wildcardAlias' } }, + { + add: { index: 'dummydata', alias: 'myHttpsAlias', filter: { term: { https: true } } }, + }, + ], + }, + }); + expect((await es.count({ index: 'myAlias' })).count).to.be(3); + expect((await es.count({ index: 'wildcardAlias' })).count).to.be(3); + expect((await es.count({ index: 'myHttpsAlias' })).count).to.be(2); + + // Reindex + await supertest + .post(`/api/upgrade_assistant/reindex/dummydata`) + .set('kbn-xsrf', 'xxx') + .expect(200); + const lastState = await waitForReindexToComplete('dummydata'); + + // The regular aliases should still return 3 docs + expect((await es.count({ index: 'myAlias' })).count).to.be(3); + expect((await es.count({ index: 'wildcardAlias' })).count).to.be(3); + // The filtered alias should still return 2 docs + expect((await es.count({ index: 'myHttpsAlias' })).count).to.be(2); + + // Cleanup newly created index + await es.indices.delete({ + index: lastState.newIndexName, + }); + }); + + it('shows no warnings', async () => { + const resp = await supertest.get(`/api/upgrade_assistant/reindex/reindexed-v8-6.0-data`); // reusing the index previously migrated in v8->v9 UA tests + // By default, all reindexing operations will replace an index with an alias (with the same name) + // pointing to a newly created "reindexed" index. + expect(resp.body.warnings.length).to.be(1); + expect(resp.body.warnings[0].warningType).to.be('replaceIndexWithAlias'); + }); + + it('reindexes old 7.0 index', async () => { + const { body } = await supertest + .post(`/api/upgrade_assistant/reindex/reindexed-v8-6.0-data`) // reusing the index previously migrated in v8->v9 UA tests + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(body.indexName).to.equal('reindexed-v8-6.0-data'); + expect(body.status).to.equal(ReindexStatus.inProgress); + + const lastState = await waitForReindexToComplete('reindexed-v8-6.0-data'); + expect(lastState.errorMessage).to.equal(null); + expect(lastState.status).to.equal(ReindexStatus.completed); + }); + + it('should reindex a batch in order and report queue state', async () => { + const assertQueueState = async ( + firstInQueueIndexName: string | undefined, + queueLength: number + ) => { + const response = await supertest + .get(`/api/upgrade_assistant/reindex/batch/queue`) + .set('kbn-xsrf', 'xxx') + .expect(200); + + const { queue } = response.body; + + const [firstInQueue] = queue; + + if (!firstInQueueIndexName) { + expect(firstInQueueIndexName).to.be(undefined); + } else { + expect(firstInQueue.indexName).to.be(firstInQueueIndexName); + } + + expect(queue.length).to.be(queueLength); + }; + + const test1 = 'batch-reindex-test1'; + const test2 = 'batch-reindex-test2'; + const test3 = 'batch-reindex-test3'; + + const cleanupReindex = async (indexName: string) => { + try { + await es.indices.delete({ index: generateNewIndexName(indexName) }); + } catch (e) { + try { + await es.indices.delete({ index: indexName }); + } catch (_err) { + // Ignore + } + } + }; + + try { + // Set up indices for the batch + await es.indices.create({ index: test1 }); + await es.indices.create({ index: test2 }); + await es.indices.create({ index: test3 }); + + await es.indices.close({ index: test1 }); + + const result = await supertest + .post(`/api/upgrade_assistant/reindex/batch`) + .set('kbn-xsrf', 'xxx') + .send({ indexNames: [test1, test2, test3] }) + .expect(200); + + expect(result.body.enqueued.length).to.equal(3); + expect(result.body.errors.length).to.equal(0); + + const [{ newIndexName: nameOfIndexThatShouldBeClosed }] = result.body.enqueued; + + await assertQueueState(test1, 3); + await waitForReindexToComplete(test1); + + await assertQueueState(test2, 2); + await waitForReindexToComplete(test2); + + await assertQueueState(test3, 1); + await waitForReindexToComplete(test3); + + await assertQueueState(undefined, 0); + + // Check that the closed index is still closed after reindexing + const resolvedIndices = await es.indices.resolveIndex({ + name: nameOfIndexThatShouldBeClosed, + }); + + const test1ReindexedState = getIndexState( + nameOfIndexThatShouldBeClosed, + resolvedIndices as ResolveIndexResponseFromES + ); + expect(test1ReindexedState).to.be('closed'); + } finally { + await cleanupReindex(test1); + await cleanupReindex(test2); + await cleanupReindex(test3); + } + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/graph.ts b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/graph.ts index aaccdd0e9a41..2cbe40e94549 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/graph.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/graph.ts @@ -49,7 +49,7 @@ export default function ({ getService }: FtrProviderContext) { it('should return an empty graph', async () => { const response = await postGraph(supertestViewer, { query: { - eventIds: [], + originEventIds: [], start: 'now-1d/d', end: 'now/d', }, @@ -63,7 +63,7 @@ export default function ({ getService }: FtrProviderContext) { it('should return a graph with nodes and edges by actor', async () => { const response = await postGraph(supertestViewer, { query: { - eventIds: [], + originEventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', esQuery: { diff --git a/x-pack/test_serverless/functional/test_suites/search/inference_management.ts b/x-pack/test_serverless/functional/test_suites/search/inference_management.ts index c22694d0b648..fce97e3bbf47 100644 --- a/x-pack/test_serverless/functional/test_suites/search/inference_management.ts +++ b/x-pack/test_serverless/functional/test_suites/search/inference_management.ts @@ -23,6 +23,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const ml = getService('ml'); describe('Serverless Inference Management UI', function () { + // see details: https://github.com/elastic/kibana/issues/204539 + this.tags(['failsOnMKI']); const endpoint = 'endpoint-1'; const taskType = 'sparse_embedding'; const modelConfig = { diff --git a/yarn.lock b/yarn.lock index 561c7c92280e..90597fb5545c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6137,7 +6137,7 @@ version "0.0.0" uid "" -"@kbn/lists-plugin@link:x-pack/plugins/lists": +"@kbn/lists-plugin@link:x-pack/solutions/security/plugins/lists": version "0.0.0" uid "" @@ -6953,7 +6953,7 @@ version "0.0.0" uid "" -"@kbn/screenshotting-plugin@link:x-pack/plugins/screenshotting": +"@kbn/screenshotting-plugin@link:x-pack/platform/plugins/shared/screenshotting": version "0.0.0" uid "" @@ -7129,7 +7129,7 @@ version "0.0.0" uid "" -"@kbn/securitysolution-autocomplete@link:packages/kbn-securitysolution-autocomplete": +"@kbn/securitysolution-autocomplete@link:x-pack/solutions/security/packages/kbn-securitysolution-autocomplete": version "0.0.0" uid "" @@ -7141,71 +7141,71 @@ version "0.0.0" uid "" -"@kbn/securitysolution-endpoint-exceptions-common@link:packages/kbn-securitysolution-endpoint-exceptions-common": +"@kbn/securitysolution-endpoint-exceptions-common@link:x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common": version "0.0.0" uid "" -"@kbn/securitysolution-es-utils@link:packages/kbn-securitysolution-es-utils": +"@kbn/securitysolution-es-utils@link:src/platform/packages/shared/kbn-securitysolution-es-utils": version "0.0.0" uid "" -"@kbn/securitysolution-exception-list-components@link:packages/kbn-securitysolution-exception-list-components": +"@kbn/securitysolution-exception-list-components@link:x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components": version "0.0.0" uid "" -"@kbn/securitysolution-exceptions-common@link:packages/kbn-securitysolution-exceptions-common": +"@kbn/securitysolution-exceptions-common@link:x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common": version "0.0.0" uid "" -"@kbn/securitysolution-hook-utils@link:packages/kbn-securitysolution-hook-utils": +"@kbn/securitysolution-hook-utils@link:x-pack/solutions/security/packages/kbn-securitysolution-hook-utils": version "0.0.0" uid "" -"@kbn/securitysolution-io-ts-alerting-types@link:packages/kbn-securitysolution-io-ts-alerting-types": +"@kbn/securitysolution-io-ts-alerting-types@link:x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types": version "0.0.0" uid "" -"@kbn/securitysolution-io-ts-list-types@link:packages/kbn-securitysolution-io-ts-list-types": +"@kbn/securitysolution-io-ts-list-types@link:x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types": version "0.0.0" uid "" -"@kbn/securitysolution-io-ts-types@link:packages/kbn-securitysolution-io-ts-types": +"@kbn/securitysolution-io-ts-types@link:src/platform/packages/shared/kbn-securitysolution-io-ts-types": version "0.0.0" uid "" -"@kbn/securitysolution-io-ts-utils@link:packages/kbn-securitysolution-io-ts-utils": +"@kbn/securitysolution-io-ts-utils@link:src/platform/packages/shared/kbn-securitysolution-io-ts-utils": version "0.0.0" uid "" -"@kbn/securitysolution-list-api@link:packages/kbn-securitysolution-list-api": +"@kbn/securitysolution-list-api@link:x-pack/solutions/security/packages/kbn-securitysolution-list-api": version "0.0.0" uid "" -"@kbn/securitysolution-list-constants@link:packages/kbn-securitysolution-list-constants": +"@kbn/securitysolution-list-constants@link:x-pack/solutions/security/packages/kbn-securitysolution-list-constants": version "0.0.0" uid "" -"@kbn/securitysolution-list-hooks@link:packages/kbn-securitysolution-list-hooks": +"@kbn/securitysolution-list-hooks@link:x-pack/solutions/security/packages/kbn-securitysolution-list-hooks": version "0.0.0" uid "" -"@kbn/securitysolution-list-utils@link:packages/kbn-securitysolution-list-utils": +"@kbn/securitysolution-list-utils@link:x-pack/solutions/security/packages/kbn-securitysolution-list-utils": version "0.0.0" uid "" -"@kbn/securitysolution-lists-common@link:packages/kbn-securitysolution-lists-common": +"@kbn/securitysolution-lists-common@link:x-pack/solutions/security/packages/kbn-securitysolution-lists-common": version "0.0.0" uid "" -"@kbn/securitysolution-rules@link:packages/kbn-securitysolution-rules": +"@kbn/securitysolution-rules@link:src/platform/packages/shared/kbn-securitysolution-rules": version "0.0.0" uid "" -"@kbn/securitysolution-t-grid@link:packages/kbn-securitysolution-t-grid": +"@kbn/securitysolution-t-grid@link:x-pack/solutions/security/packages/kbn-securitysolution-t-grid": version "0.0.0" uid "" -"@kbn/securitysolution-utils@link:packages/kbn-securitysolution-utils": +"@kbn/securitysolution-utils@link:x-pack/solutions/security/packages/kbn-securitysolution-utils": version "0.0.0" uid ""