diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 721de7917534d..0b56007cf9aeb 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -128,7 +128,12 @@ enabled: - x-pack/test/alerting_api_integration/security_and_spaces/group2/config.ts - x-pack/test/alerting_api_integration/security_and_spaces/group3/config.ts - x-pack/test/alerting_api_integration/security_and_spaces/group2/config_non_dedicated_task_runner.ts - - x-pack/test/alerting_api_integration/spaces_only/config.ts + - x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/config.ts + - x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/config.ts + - x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/config.ts + - x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/config.ts + - x-pack/test/alerting_api_integration/spaces_only/tests/actions/config.ts + - x-pack/test/alerting_api_integration/spaces_only/tests/action_task_params/config.ts - x-pack/test/api_integration_basic/config.ts - x-pack/test/api_integration/config_security_basic.ts - x-pack/test/api_integration/config_security_trial.ts @@ -161,7 +166,16 @@ enabled: - x-pack/test/fleet_api_integration/config.ts - x-pack/test/fleet_functional/config.ts - x-pack/test/ftr_apis/security_and_spaces/config.ts - - x-pack/test/functional_basic/config.ts + - x-pack/test/functional_basic/apps/ml/permissions/config.ts + - x-pack/test/functional_basic/apps/ml/data_visualizer/group1/config.ts + - x-pack/test/functional_basic/apps/ml/data_visualizer/group2/config.ts + - x-pack/test/functional_basic/apps/ml/data_visualizer/group3/config.ts + - x-pack/test/functional_basic/apps/transform/creation/index_pattern/config.ts + - x-pack/test/functional_basic/apps/transform/start_reset_delete/config.ts + - x-pack/test/functional_basic/apps/transform/edit_clone/config.ts + - x-pack/test/functional_basic/apps/transform/creation/runtime_mappings_saved_search/config.ts + - x-pack/test/functional_basic/apps/transform/permissions/config.ts + - x-pack/test/functional_basic/apps/transform/feature_controls/config.ts - x-pack/test/functional_cors/config.ts - x-pack/test/functional_embedded/config.ts - x-pack/test/functional_enterprise_search/without_host_configured.config.ts @@ -216,7 +230,12 @@ enabled: - x-pack/test/functional/apps/snapshot_restore/config.ts - x-pack/test/functional/apps/spaces/config.ts - x-pack/test/functional/apps/status_page/config.ts - - x-pack/test/functional/apps/transform/config.ts + - x-pack/test/functional/apps/transform/creation/index_pattern/config.ts + - x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/config.ts + - x-pack/test/functional/apps/transform/start_reset_delete/config.ts + - x-pack/test/functional/apps/transform/edit_clone/config.ts + - x-pack/test/functional/apps/transform/permissions/config.ts + - x-pack/test/functional/apps/transform/feature_controls/config.ts - x-pack/test/functional/apps/upgrade_assistant/config.ts - x-pack/test/functional/apps/uptime/config.ts - x-pack/test/functional/apps/visualize/config.ts diff --git a/.buildkite/scripts/steps/test/kbn_handlebars.sh b/.buildkite/scripts/steps/test/kbn_handlebars.sh index 0e7fc6ebb0648..b66bc75679211 100755 --- a/.buildkite/scripts/steps/test/kbn_handlebars.sh +++ b/.buildkite/scripts/steps/test/kbn_handlebars.sh @@ -4,5 +4,5 @@ set -euo pipefail source .buildkite/scripts/common/util.sh -echo '--- Checking for @kbn/handlebars test changes' -packages/kbn-handlebars/scripts/check_for_test_changes.sh +echo '--- Checking for @kbn/handlebars upstream updates' +packages/kbn-handlebars/scripts/check_for_upstream_updates.sh diff --git a/.eslintrc.js b/.eslintrc.js index 8fd3029a0592c..d79b0400368ab 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -518,7 +518,7 @@ module.exports = { }, }, { - files: ['packages/kbn-handlebars/src/upstream/**/*.{js,mjs,ts,tsx}'], + files: ['packages/kbn-handlebars/src/spec/**/*.{js,mjs,ts,tsx}'], rules: { '@kbn/eslint/require-license-header': [ 'error', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 59455a100f427..524d2abbc7f9e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1040,9 +1040,9 @@ packages/shared-ux/button/exit_full_screen/types @elastic/appex-sharedux packages/shared-ux/card/no_data/impl @elastic/appex-sharedux packages/shared-ux/card/no_data/mocks @elastic/appex-sharedux packages/shared-ux/card/no_data/types @elastic/appex-sharedux -packages/shared-ux/code_editor/impl @elastic/shared-ux -packages/shared-ux/code_editor/mocks @elastic/shared-ux -packages/shared-ux/code_editor/types @elastic/shared-ux +packages/shared-ux/code_editor/impl @elastic/appex-sharedux +packages/shared-ux/code_editor/mocks @elastic/appex-sharedux +packages/shared-ux/code_editor/types @elastic/appex-sharedux packages/shared-ux/file/context @elastic/appex-sharedux packages/shared-ux/file/file_picker/impl @elastic/appex-sharedux packages/shared-ux/file/file_upload/impl @elastic/appex-sharedux diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 806c6cbf07a0a..f221a780c7422 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -17,14 +17,13 @@ jobs: fail-fast: false matrix: language: [ 'javascript' ] - # branch: [ 'main', '7.17' ] + branch: [ 'main', '7.17' ] steps: - name: Checkout repository uses: actions/checkout@v3 - # TODO: Enable once a `.github/codeql/codeql-config.yml` file has been committed to 7.17 - # with: - # ref: ${{ matrix.branch }} + with: + ref: ${{ matrix.branch }} - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index a36bd508f0ad0..ef292b2861922 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 6855f8f9002c8..df559d1d23dfd 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 2afb1f1ffe062..d1154365f6269 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index ed0c19af12faf..6c7bfae415297 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index ec829fca26741..95bf2cd443bc6 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -202,7 +202,7 @@ "APMPluginSetupDependencies", ") => { config$: ", "Observable", - "; autoCreateApmDataView: boolean; serviceMapEnabled: boolean; serviceMapFingerprintBucketSize: number; serviceMapFingerprintGlobalBucketSize: number; serviceMapTraceIdBucketSize: number; serviceMapTraceIdGlobalBucketSize: number; serviceMapMaxTracesPerRequest: number; ui: Readonly<{} & { enabled: boolean; transactionGroupBucketSize: number; maxTraceItems: number; }>; searchAggregatedTransactions: ", + "; autoCreateApmDataView: boolean; serviceMapEnabled: boolean; serviceMapFingerprintBucketSize: number; serviceMapFingerprintGlobalBucketSize: number; serviceMapTraceIdBucketSize: number; serviceMapTraceIdGlobalBucketSize: number; serviceMapMaxTracesPerRequest: number; ui: Readonly<{} & { enabled: boolean; maxTraceItems: number; }>; searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", "; telemetryCollectionEnabled: boolean; metricsInterval: number; agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; forceSyntheticSource: boolean; }>>; getApmIndices: () => Promise>; createApmEventClient: ({ request, context, debug, }: { debug?: boolean | undefined; request: ", { @@ -448,7 +448,7 @@ "label": "config", "description": [], "signature": [ - "{ readonly indices: Readonly<{} & { metric: string; error: string; span: string; transaction: string; onboarding: string; }>; readonly autoCreateApmDataView: boolean; readonly serviceMapEnabled: boolean; readonly serviceMapFingerprintBucketSize: number; readonly serviceMapFingerprintGlobalBucketSize: number; readonly serviceMapTraceIdBucketSize: number; readonly serviceMapTraceIdGlobalBucketSize: number; readonly serviceMapMaxTracesPerRequest: number; readonly ui: Readonly<{} & { enabled: boolean; transactionGroupBucketSize: number; maxTraceItems: number; }>; readonly searchAggregatedTransactions: ", + "{ readonly indices: Readonly<{} & { metric: string; error: string; span: string; transaction: string; onboarding: string; }>; readonly autoCreateApmDataView: boolean; readonly serviceMapEnabled: boolean; readonly serviceMapFingerprintBucketSize: number; readonly serviceMapFingerprintGlobalBucketSize: number; readonly serviceMapTraceIdBucketSize: number; readonly serviceMapTraceIdGlobalBucketSize: number; readonly serviceMapMaxTracesPerRequest: number; readonly ui: Readonly<{} & { enabled: boolean; maxTraceItems: number; }>; readonly searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", "; readonly telemetryCollectionEnabled: boolean; readonly metricsInterval: number; readonly agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; readonly forceSyntheticSource: boolean; }" ], @@ -840,7 +840,7 @@ "label": "APMConfig", "description": [], "signature": [ - "{ readonly indices: Readonly<{} & { metric: string; error: string; span: string; transaction: string; onboarding: string; }>; readonly autoCreateApmDataView: boolean; readonly serviceMapEnabled: boolean; readonly serviceMapFingerprintBucketSize: number; readonly serviceMapFingerprintGlobalBucketSize: number; readonly serviceMapTraceIdBucketSize: number; readonly serviceMapTraceIdGlobalBucketSize: number; readonly serviceMapMaxTracesPerRequest: number; readonly ui: Readonly<{} & { enabled: boolean; transactionGroupBucketSize: number; maxTraceItems: number; }>; readonly searchAggregatedTransactions: ", + "{ readonly indices: Readonly<{} & { metric: string; error: string; span: string; transaction: string; onboarding: string; }>; readonly autoCreateApmDataView: boolean; readonly serviceMapEnabled: boolean; readonly serviceMapFingerprintBucketSize: number; readonly serviceMapFingerprintGlobalBucketSize: number; readonly serviceMapTraceIdBucketSize: number; readonly serviceMapTraceIdGlobalBucketSize: number; readonly serviceMapMaxTracesPerRequest: number; readonly ui: Readonly<{} & { enabled: boolean; maxTraceItems: number; }>; readonly searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", "; readonly telemetryCollectionEnabled: boolean; readonly metricsInterval: number; readonly agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; readonly forceSyntheticSource: boolean; }" ], @@ -4798,7 +4798,7 @@ "section": "def-server.APMRouteHandlerResources", "text": "APMRouteHandlerResources" }, - ", { transactionGroups: { transactionType: string; name: string; latency: number | null; throughput: number; errorRate: number; impact: number; }[]; isAggregationAccurate: boolean; bucketSize: number; }, ", + ", { transactionGroups: { transactionType: string; name: string; latency: number | null; throughput: number; errorRate: number; impact: number; }[]; transactionOverflowCount: number; maxTransactionGroupsExceeded: boolean; }, ", "APMRouteCreateOptions", ">; \"GET /internal/apm/traces/{traceId}/spans/{spanId}\": ", { @@ -6396,7 +6396,7 @@ "AgentName", "; } & { serviceName: string; healthStatus: ", "ServiceHealthStatus", - "; } & { serviceName: string; alertsCount: number; }>; }, ", + "; } & { serviceName: string; alertsCount: number; }>; maxServiceCountExceeded: boolean; serviceOverflowCount: number; }, ", "APMRouteCreateOptions", ">; \"GET /internal/apm/service-map/dependency\": ", { @@ -7679,7 +7679,7 @@ "description": [], "signature": [ "Observable", - "; autoCreateApmDataView: boolean; serviceMapEnabled: boolean; serviceMapFingerprintBucketSize: number; serviceMapFingerprintGlobalBucketSize: number; serviceMapTraceIdBucketSize: number; serviceMapTraceIdGlobalBucketSize: number; serviceMapMaxTracesPerRequest: number; ui: Readonly<{} & { enabled: boolean; transactionGroupBucketSize: number; maxTraceItems: number; }>; searchAggregatedTransactions: ", + "; autoCreateApmDataView: boolean; serviceMapEnabled: boolean; serviceMapFingerprintBucketSize: number; serviceMapFingerprintGlobalBucketSize: number; serviceMapTraceIdBucketSize: number; serviceMapTraceIdGlobalBucketSize: number; serviceMapMaxTracesPerRequest: number; ui: Readonly<{} & { enabled: boolean; maxTraceItems: number; }>; searchAggregatedTransactions: ", "SearchAggregatedTransactionSetting", "; telemetryCollectionEnabled: boolean; metricsInterval: number; agent: Readonly<{} & { migrations: Readonly<{} & { enabled: boolean; }>; }>; forceSyntheticSource: boolean; }>>" ], diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index a0a63f1dd4367..c34d16be46cf6 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index bb08afc588830..a3b03ad4a4452 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index eae171c59adc6..908a8ec253f33 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 0d228f8ad9a4d..f771a12f1eba2 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 227d548020973..a9357e0c71e8b 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 7743510f26d88..048018c70181e 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index fe1dec5fe6310..45aa0fa519fd9 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index ce2e358ea9f50..84bef08b70fa7 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index a90893f737fb1..23d0f69cd9546 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index 0f4a756b7d724..ab988904fc49b 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index d82d0233f9c1b..a1f8d009a7ab5 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index ce8800ed5cb1c..298bc5370283d 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 35c90d18566f3..437b9eb6c4613 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.devdocs.json b/api_docs/content_management.devdocs.json new file mode 100644 index 0000000000000..338c6568fdb4d --- /dev/null +++ b/api_docs/content_management.devdocs.json @@ -0,0 +1,100 @@ +{ + "id": "contentManagement", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [], + "start": { + "parentPluginId": "contentManagement", + "id": "def-public.ContentManagementPublicStart", + "type": "Interface", + "tags": [], + "label": "ContentManagementPublicStart", + "description": [], + "path": "src/plugins/content_management/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "start", + "initialIsOpen": true + } + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [], + "setup": { + "parentPluginId": "contentManagement", + "id": "def-server.ContentManagementServerSetup", + "type": "Interface", + "tags": [], + "label": "ContentManagementServerSetup", + "description": [], + "path": "src/plugins/content_management/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "setup", + "initialIsOpen": true + }, + "start": { + "parentPluginId": "contentManagement", + "id": "def-server.ContentManagementServerStart", + "type": "Interface", + "tags": [], + "label": "ContentManagementServerStart", + "description": [], + "path": "src/plugins/content_management/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "start", + "initialIsOpen": true + } + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "contentManagement", + "id": "def-common.API_ENDPOINT", + "type": "string", + "tags": [], + "label": "API_ENDPOINT", + "description": [], + "signature": [ + "\"/api/content_management\"" + ], + "path": "src/plugins/content_management/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.PLUGIN_ID", + "type": "string", + "tags": [], + "label": "PLUGIN_ID", + "description": [], + "signature": [ + "\"contentManagement\"" + ], + "path": "src/plugins/content_management/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx new file mode 100644 index 0000000000000..1964343a7d3eb --- /dev/null +++ b/api_docs/content_management.mdx @@ -0,0 +1,43 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibContentManagementPluginApi +slug: /kibana-dev-docs/api/contentManagement +title: "contentManagement" +image: https://source.unsplash.com/400x175/?github +description: API docs for the contentManagement plugin +date: 2023-02-02 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] +--- +import contentManagementObj from './content_management.devdocs.json'; + +Content management app + +Contact [@elastic/kibana-global-experience](https://github.com/orgs/elastic/teams/@elastic/kibana-global-experience) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 5 | 0 | 5 | 0 | + +## Client + +### Start + + +## Server + +### Setup + + +### Start + + +## Common + +### Consts, variables and types + + diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 266c100addc1c..19ce784eea455 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/core.devdocs.json b/api_docs/core.devdocs.json index 04ff270cbf464..d254c4480e9a8 100644 --- a/api_docs/core.devdocs.json +++ b/api_docs/core.devdocs.json @@ -5098,27 +5098,27 @@ }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_selector/use_recently_viewed_monitors.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_selector/use_recently_viewed_monitors.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts" }, { "plugin": "synthetics", @@ -5408,6 +5408,110 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "core", + "id": "def-public.CustomBrandingSetup", + "type": "Interface", + "tags": [], + "label": "CustomBrandingSetup", + "description": [], + "path": "packages/core/custom-branding/core-custom-branding-browser/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-public.CustomBrandingSetup.customBranding$", + "type": "Object", + "tags": [], + "label": "customBranding$", + "description": [], + "signature": [ + "Observable", + "<", + { + "pluginId": "@kbn/core-custom-branding-common", + "scope": "common", + "docId": "kibKbnCoreCustomBrandingCommonPluginApi", + "section": "def-common.CustomBranding", + "text": "CustomBranding" + }, + ">" + ], + "path": "packages/core/custom-branding/core-custom-branding-browser/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "core", + "id": "def-public.CustomBrandingSetup.hasCustomBranding$", + "type": "Object", + "tags": [], + "label": "hasCustomBranding$", + "description": [], + "signature": [ + "Observable", + "" + ], + "path": "packages/core/custom-branding/core-custom-branding-browser/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "core", + "id": "def-public.CustomBrandingStart", + "type": "Interface", + "tags": [], + "label": "CustomBrandingStart", + "description": [], + "path": "packages/core/custom-branding/core-custom-branding-browser/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-public.CustomBrandingStart.customBranding$", + "type": "Object", + "tags": [], + "label": "customBranding$", + "description": [], + "signature": [ + "Observable", + "<", + { + "pluginId": "@kbn/core-custom-branding-common", + "scope": "common", + "docId": "kibKbnCoreCustomBrandingCommonPluginApi", + "section": "def-common.CustomBranding", + "text": "CustomBranding" + }, + ">" + ], + "path": "packages/core/custom-branding/core-custom-branding-browser/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "core", + "id": "def-public.CustomBrandingStart.hasCustomBranding$", + "type": "Object", + "tags": [], + "label": "hasCustomBranding$", + "description": [], + "signature": [ + "Observable", + "" + ], + "path": "packages/core/custom-branding/core-custom-branding-browser/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-public.DeprecationsServiceStart", @@ -15033,15 +15137,15 @@ }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts" }, { "plugin": "graph", @@ -15061,11 +15165,11 @@ }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.test.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.test.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.test.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.test.ts" }, { "plugin": "savedObjects", @@ -21018,6 +21122,14 @@ "plugin": "cases", "path": "x-pack/plugins/cases/server/services/so_references.ts" }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/server/services/so_references.ts" + }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/server/services/so_references.ts" + }, { "plugin": "lists", "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.mock.ts" @@ -22727,6 +22839,18 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/share_action.ts" }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts" + }, { "plugin": "infra", "path": "x-pack/plugins/infra/public/common/visualizations/lens/types.ts" @@ -22798,6 +22922,14 @@ { "plugin": "cases", "path": "x-pack/plugins/cases/server/services/user_actions/test_utils.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.test.ts" } ], "initialIsOpen": false @@ -32746,6 +32878,148 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "core", + "id": "def-server.CustomBrandingSetup", + "type": "Interface", + "tags": [], + "label": "CustomBrandingSetup", + "description": [], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.CustomBrandingSetup.register", + "type": "Function", + "tags": [], + "label": "register", + "description": [], + "signature": [ + "(fetchFn: ", + { + "pluginId": "@kbn/core-custom-branding-server", + "scope": "common", + "docId": "kibKbnCoreCustomBrandingServerPluginApi", + "section": "def-common.CustomBrandingFetchFn", + "text": "CustomBrandingFetchFn" + }, + ") => void" + ], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.CustomBrandingSetup.register.$1", + "type": "Function", + "tags": [], + "label": "fetchFn", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-custom-branding-server", + "scope": "common", + "docId": "kibKbnCoreCustomBrandingServerPluginApi", + "section": "def-common.CustomBrandingFetchFn", + "text": "CustomBrandingFetchFn" + } + ], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.CustomBrandingSetup.getBrandingFor", + "type": "Function", + "tags": [], + "label": "getBrandingFor", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", options: { unauthenticated?: boolean | undefined; }) => Promise<", + { + "pluginId": "@kbn/core-custom-branding-common", + "scope": "common", + "docId": "kibKbnCoreCustomBrandingCommonPluginApi", + "section": "def-common.CustomBranding", + "text": "CustomBranding" + }, + ">" + ], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.CustomBrandingSetup.getBrandingFor.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.CustomBrandingSetup.getBrandingFor.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.CustomBrandingSetup.getBrandingFor.$2.unauthenticated", + "type": "CompoundType", + "tags": [], + "label": "unauthenticated", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-server.CustomHttpResponseOptions", diff --git a/api_docs/core.mdx b/api_docs/core.mdx index da92cad1c112c..8cf73d1db3450 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github description: API docs for the core plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2832 | 17 | 1016 | 0 | +| 2845 | 17 | 1029 | 0 | ## Client diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 32e364fe911fb..2cca32c7fb2e3 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index 800cf884e2595..d49a3f1385889 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index cc5ff14a7dff3..8c8272de31cb6 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index b8f5b5f037f20..762d74c33d14e 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -3540,7 +3540,7 @@ "label": "getActiveIndexFilter", "description": [], "signature": [ - "() => string[]" + "() => any[]" ], "path": "src/plugins/data/common/search/search_source/search_source.ts", "deprecated": false, @@ -10174,7 +10174,7 @@ "section": "def-common.SearchSourceFields", "text": "SearchSourceFields" }, - "[K]; getActiveIndexFilter: () => string[]; getOwnField: any[]; getOwnField: string[]; getOwnField: any[]; getOwnField: string[]" + "() => any[]" ], "path": "src/plugins/data/common/search/search_source/search_source.ts", "deprecated": false, @@ -35050,7 +35050,7 @@ "section": "def-common.SearchSourceFields", "text": "SearchSourceFields" }, - "[K]; getActiveIndexFilter: () => string[]; getOwnField: any[]; getOwnField: | discover, monitoring | - | | | @kbn/core-saved-objects-common, @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-api-server, core, home, dataViews, discover, savedObjectsTagging, savedObjectsFinder, fleet, canvas, osquery, securitySolution, synthetics, savedObjects, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-server, @kbn/core-saved-objects-import-export-server-internal, apm, savedObjectsTaggingOss, savedObjectsManagement, cases, lists, upgradeAssistant, @kbn/core-ui-settings-server-internal, data, dashboard | - | | | @kbn/core-saved-objects-common, @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-api-server, core, home, dataViews, discover, savedObjectsTagging, savedObjectsFinder, fleet, canvas, osquery, securitySolution, synthetics, savedObjects, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-server, @kbn/core-saved-objects-import-export-server-internal, apm, savedObjectsTaggingOss, savedObjectsManagement, cases, lists, upgradeAssistant, @kbn/core-ui-settings-server-internal, data | - | -| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, dataViews, unifiedSearch, triggersActionsUi, savedObjectsManagement, controls, unifiedFieldList, lens, aiops, ml, infra, visTypeTimeseries, apm, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover, data | - | +| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, dataViews, unifiedSearch, triggersActionsUi, savedObjectsManagement, controls, unifiedFieldList, lens, discover, aiops, ml, infra, visTypeTimeseries, apm, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, data | - | | | discover | - | -| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, dataViews, unifiedSearch, triggersActionsUi, savedObjectsManagement, controls, unifiedFieldList, lens, aiops, ml, infra, visTypeTimeseries, apm, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover, data | - | -| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, data, unifiedSearch, triggersActionsUi, savedObjectsManagement, controls, unifiedFieldList, lens, aiops, ml, infra, visTypeTimeseries, apm, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover | - | +| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, dataViews, unifiedSearch, triggersActionsUi, savedObjectsManagement, controls, unifiedFieldList, lens, discover, aiops, ml, infra, visTypeTimeseries, apm, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, data | - | +| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, data, unifiedSearch, triggersActionsUi, savedObjectsManagement, controls, unifiedFieldList, lens, discover, aiops, ml, infra, visTypeTimeseries, apm, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega | - | | | data, discover, imageEmbeddable, embeddable | - | | | advancedSettings, discover | - | | | @kbn/core-plugins-browser-internal, @kbn/core-root-browser-internal, dataViews, home, data, savedObjects, embeddable, unifiedSearch, presentationUtil, visualizations, dashboard, lens, discover, cases, fileUpload, maps, ml, infra, fleet, canvas, dashboardEnhanced, graph, synthetics, transform, watcher, dataVisualizer, cloudSecurityPosture, securitySolution | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index fd4dae4f0f318..65180c999c8eb 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -478,8 +478,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject)+ 22 more | - | -| | [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject)+ 38 more | - | +| | [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject)+ 26 more | - | +| | [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [so_references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/so_references.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/operations/find.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject), [test_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/server/services/user_actions/test_utils.ts#:~:text=SavedObject)+ 44 more | - | | | [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=find) | - | | | [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=SimpleSavedObject), [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=SimpleSavedObject), [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=SimpleSavedObject), [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=SimpleSavedObject), [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=SimpleSavedObject), [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=SimpleSavedObject), [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=SimpleSavedObject), [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=SimpleSavedObject), [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=SimpleSavedObject), [saved_objects_finder.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx#:~:text=SimpleSavedObject)+ 1 more | - | | | [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/common/ui/types.ts#:~:text=ResolvedSimpleSavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/common/ui/types.ts#:~:text=ResolvedSimpleSavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/common/ui/types.ts#:~:text=ResolvedSimpleSavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cases/common/ui/types.ts#:~:text=ResolvedSimpleSavedObject) | - | @@ -752,11 +752,11 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [discover_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/services/discover_state.ts#:~:text=syncQueryStateWithUrl), [discover_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/services/discover_state.ts#:~:text=syncQueryStateWithUrl) | - | | | [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject) | - | | | [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject) | - | -| | [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title), [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title) | - | +| | [use_fetch_occurances_range.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/use_fetch_occurances_range.ts#:~:text=title), [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title), [use_fetch_occurances_range.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/use_fetch_occurances_range.ts#:~:text=title), [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title) | - | | | [saved_search_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx#:~:text=create), [saved_search_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx#:~:text=create) | - | | | [fetch_hits_in_interval.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/context/utils/fetch_hits_in_interval.ts#:~:text=EsQuerySearchAfter), [fetch_hits_in_interval.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/context/utils/fetch_hits_in_interval.ts#:~:text=EsQuerySearchAfter), [get_es_query_search_after.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/context/utils/get_es_query_search_after.ts#:~:text=EsQuerySearchAfter), [get_es_query_search_after.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/context/utils/get_es_query_search_after.ts#:~:text=EsQuerySearchAfter), [get_es_query_search_after.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/context/utils/get_es_query_search_after.ts#:~:text=EsQuerySearchAfter) | - | -| | [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title), [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title) | - | -| | [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title) | - | +| | [use_fetch_occurances_range.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/use_fetch_occurances_range.ts#:~:text=title), [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title), [use_fetch_occurances_range.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/use_fetch_occurances_range.ts#:~:text=title), [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title) | - | +| | [use_fetch_occurances_range.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/use_fetch_occurances_range.ts#:~:text=title), [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title) | - | | | [on_save_search.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx#:~:text=SavedObjectSaveModal), [on_save_search.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx#:~:text=SavedObjectSaveModal) | 8.8.0 | | | [saved_search_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx#:~:text=executeTriggerActions), [search_embeddable_factory.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/embeddable/search_embeddable_factory.ts#:~:text=executeTriggerActions), [plugin.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/plugin.tsx#:~:text=executeTriggerActions) | - | | | [ui_settings.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/server/ui_settings.ts#:~:text=metric), [ui_settings.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/server/ui_settings.ts#:~:text=metric), [ui_settings.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/server/ui_settings.ts#:~:text=metric) | - | @@ -976,7 +976,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectsCreateOptions), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectsCreateOptions), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectsCreateOptions), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectsCreateOptions) | - | | | [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectsBatchResponse), [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectsBatchResponse) | - | | | [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectAttributes), [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectAttributes), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectAttributes), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectAttributes), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectAttributes), [use_get_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_get_saved_object.tsx#:~:text=SavedObjectAttributes), [use_get_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_get_saved_object.tsx#:~:text=SavedObjectAttributes), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectAttributes), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectAttributes), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectAttributes)+ 2 more | - | -| | [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [memory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts#:~:text=SavedObjectReference), [memory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts#:~:text=SavedObjectReference), [rx.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts#:~:text=SavedObjectReference), [rx.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts#:~:text=SavedObjectReference)+ 6 more | - | +| | [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [memory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts#:~:text=SavedObjectReference)+ 11 more | - | | | [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=savedObjects), [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=savedObjects), [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=savedObjects), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=savedObjects), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=savedObjects), [use_delete_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_delete_saved_object.tsx#:~:text=savedObjects), [use_delete_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_delete_saved_object.tsx#:~:text=savedObjects), [use_get_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_get_saved_object.tsx#:~:text=savedObjects), [use_get_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_get_saved_object.tsx#:~:text=savedObjects), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=savedObjects)+ 1 more | - | | | [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=savedObjects), [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=savedObjects), [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=savedObjects), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=savedObjects), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=savedObjects), [use_delete_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_delete_saved_object.tsx#:~:text=savedObjects), [use_delete_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_delete_saved_object.tsx#:~:text=savedObjects), [use_get_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_get_saved_object.tsx#:~:text=savedObjects), [use_get_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_get_saved_object.tsx#:~:text=savedObjects), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=savedObjects)+ 1 more | - | | | [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=create) | - | @@ -988,7 +988,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectsCreateOptions), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectsCreateOptions), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectsCreateOptions), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectsCreateOptions) | - | | | [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectsBatchResponse), [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectsBatchResponse) | - | | | [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectAttributes), [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectAttributes), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectAttributes), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectAttributes), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectAttributes), [use_get_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_get_saved_object.tsx#:~:text=SavedObjectAttributes), [use_get_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_get_saved_object.tsx#:~:text=SavedObjectAttributes), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectAttributes), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectAttributes), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectAttributes)+ 2 more | - | -| | [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [memory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts#:~:text=SavedObjectReference), [memory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts#:~:text=SavedObjectReference), [rx.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts#:~:text=SavedObjectReference), [rx.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts#:~:text=SavedObjectReference)+ 6 more | - | +| | [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [memory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts#:~:text=SavedObjectReference)+ 11 more | - | @@ -1506,16 +1506,16 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | | [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts#:~:text=SavedObjectsClientContract), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts#:~:text=SavedObjectsClientContract), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts#:~:text=SavedObjectsClientContract), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/state/private_locations/api.ts#:~:text=SavedObjectsClientContract), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/state/private_locations/api.ts#:~:text=SavedObjectsClientContract), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/state/private_locations/api.ts#:~:text=SavedObjectsClientContract) | - | | | [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts#:~:text=create), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/state/private_locations/api.ts#:~:text=create) | - | | | [delete_param.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx#:~:text=bulkDelete) | - | -| | [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts#:~:text=find), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_location_monitors.ts#:~:text=find), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts#:~:text=find), [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts#:~:text=find), [use_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.ts#:~:text=find), [use_filters.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.test.ts#:~:text=find), [use_filters.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.test.ts#:~:text=find) | - | +| | [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts#:~:text=find), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_location_monitors.ts#:~:text=find), [use_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts#:~:text=find), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts#:~:text=find), [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts#:~:text=find), [use_filters.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.test.ts#:~:text=find), [use_filters.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.test.ts#:~:text=find) | - | | | [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts#:~:text=get), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/state/private_locations/api.ts#:~:text=get) | - | | | [use_invalid_monitors.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_invalid_monitors.tsx#:~:text=bulkResolve), [use_recently_viewed_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_selector/use_recently_viewed_monitors.ts#:~:text=bulkResolve) | - | | | [synthetics_monitor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/common/types/synthetics_monitor.ts#:~:text=SimpleSavedObject), [synthetics_monitor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/common/types/synthetics_monitor.ts#:~:text=SimpleSavedObject), [synthetics_monitor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/common/types/synthetics_monitor.ts#:~:text=SimpleSavedObject) | - | -| | [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts#:~:text=savedObjects), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_location_monitors.ts#:~:text=savedObjects), [use_locations_api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_locations_api.ts#:~:text=savedObjects), [use_invalid_monitors.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_invalid_monitors.tsx#:~:text=savedObjects), [delete_param.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx#:~:text=savedObjects), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts#:~:text=savedObjects), [use_locations_api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts#:~:text=savedObjects), [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts#:~:text=savedObjects), [use_recently_viewed_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_selector/use_recently_viewed_monitors.ts#:~:text=savedObjects), [use_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.ts#:~:text=savedObjects)+ 1 more | - | -| | [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts#:~:text=savedObjects), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_location_monitors.ts#:~:text=savedObjects), [use_locations_api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_locations_api.ts#:~:text=savedObjects), [use_invalid_monitors.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_invalid_monitors.tsx#:~:text=savedObjects), [delete_param.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx#:~:text=savedObjects), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts#:~:text=savedObjects), [use_locations_api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts#:~:text=savedObjects), [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts#:~:text=savedObjects), [use_recently_viewed_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_selector/use_recently_viewed_monitors.ts#:~:text=savedObjects), [use_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.ts#:~:text=savedObjects)+ 1 more | - | +| | [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts#:~:text=savedObjects), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_location_monitors.ts#:~:text=savedObjects), [use_locations_api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_locations_api.ts#:~:text=savedObjects), [use_invalid_monitors.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_invalid_monitors.tsx#:~:text=savedObjects), [use_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts#:~:text=savedObjects), [use_recently_viewed_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_selector/use_recently_viewed_monitors.ts#:~:text=savedObjects), [delete_param.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx#:~:text=savedObjects), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts#:~:text=savedObjects), [use_locations_api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts#:~:text=savedObjects), [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts#:~:text=savedObjects)+ 1 more | - | +| | [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts#:~:text=savedObjects), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_location_monitors.ts#:~:text=savedObjects), [use_locations_api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_locations_api.ts#:~:text=savedObjects), [use_invalid_monitors.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_invalid_monitors.tsx#:~:text=savedObjects), [use_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts#:~:text=savedObjects), [use_recently_viewed_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_selector/use_recently_viewed_monitors.ts#:~:text=savedObjects), [delete_param.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx#:~:text=savedObjects), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts#:~:text=savedObjects), [use_locations_api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts#:~:text=savedObjects), [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts#:~:text=savedObjects)+ 1 more | - | | | [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts#:~:text=SavedObjectsClientContract), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts#:~:text=SavedObjectsClientContract), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts#:~:text=SavedObjectsClientContract), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/state/private_locations/api.ts#:~:text=SavedObjectsClientContract), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/state/private_locations/api.ts#:~:text=SavedObjectsClientContract), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/state/private_locations/api.ts#:~:text=SavedObjectsClientContract) | - | | | [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts#:~:text=create), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/state/private_locations/api.ts#:~:text=create) | - | | | [delete_param.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx#:~:text=bulkDelete) | - | -| | [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts#:~:text=find), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_location_monitors.ts#:~:text=find), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts#:~:text=find), [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts#:~:text=find), [use_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.ts#:~:text=find), [use_filters.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.test.ts#:~:text=find), [use_filters.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.test.ts#:~:text=find) | - | +| | [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts#:~:text=find), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/manage_locations/hooks/use_location_monitors.ts#:~:text=find), [use_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts#:~:text=find), [use_location_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts#:~:text=find), [use_monitor_name.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts#:~:text=find), [use_filters.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.test.ts#:~:text=find), [use_filters.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.test.ts#:~:text=find) | - | | | [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/state/private_locations/api.ts#:~:text=get), [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/state/private_locations/api.ts#:~:text=get) | - | | | [use_invalid_monitors.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_invalid_monitors.tsx#:~:text=bulkResolve), [use_recently_viewed_monitors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_selector/use_recently_viewed_monitors.ts#:~:text=bulkResolve) | - | | | [synthetics_monitor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/common/types/synthetics_monitor.ts#:~:text=SimpleSavedObject), [synthetics_monitor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/common/types/synthetics_monitor.ts#:~:text=SimpleSavedObject), [synthetics_monitor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/synthetics/common/types/synthetics_monitor.ts#:~:text=SimpleSavedObject) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index f30544104b267..d68a5ca0d423b 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 021378947c8ee..0dc280c086702 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.devdocs.json b/api_docs/discover.devdocs.json index 438a91e5d2171..5a808736cc9b2 100644 --- a/api_docs/discover.devdocs.json +++ b/api_docs/discover.devdocs.json @@ -478,7 +478,7 @@ "section": "def-common.SearchSourceFields", "text": "SearchSourceFields" }, - "[K]; getActiveIndexFilter: () => string[]; getOwnField: any[]; getOwnField: >", + "label": "[key: string]: Partial Promise<", + " | undefined; } | undefined, context?: ", + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined, request?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined) => Promise<", { "pluginId": "fleet", "scope": "common", @@ -6073,6 +6095,50 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.create.$5", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.create.$6", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [] @@ -7086,7 +7152,23 @@ "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, - " | undefined; skipUnassignFromAgentPolicies?: boolean | undefined; force?: boolean | undefined; } | undefined) => Promise<", + " | undefined; skipUnassignFromAgentPolicies?: boolean | undefined; force?: boolean | undefined; } | undefined, context?: ", + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined, request?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined) => Promise<", { "pluginId": "fleet", "scope": "common", @@ -7218,6 +7300,50 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.delete.$5", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.delete.$6", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [] @@ -7743,6 +7869,14 @@ "section": "def-common.PackagePolicy", "text": "PackagePolicy" }, + " : A extends \"packagePolicyUpdate\" ? ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.UpdatePackagePolicy", + "text": "UpdatePackagePolicy" + }, " : ", { "pluginId": "fleet", @@ -7751,7 +7885,23 @@ "section": "def-common.NewPackagePolicy", "text": "NewPackagePolicy" }, - ", context: ", + ", soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", esClient: ", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" + }, + ", context?: ", { "pluginId": "@kbn/core-http-request-handler-context-server", "scope": "common", @@ -7759,7 +7909,7 @@ "section": "def-common.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", request: ", + " | undefined, request?: ", { "pluginId": "@kbn/core-http-server", "scope": "common", @@ -7767,7 +7917,7 @@ "section": "def-common.KibanaRequest", "text": "KibanaRequest" }, - ") => Promise | undefined) => Promise" + " | undefined" ], "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", "deprecated": false, "trackAdoption": false, - "isRequired": true + "isRequired": false } ], "returnComment": [] @@ -7900,7 +8109,39 @@ "signature": [ "(deletedPackagePolicies: ", "DeletePackagePoliciesResponse", - ") => Promise" + ", soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", esClient: ", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" + }, + ", context?: ", + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined, request?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined) => Promise" ], "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", "deprecated": false, @@ -7920,38 +8161,156 @@ "deprecated": false, "trackAdoption": false, "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "fleet", - "id": "def-server.PackagePolicyClient.runPostDeleteExternalCallbacks", - "type": "Function", - "tags": [], - "label": "runPostDeleteExternalCallbacks", - "description": [], - "signature": [ - "(deletedPackagePolicies: ", - { - "pluginId": "fleet", - "scope": "common", - "docId": "kibFleetPluginApi", - "section": "def-common.PostDeletePackagePoliciesResponse", - "text": "PostDeletePackagePoliciesResponse" }, - ") => Promise" - ], - "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ { "parentPluginId": "fleet", - "id": "def-server.PackagePolicyClient.runPostDeleteExternalCallbacks.$1", - "type": "Array", + "id": "def-server.PackagePolicyClient.runDeleteExternalCallbacks.$2", + "type": "Object", "tags": [], - "label": "deletedPackagePolicies", + "label": "soClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + } + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.runDeleteExternalCallbacks.$3", + "type": "Object", + "tags": [], + "label": "esClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" + } + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.runDeleteExternalCallbacks.$4", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.runDeleteExternalCallbacks.$5", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.runPostDeleteExternalCallbacks", + "type": "Function", + "tags": [], + "label": "runPostDeleteExternalCallbacks", + "description": [], + "signature": [ + "(deletedPackagePolicies: ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PostDeletePackagePoliciesResponse", + "text": "PostDeletePackagePoliciesResponse" + }, + ", soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", esClient: ", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" + }, + ", context?: ", + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined, request?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined) => Promise" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.runPostDeleteExternalCallbacks.$1", + "type": "Array", + "tags": [], + "label": "deletedPackagePolicies", "description": [], "signature": [ { @@ -7966,6 +8325,92 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.runPostDeleteExternalCallbacks.$2", + "type": "Object", + "tags": [], + "label": "soClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + } + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.runPostDeleteExternalCallbacks.$3", + "type": "Object", + "tags": [], + "label": "esClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" + } + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.runPostDeleteExternalCallbacks.$4", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackagePolicyClient.runPostDeleteExternalCallbacks.$5", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/package_policy_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [] @@ -8214,7 +8659,23 @@ "section": "def-common.NewPackagePolicy", "text": "NewPackagePolicy" }, - ", context: ", + ", soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", esClient: ", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" + }, + ", context?: ", { "pluginId": "@kbn/core-http-request-handler-context-server", "scope": "common", @@ -8222,7 +8683,7 @@ "section": "def-common.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", request: ", + " | undefined, request?: ", { "pluginId": "@kbn/core-http-server", "scope": "common", @@ -8230,7 +8691,7 @@ "section": "def-common.KibanaRequest", "text": "KibanaRequest" }, - ") => Promise<", + " | undefined) => Promise<", { "pluginId": "fleet", "scope": "common", @@ -8270,15 +8731,15 @@ "id": "def-server.PostPackagePolicyCreateCallback.$2", "type": "Object", "tags": [], - "label": "context", + "label": "soClient", "description": [], "signature": [ { - "pluginId": "@kbn/core-http-request-handler-context-server", + "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", - "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", - "section": "def-common.RequestHandlerContext", - "text": "RequestHandlerContext" + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" } ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", @@ -8290,28 +8751,1249 @@ "id": "def-server.PostPackagePolicyCreateCallback.$3", "type": "Object", "tags": [], - "label": "request", + "label": "esClient", "description": [], "signature": [ - { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - "" - ], - "path": "x-pack/plugins/fleet/server/types/extensions.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "fleet", - "id": "def-server.PostPackagePolicyDeleteCallback", + "{ name: string | symbol; get: { (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetResponse", + ">; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetResponse", + ", unknown>>; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetResponse", + ">; }; delete: { (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; cluster: ", + "default", + "; eql: ", + "default", + "; search: { >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchResponse", + ">; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchResponse", + ", unknown>>; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchResponse", + ">; }; create: { (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; monitoring: ", + "default", + "; security: ", + "default", + "; index: { (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; update: { (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateResponse", + ">; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateResponse", + ", unknown>>; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateResponse", + ">; }; asyncSearch: ", + "default", + "; [kAsyncSearch]: symbol | null; [kAutoscaling]: symbol | null; [kCat]: symbol | null; [kCcr]: symbol | null; [kCluster]: symbol | null; [kDanglingIndices]: symbol | null; [kEnrich]: symbol | null; [kEql]: symbol | null; [kFeatures]: symbol | null; [kFleet]: symbol | null; [kGraph]: symbol | null; [kIlm]: symbol | null; [kIndices]: symbol | null; [kIngest]: symbol | null; [kLicense]: symbol | null; [kLogstash]: symbol | null; [kMigration]: symbol | null; [kMl]: symbol | null; [kMonitoring]: symbol | null; [kNodes]: symbol | null; [kRollup]: symbol | null; [kSearchableSnapshots]: symbol | null; [kSecurity]: symbol | null; [kShutdown]: symbol | null; [kSlm]: symbol | null; [kSnapshot]: symbol | null; [kSql]: symbol | null; [kSsl]: symbol | null; [kTasks]: symbol | null; [kTextStructure]: symbol | null; [kTransform]: symbol | null; [kWatcher]: symbol | null; [kXpack]: symbol | null; transport: ", + "default", + "; helpers: ", + "default", + "; child: (opts: ", + "ClientOptions", + ") => ", + "default", + "; autoscaling: ", + "default", + "; bulk: { (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "BulkResponse", + ">; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "BulkResponse", + ", unknown>>; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "BulkResponse", + ">; }; cat: ", + "default", + "; ccr: ", + "default", + "; clearScroll: { (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClearScrollResponse", + ">; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClearScrollResponse", + ", unknown>>; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClearScrollResponse", + ">; }; closePointInTime: { (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClosePointInTimeResponse", + ", unknown>>; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; }; count: { (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "CountResponse", + ">; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "CountResponse", + ", unknown>>; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "CountResponse", + ">; }; danglingIndices: ", + "default", + "; deleteByQuery: { (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "DeleteByQueryResponse", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; }; deleteByQueryRethrottle: { (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TasksTaskListResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; }; deleteScript: { (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; enrich: ", + "default", + "; exists: { (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; existsSource: { (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; explain: { (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ExplainResponse", + ">; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ExplainResponse", + ", unknown>>; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ExplainResponse", + ">; }; features: ", + "default", + "; fieldCaps: { (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "FieldCapsResponse", + ">; (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "FieldCapsResponse", + ", unknown>>; (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "FieldCapsResponse", + ">; }; fleet: ", + "default", + "; getScript: { (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptResponse", + ">; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptResponse", + ", unknown>>; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptResponse", + ">; }; getScriptContext: { (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptContextResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; }; getScriptLanguages: { (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptLanguagesResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; }; getSource: { (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; graph: ", + "default", + "; ilm: ", + "default", + "; indices: ", + "default", + "; info: { (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "InfoResponse", + ">; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "InfoResponse", + ", unknown>>; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "InfoResponse", + ">; }; ingest: ", + "default", + "; knnSearch: { (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "KnnSearchResponse", + ">; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "KnnSearchResponse", + ", unknown>>; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "KnnSearchResponse", + ">; }; license: ", + "default", + "; logstash: ", + "default", + "; mget: { (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MgetResponse", + ">; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MgetResponse", + ", unknown>>; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MgetResponse", + ">; }; migration: ", + "default", + "; ml: ", + "default", + "; msearch: { >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchResponse", + ">; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchResponse", + ", unknown>>; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchResponse", + ">; }; msearchTemplate: { >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchTemplateResponse", + ", unknown>>; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; }; mtermvectors: { (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MtermvectorsResponse", + ", unknown>>; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; }; nodes: ", + "default", + "; openPointInTime: { (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "OpenPointInTimeResponse", + ", unknown>>; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; }; ping: { (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; putScript: { (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; rankEval: { (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RankEvalResponse", + ">; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RankEvalResponse", + ", unknown>>; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RankEvalResponse", + ">; }; reindex: { (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexResponse", + ">; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexResponse", + ", unknown>>; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexResponse", + ">; }; reindexRethrottle: { (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexRethrottleResponse", + ", unknown>>; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; }; renderSearchTemplate: { (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RenderSearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; }; rollup: ", + "default", + "; scriptsPainlessExecute: { (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScriptsPainlessExecuteResponse", + ", unknown>>; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; }; scroll: { >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScrollResponse", + ">; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScrollResponse", + ", unknown>>; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScrollResponse", + ">; }; searchMvt: { (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; searchShards: { (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchShardsResponse", + ">; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchShardsResponse", + ", unknown>>; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchShardsResponse", + ">; }; searchTemplate: { (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; }; searchableSnapshots: ", + "default", + "; shutdown: ", + "default", + "; slm: ", + "default", + "; snapshot: ", + "default", + "; sql: ", + "default", + "; ssl: ", + "default", + "; tasks: ", + "default", + "; termsEnum: { (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermsEnumResponse", + ">; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermsEnumResponse", + ", unknown>>; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermsEnumResponse", + ">; }; termvectors: { (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermvectorsResponse", + ">; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermvectorsResponse", + ", unknown>>; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermvectorsResponse", + ">; }; textStructure: ", + "default", + "; transform: ", + "default", + "; updateByQuery: { (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; }; updateByQueryRethrottle: { (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryRethrottleResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; }; watcher: ", + "default", + "; xpack: ", + "default", + "; }" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyCreateCallback.$4", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyCreateCallback.$5", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyDeleteCallback", "type": "Type", "tags": [], "label": "PostPackagePolicyDeleteCallback", @@ -8319,7 +10001,39 @@ "signature": [ "(packagePolicies: ", "DeletePackagePoliciesResponse", - ") => Promise" + ", soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", esClient: ", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" + }, + ", context?: ", + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined, request?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined) => Promise" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, @@ -8328,20 +10042,2630 @@ "children": [ { "parentPluginId": "fleet", - "id": "def-server.PostPackagePolicyDeleteCallback.$1", - "type": "Array", + "id": "def-server.PostPackagePolicyDeleteCallback.$1", + "type": "Array", + "tags": [], + "label": "packagePolicies", + "description": [], + "signature": [ + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PackagePolicy", + "text": "PackagePolicy" + }, + "[]" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyDeleteCallback.$2", + "type": "Object", + "tags": [], + "label": "soClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + } + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyDeleteCallback.$3", + "type": "Object", + "tags": [], + "label": "esClient", + "description": [], + "signature": [ + "{ name: string | symbol; get: { (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetResponse", + ">; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetResponse", + ", unknown>>; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetResponse", + ">; }; delete: { (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; cluster: ", + "default", + "; eql: ", + "default", + "; search: { >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchResponse", + ">; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchResponse", + ", unknown>>; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchResponse", + ">; }; create: { (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; monitoring: ", + "default", + "; security: ", + "default", + "; index: { (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; update: { (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateResponse", + ">; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateResponse", + ", unknown>>; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateResponse", + ">; }; asyncSearch: ", + "default", + "; [kAsyncSearch]: symbol | null; [kAutoscaling]: symbol | null; [kCat]: symbol | null; [kCcr]: symbol | null; [kCluster]: symbol | null; [kDanglingIndices]: symbol | null; [kEnrich]: symbol | null; [kEql]: symbol | null; [kFeatures]: symbol | null; [kFleet]: symbol | null; [kGraph]: symbol | null; [kIlm]: symbol | null; [kIndices]: symbol | null; [kIngest]: symbol | null; [kLicense]: symbol | null; [kLogstash]: symbol | null; [kMigration]: symbol | null; [kMl]: symbol | null; [kMonitoring]: symbol | null; [kNodes]: symbol | null; [kRollup]: symbol | null; [kSearchableSnapshots]: symbol | null; [kSecurity]: symbol | null; [kShutdown]: symbol | null; [kSlm]: symbol | null; [kSnapshot]: symbol | null; [kSql]: symbol | null; [kSsl]: symbol | null; [kTasks]: symbol | null; [kTextStructure]: symbol | null; [kTransform]: symbol | null; [kWatcher]: symbol | null; [kXpack]: symbol | null; transport: ", + "default", + "; helpers: ", + "default", + "; child: (opts: ", + "ClientOptions", + ") => ", + "default", + "; autoscaling: ", + "default", + "; bulk: { (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "BulkResponse", + ">; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "BulkResponse", + ", unknown>>; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "BulkResponse", + ">; }; cat: ", + "default", + "; ccr: ", + "default", + "; clearScroll: { (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClearScrollResponse", + ">; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClearScrollResponse", + ", unknown>>; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClearScrollResponse", + ">; }; closePointInTime: { (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClosePointInTimeResponse", + ", unknown>>; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; }; count: { (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "CountResponse", + ">; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "CountResponse", + ", unknown>>; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "CountResponse", + ">; }; danglingIndices: ", + "default", + "; deleteByQuery: { (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "DeleteByQueryResponse", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; }; deleteByQueryRethrottle: { (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TasksTaskListResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; }; deleteScript: { (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; enrich: ", + "default", + "; exists: { (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; existsSource: { (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; explain: { (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ExplainResponse", + ">; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ExplainResponse", + ", unknown>>; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ExplainResponse", + ">; }; features: ", + "default", + "; fieldCaps: { (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "FieldCapsResponse", + ">; (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "FieldCapsResponse", + ", unknown>>; (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "FieldCapsResponse", + ">; }; fleet: ", + "default", + "; getScript: { (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptResponse", + ">; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptResponse", + ", unknown>>; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptResponse", + ">; }; getScriptContext: { (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptContextResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; }; getScriptLanguages: { (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptLanguagesResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; }; getSource: { (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; graph: ", + "default", + "; ilm: ", + "default", + "; indices: ", + "default", + "; info: { (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "InfoResponse", + ">; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "InfoResponse", + ", unknown>>; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "InfoResponse", + ">; }; ingest: ", + "default", + "; knnSearch: { (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "KnnSearchResponse", + ">; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "KnnSearchResponse", + ", unknown>>; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "KnnSearchResponse", + ">; }; license: ", + "default", + "; logstash: ", + "default", + "; mget: { (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MgetResponse", + ">; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MgetResponse", + ", unknown>>; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MgetResponse", + ">; }; migration: ", + "default", + "; ml: ", + "default", + "; msearch: { >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchResponse", + ">; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchResponse", + ", unknown>>; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchResponse", + ">; }; msearchTemplate: { >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchTemplateResponse", + ", unknown>>; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; }; mtermvectors: { (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MtermvectorsResponse", + ", unknown>>; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; }; nodes: ", + "default", + "; openPointInTime: { (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "OpenPointInTimeResponse", + ", unknown>>; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; }; ping: { (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; putScript: { (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; rankEval: { (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RankEvalResponse", + ">; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RankEvalResponse", + ", unknown>>; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RankEvalResponse", + ">; }; reindex: { (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexResponse", + ">; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexResponse", + ", unknown>>; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexResponse", + ">; }; reindexRethrottle: { (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexRethrottleResponse", + ", unknown>>; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; }; renderSearchTemplate: { (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RenderSearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; }; rollup: ", + "default", + "; scriptsPainlessExecute: { (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScriptsPainlessExecuteResponse", + ", unknown>>; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; }; scroll: { >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScrollResponse", + ">; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScrollResponse", + ", unknown>>; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScrollResponse", + ">; }; searchMvt: { (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; searchShards: { (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchShardsResponse", + ">; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchShardsResponse", + ", unknown>>; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchShardsResponse", + ">; }; searchTemplate: { (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; }; searchableSnapshots: ", + "default", + "; shutdown: ", + "default", + "; slm: ", + "default", + "; snapshot: ", + "default", + "; sql: ", + "default", + "; ssl: ", + "default", + "; tasks: ", + "default", + "; termsEnum: { (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermsEnumResponse", + ">; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermsEnumResponse", + ", unknown>>; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermsEnumResponse", + ">; }; termvectors: { (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermvectorsResponse", + ">; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermvectorsResponse", + ", unknown>>; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermvectorsResponse", + ">; }; textStructure: ", + "default", + "; transform: ", + "default", + "; updateByQuery: { (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; }; updateByQueryRethrottle: { (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryRethrottleResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; }; watcher: ", + "default", + "; xpack: ", + "default", + "; }" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyDeleteCallback.$4", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyDeleteCallback.$5", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyPostCreateCallback", + "type": "Type", + "tags": [], + "label": "PostPackagePolicyPostCreateCallback", + "description": [], + "signature": [ + "(packagePolicy: ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PackagePolicy", + "text": "PackagePolicy" + }, + ", soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", esClient: ", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" + }, + ", context?: ", + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined, request?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + " | undefined) => Promise<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PackagePolicy", + "text": "PackagePolicy" + }, + ">" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyPostCreateCallback.$1", + "type": "Object", + "tags": [], + "label": "packagePolicy", + "description": [], + "signature": [ + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PackagePolicy", + "text": "PackagePolicy" + } + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyPostCreateCallback.$2", + "type": "Object", + "tags": [], + "label": "soClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + } + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyPostCreateCallback.$3", + "type": "Object", + "tags": [], + "label": "esClient", + "description": [], + "signature": [ + "{ name: string | symbol; get: { (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetResponse", + ">; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetResponse", + ", unknown>>; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetResponse", + ">; }; delete: { (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; cluster: ", + "default", + "; eql: ", + "default", + "; search: { >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchResponse", + ">; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchResponse", + ", unknown>>; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchResponse", + ">; }; create: { (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; monitoring: ", + "default", + "; security: ", + "default", + "; index: { (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; update: { (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateResponse", + ">; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateResponse", + ", unknown>>; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateResponse", + ">; }; asyncSearch: ", + "default", + "; [kAsyncSearch]: symbol | null; [kAutoscaling]: symbol | null; [kCat]: symbol | null; [kCcr]: symbol | null; [kCluster]: symbol | null; [kDanglingIndices]: symbol | null; [kEnrich]: symbol | null; [kEql]: symbol | null; [kFeatures]: symbol | null; [kFleet]: symbol | null; [kGraph]: symbol | null; [kIlm]: symbol | null; [kIndices]: symbol | null; [kIngest]: symbol | null; [kLicense]: symbol | null; [kLogstash]: symbol | null; [kMigration]: symbol | null; [kMl]: symbol | null; [kMonitoring]: symbol | null; [kNodes]: symbol | null; [kRollup]: symbol | null; [kSearchableSnapshots]: symbol | null; [kSecurity]: symbol | null; [kShutdown]: symbol | null; [kSlm]: symbol | null; [kSnapshot]: symbol | null; [kSql]: symbol | null; [kSsl]: symbol | null; [kTasks]: symbol | null; [kTextStructure]: symbol | null; [kTransform]: symbol | null; [kWatcher]: symbol | null; [kXpack]: symbol | null; transport: ", + "default", + "; helpers: ", + "default", + "; child: (opts: ", + "ClientOptions", + ") => ", + "default", + "; autoscaling: ", + "default", + "; bulk: { (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "BulkResponse", + ">; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "BulkResponse", + ", unknown>>; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "BulkResponse", + ">; }; cat: ", + "default", + "; ccr: ", + "default", + "; clearScroll: { (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClearScrollResponse", + ">; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClearScrollResponse", + ", unknown>>; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClearScrollResponse", + ">; }; closePointInTime: { (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClosePointInTimeResponse", + ", unknown>>; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; }; count: { (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "CountResponse", + ">; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "CountResponse", + ", unknown>>; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "CountResponse", + ">; }; danglingIndices: ", + "default", + "; deleteByQuery: { (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "DeleteByQueryResponse", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; }; deleteByQueryRethrottle: { (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TasksTaskListResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; }; deleteScript: { (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; enrich: ", + "default", + "; exists: { (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; existsSource: { (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; explain: { (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ExplainResponse", + ">; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ExplainResponse", + ", unknown>>; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ExplainResponse", + ">; }; features: ", + "default", + "; fieldCaps: { (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "FieldCapsResponse", + ">; (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "FieldCapsResponse", + ", unknown>>; (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "FieldCapsResponse", + ">; }; fleet: ", + "default", + "; getScript: { (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptResponse", + ">; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptResponse", + ", unknown>>; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptResponse", + ">; }; getScriptContext: { (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptContextResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; }; getScriptLanguages: { (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptLanguagesResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; }; getSource: { (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; graph: ", + "default", + "; ilm: ", + "default", + "; indices: ", + "default", + "; info: { (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "InfoResponse", + ">; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "InfoResponse", + ", unknown>>; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "InfoResponse", + ">; }; ingest: ", + "default", + "; knnSearch: { (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "KnnSearchResponse", + ">; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "KnnSearchResponse", + ", unknown>>; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "KnnSearchResponse", + ">; }; license: ", + "default", + "; logstash: ", + "default", + "; mget: { (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MgetResponse", + ">; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MgetResponse", + ", unknown>>; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MgetResponse", + ">; }; migration: ", + "default", + "; ml: ", + "default", + "; msearch: { >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchResponse", + ">; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchResponse", + ", unknown>>; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchResponse", + ">; }; msearchTemplate: { >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchTemplateResponse", + ", unknown>>; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; }; mtermvectors: { (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MtermvectorsResponse", + ", unknown>>; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; }; nodes: ", + "default", + "; openPointInTime: { (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "OpenPointInTimeResponse", + ", unknown>>; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; }; ping: { (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; putScript: { (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; rankEval: { (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RankEvalResponse", + ">; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RankEvalResponse", + ", unknown>>; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RankEvalResponse", + ">; }; reindex: { (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexResponse", + ">; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexResponse", + ", unknown>>; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexResponse", + ">; }; reindexRethrottle: { (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexRethrottleResponse", + ", unknown>>; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; }; renderSearchTemplate: { (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RenderSearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; }; rollup: ", + "default", + "; scriptsPainlessExecute: { (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScriptsPainlessExecuteResponse", + ", unknown>>; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; }; scroll: { >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScrollResponse", + ">; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScrollResponse", + ", unknown>>; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScrollResponse", + ">; }; searchMvt: { (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; searchShards: { (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchShardsResponse", + ">; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchShardsResponse", + ", unknown>>; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchShardsResponse", + ">; }; searchTemplate: { (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; }; searchableSnapshots: ", + "default", + "; shutdown: ", + "default", + "; slm: ", + "default", + "; snapshot: ", + "default", + "; sql: ", + "default", + "; ssl: ", + "default", + "; tasks: ", + "default", + "; termsEnum: { (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermsEnumResponse", + ">; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermsEnumResponse", + ", unknown>>; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermsEnumResponse", + ">; }; termvectors: { (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermvectorsResponse", + ">; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermvectorsResponse", + ", unknown>>; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermvectorsResponse", + ">; }; textStructure: ", + "default", + "; transform: ", + "default", + "; updateByQuery: { (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; }; updateByQueryRethrottle: { (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryRethrottleResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; }; watcher: ", + "default", + "; xpack: ", + "default", + "; }" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyPostCreateCallback.$4", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyPostCreateCallback.$5", + "type": "Object", "tags": [], - "label": "packagePolicies", + "label": "request", "description": [], "signature": [ { - "pluginId": "fleet", + "pluginId": "@kbn/core-http-server", "scope": "common", - "docId": "kibFleetPluginApi", - "section": "def-common.PackagePolicy", - "text": "PackagePolicy" + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" }, - "[]" + " | undefined" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, @@ -8352,21 +12676,39 @@ }, { "parentPluginId": "fleet", - "id": "def-server.PostPackagePolicyPostCreateCallback", + "id": "def-server.PostPackagePolicyPostDeleteCallback", "type": "Type", "tags": [], - "label": "PostPackagePolicyPostCreateCallback", + "label": "PostPackagePolicyPostDeleteCallback", "description": [], "signature": [ - "(packagePolicy: ", + "(deletedPackagePolicies: ", + "_DeepReadonlyArray", + "<{ id: string; name?: string | undefined; success: boolean; package?: ", { "pluginId": "fleet", "scope": "common", "docId": "kibFleetPluginApi", - "section": "def-common.PackagePolicy", - "text": "PackagePolicy" + "section": "def-common.PackagePolicyPackage", + "text": "PackagePolicyPackage" + }, + " | undefined; policy_id?: string | undefined; statusCode?: number | undefined; body?: { message: string; } | undefined; }>, soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", esClient: ", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" }, - ", context: ", + ", context?: ", { "pluginId": "@kbn/core-http-request-handler-context-server", "scope": "common", @@ -8374,7 +12716,7 @@ "section": "def-common.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", request: ", + " | undefined, request?: ", { "pluginId": "@kbn/core-http-server", "scope": "common", @@ -8382,15 +12724,7 @@ "section": "def-common.KibanaRequest", "text": "KibanaRequest" }, - ") => Promise<", - { - "pluginId": "fleet", - "scope": "common", - "docId": "kibFleetPluginApi", - "section": "def-common.PackagePolicy", - "text": "PackagePolicy" - }, - ">" + " | undefined) => Promise" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, @@ -8399,19 +12733,22 @@ "children": [ { "parentPluginId": "fleet", - "id": "def-server.PostPackagePolicyPostCreateCallback.$1", + "id": "def-server.PostPackagePolicyPostDeleteCallback.$1", "type": "Object", "tags": [], - "label": "packagePolicy", + "label": "deletedPackagePolicies", "description": [], "signature": [ + "_DeepReadonlyArray", + "<{ id: string; name?: string | undefined; success: boolean; package?: ", { "pluginId": "fleet", "scope": "common", "docId": "kibFleetPluginApi", - "section": "def-common.PackagePolicy", - "text": "PackagePolicy" - } + "section": "def-common.PackagePolicyPackage", + "text": "PackagePolicyPackage" + }, + " | undefined; policy_id?: string | undefined; statusCode?: number | undefined; body?: { message: string; } | undefined; }>" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, @@ -8419,18 +12756,18 @@ }, { "parentPluginId": "fleet", - "id": "def-server.PostPackagePolicyPostCreateCallback.$2", + "id": "def-server.PostPackagePolicyPostDeleteCallback.$2", "type": "Object", "tags": [], - "label": "context", + "label": "soClient", "description": [], "signature": [ { - "pluginId": "@kbn/core-http-request-handler-context-server", + "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", - "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", - "section": "def-common.RequestHandlerContext", - "text": "RequestHandlerContext" + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" } ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", @@ -8439,71 +12776,1241 @@ }, { "parentPluginId": "fleet", - "id": "def-server.PostPackagePolicyPostCreateCallback.$3", + "id": "def-server.PostPackagePolicyPostDeleteCallback.$3", "type": "Object", "tags": [], - "label": "request", + "label": "esClient", "description": [], "signature": [ - { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - "" + "{ name: string | symbol; get: { (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetResponse", + ">; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetResponse", + ", unknown>>; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetResponse", + ">; }; delete: { (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; cluster: ", + "default", + "; eql: ", + "default", + "; search: { >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchResponse", + ">; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchResponse", + ", unknown>>; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchResponse", + ">; }; create: { (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; monitoring: ", + "default", + "; security: ", + "default", + "; index: { (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; update: { (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateResponse", + ">; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateResponse", + ", unknown>>; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateResponse", + ">; }; asyncSearch: ", + "default", + "; [kAsyncSearch]: symbol | null; [kAutoscaling]: symbol | null; [kCat]: symbol | null; [kCcr]: symbol | null; [kCluster]: symbol | null; [kDanglingIndices]: symbol | null; [kEnrich]: symbol | null; [kEql]: symbol | null; [kFeatures]: symbol | null; [kFleet]: symbol | null; [kGraph]: symbol | null; [kIlm]: symbol | null; [kIndices]: symbol | null; [kIngest]: symbol | null; [kLicense]: symbol | null; [kLogstash]: symbol | null; [kMigration]: symbol | null; [kMl]: symbol | null; [kMonitoring]: symbol | null; [kNodes]: symbol | null; [kRollup]: symbol | null; [kSearchableSnapshots]: symbol | null; [kSecurity]: symbol | null; [kShutdown]: symbol | null; [kSlm]: symbol | null; [kSnapshot]: symbol | null; [kSql]: symbol | null; [kSsl]: symbol | null; [kTasks]: symbol | null; [kTextStructure]: symbol | null; [kTransform]: symbol | null; [kWatcher]: symbol | null; [kXpack]: symbol | null; transport: ", + "default", + "; helpers: ", + "default", + "; child: (opts: ", + "ClientOptions", + ") => ", + "default", + "; autoscaling: ", + "default", + "; bulk: { (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "BulkResponse", + ">; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "BulkResponse", + ", unknown>>; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "BulkResponse", + ">; }; cat: ", + "default", + "; ccr: ", + "default", + "; clearScroll: { (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClearScrollResponse", + ">; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClearScrollResponse", + ", unknown>>; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClearScrollResponse", + ">; }; closePointInTime: { (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClosePointInTimeResponse", + ", unknown>>; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; }; count: { (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "CountResponse", + ">; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "CountResponse", + ", unknown>>; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "CountResponse", + ">; }; danglingIndices: ", + "default", + "; deleteByQuery: { (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "DeleteByQueryResponse", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; }; deleteByQueryRethrottle: { (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TasksTaskListResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; }; deleteScript: { (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; enrich: ", + "default", + "; exists: { (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; existsSource: { (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; explain: { (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ExplainResponse", + ">; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ExplainResponse", + ", unknown>>; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ExplainResponse", + ">; }; features: ", + "default", + "; fieldCaps: { (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "FieldCapsResponse", + ">; (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "FieldCapsResponse", + ", unknown>>; (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "FieldCapsResponse", + ">; }; fleet: ", + "default", + "; getScript: { (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptResponse", + ">; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptResponse", + ", unknown>>; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptResponse", + ">; }; getScriptContext: { (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptContextResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; }; getScriptLanguages: { (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptLanguagesResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; }; getSource: { (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; graph: ", + "default", + "; ilm: ", + "default", + "; indices: ", + "default", + "; info: { (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "InfoResponse", + ">; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "InfoResponse", + ", unknown>>; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "InfoResponse", + ">; }; ingest: ", + "default", + "; knnSearch: { (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "KnnSearchResponse", + ">; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "KnnSearchResponse", + ", unknown>>; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "KnnSearchResponse", + ">; }; license: ", + "default", + "; logstash: ", + "default", + "; mget: { (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MgetResponse", + ">; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MgetResponse", + ", unknown>>; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MgetResponse", + ">; }; migration: ", + "default", + "; ml: ", + "default", + "; msearch: { >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchResponse", + ">; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchResponse", + ", unknown>>; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchResponse", + ">; }; msearchTemplate: { >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchTemplateResponse", + ", unknown>>; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; }; mtermvectors: { (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MtermvectorsResponse", + ", unknown>>; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; }; nodes: ", + "default", + "; openPointInTime: { (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "OpenPointInTimeResponse", + ", unknown>>; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; }; ping: { (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; putScript: { (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; rankEval: { (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RankEvalResponse", + ">; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RankEvalResponse", + ", unknown>>; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RankEvalResponse", + ">; }; reindex: { (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexResponse", + ">; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexResponse", + ", unknown>>; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexResponse", + ">; }; reindexRethrottle: { (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexRethrottleResponse", + ", unknown>>; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; }; renderSearchTemplate: { (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RenderSearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; }; rollup: ", + "default", + "; scriptsPainlessExecute: { (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScriptsPainlessExecuteResponse", + ", unknown>>; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; }; scroll: { >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScrollResponse", + ">; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScrollResponse", + ", unknown>>; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScrollResponse", + ">; }; searchMvt: { (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; searchShards: { (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchShardsResponse", + ">; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchShardsResponse", + ", unknown>>; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchShardsResponse", + ">; }; searchTemplate: { (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; }; searchableSnapshots: ", + "default", + "; shutdown: ", + "default", + "; slm: ", + "default", + "; snapshot: ", + "default", + "; sql: ", + "default", + "; ssl: ", + "default", + "; tasks: ", + "default", + "; termsEnum: { (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermsEnumResponse", + ">; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermsEnumResponse", + ", unknown>>; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermsEnumResponse", + ">; }; termvectors: { (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermvectorsResponse", + ">; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermvectorsResponse", + ", unknown>>; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermvectorsResponse", + ">; }; textStructure: ", + "default", + "; transform: ", + "default", + "; updateByQuery: { (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; }; updateByQueryRethrottle: { (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryRethrottleResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; }; watcher: ", + "default", + "; xpack: ", + "default", + "; }" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "fleet", - "id": "def-server.PostPackagePolicyPostDeleteCallback", - "type": "Type", - "tags": [], - "label": "PostPackagePolicyPostDeleteCallback", - "description": [], - "signature": [ - "(deletedPackagePolicies: ", - "_DeepReadonlyArray", - "<{ id: string; name?: string | undefined; success: boolean; package?: ", + }, { - "pluginId": "fleet", - "scope": "common", - "docId": "kibFleetPluginApi", - "section": "def-common.PackagePolicyPackage", - "text": "PackagePolicyPackage" + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyPostDeleteCallback.$4", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-request-handler-context-server", + "scope": "common", + "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", + "section": "def-common.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " | undefined" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false }, - " | undefined; policy_id?: string | undefined; statusCode?: number | undefined; body?: { message: string; } | undefined; }>) => Promise" - ], - "path": "x-pack/plugins/fleet/server/types/extensions.ts", - "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ { "parentPluginId": "fleet", - "id": "def-server.PostPackagePolicyPostDeleteCallback.$1", + "id": "def-server.PostPackagePolicyPostDeleteCallback.$5", "type": "Object", "tags": [], - "label": "deletedPackagePolicies", + "label": "request", "description": [], "signature": [ - "_DeepReadonlyArray", - "<{ id: string; name?: string | undefined; success: boolean; package?: ", { - "pluginId": "fleet", + "pluginId": "@kbn/core-http-server", "scope": "common", - "docId": "kibFleetPluginApi", - "section": "def-common.PackagePolicyPackage", - "text": "PackagePolicyPackage" + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" }, - " | undefined; policy_id?: string | undefined; statusCode?: number | undefined; body?: { message: string; } | undefined; }>" + " | undefined" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, @@ -8528,7 +14035,23 @@ "section": "def-common.UpdatePackagePolicy", "text": "UpdatePackagePolicy" }, - ", context: ", + ", soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", esClient: ", + { + "pluginId": "@kbn/core-elasticsearch-server", + "scope": "common", + "docId": "kibKbnCoreElasticsearchServerPluginApi", + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" + }, + ", context?: ", { "pluginId": "@kbn/core-http-request-handler-context-server", "scope": "common", @@ -8536,7 +14059,7 @@ "section": "def-common.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", request: ", + " | undefined, request?: ", { "pluginId": "@kbn/core-http-server", "scope": "common", @@ -8544,7 +14067,7 @@ "section": "def-common.KibanaRequest", "text": "KibanaRequest" }, - ") => Promise<", + " | undefined) => Promise<", { "pluginId": "fleet", "scope": "common", @@ -8584,6 +14107,1226 @@ "id": "def-server.PutPackagePolicyUpdateCallback.$2", "type": "Object", "tags": [], + "label": "soClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + } + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PutPackagePolicyUpdateCallback.$3", + "type": "Object", + "tags": [], + "label": "esClient", + "description": [], + "signature": [ + "{ name: string | symbol; get: { (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetResponse", + ">; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetResponse", + ", unknown>>; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetResponse", + ">; }; delete: { (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; cluster: ", + "default", + "; eql: ", + "default", + "; search: { >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchResponse", + ">; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchResponse", + ", unknown>>; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchResponse", + ">; }; create: { (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; monitoring: ", + "default", + "; security: ", + "default", + "; index: { (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; update: { (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateResponse", + ">; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateResponse", + ", unknown>>; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateResponse", + ">; }; asyncSearch: ", + "default", + "; [kAsyncSearch]: symbol | null; [kAutoscaling]: symbol | null; [kCat]: symbol | null; [kCcr]: symbol | null; [kCluster]: symbol | null; [kDanglingIndices]: symbol | null; [kEnrich]: symbol | null; [kEql]: symbol | null; [kFeatures]: symbol | null; [kFleet]: symbol | null; [kGraph]: symbol | null; [kIlm]: symbol | null; [kIndices]: symbol | null; [kIngest]: symbol | null; [kLicense]: symbol | null; [kLogstash]: symbol | null; [kMigration]: symbol | null; [kMl]: symbol | null; [kMonitoring]: symbol | null; [kNodes]: symbol | null; [kRollup]: symbol | null; [kSearchableSnapshots]: symbol | null; [kSecurity]: symbol | null; [kShutdown]: symbol | null; [kSlm]: symbol | null; [kSnapshot]: symbol | null; [kSql]: symbol | null; [kSsl]: symbol | null; [kTasks]: symbol | null; [kTextStructure]: symbol | null; [kTransform]: symbol | null; [kWatcher]: symbol | null; [kXpack]: symbol | null; transport: ", + "default", + "; helpers: ", + "default", + "; child: (opts: ", + "ClientOptions", + ") => ", + "default", + "; autoscaling: ", + "default", + "; bulk: { (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "BulkResponse", + ">; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "BulkResponse", + ", unknown>>; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "BulkResponse", + ">; }; cat: ", + "default", + "; ccr: ", + "default", + "; clearScroll: { (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClearScrollResponse", + ">; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClearScrollResponse", + ", unknown>>; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClearScrollResponse", + ">; }; closePointInTime: { (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClosePointInTimeResponse", + ", unknown>>; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; }; count: { (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "CountResponse", + ">; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "CountResponse", + ", unknown>>; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "CountResponse", + ">; }; danglingIndices: ", + "default", + "; deleteByQuery: { (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "DeleteByQueryResponse", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; }; deleteByQueryRethrottle: { (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TasksTaskListResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; }; deleteScript: { (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; enrich: ", + "default", + "; exists: { (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; existsSource: { (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; explain: { (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ExplainResponse", + ">; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ExplainResponse", + ", unknown>>; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ExplainResponse", + ">; }; features: ", + "default", + "; fieldCaps: { (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "FieldCapsResponse", + ">; (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "FieldCapsResponse", + ", unknown>>; (this: That, params: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "FieldCapsResponse", + ">; }; fleet: ", + "default", + "; getScript: { (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptResponse", + ">; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptResponse", + ", unknown>>; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptResponse", + ">; }; getScriptContext: { (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptContextResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; }; getScriptLanguages: { (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptLanguagesResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; }; getSource: { (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; graph: ", + "default", + "; ilm: ", + "default", + "; indices: ", + "default", + "; info: { (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "InfoResponse", + ">; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "InfoResponse", + ", unknown>>; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "InfoResponse", + ">; }; ingest: ", + "default", + "; knnSearch: { (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "KnnSearchResponse", + ">; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "KnnSearchResponse", + ", unknown>>; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "KnnSearchResponse", + ">; }; license: ", + "default", + "; logstash: ", + "default", + "; mget: { (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MgetResponse", + ">; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MgetResponse", + ", unknown>>; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MgetResponse", + ">; }; migration: ", + "default", + "; ml: ", + "default", + "; msearch: { >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchResponse", + ">; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchResponse", + ", unknown>>; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchResponse", + ">; }; msearchTemplate: { >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchTemplateResponse", + ", unknown>>; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; }; mtermvectors: { (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MtermvectorsResponse", + ", unknown>>; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; }; nodes: ", + "default", + "; openPointInTime: { (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "OpenPointInTimeResponse", + ", unknown>>; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; }; ping: { (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; putScript: { (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; rankEval: { (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RankEvalResponse", + ">; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RankEvalResponse", + ", unknown>>; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RankEvalResponse", + ">; }; reindex: { (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexResponse", + ">; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexResponse", + ", unknown>>; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexResponse", + ">; }; reindexRethrottle: { (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexRethrottleResponse", + ", unknown>>; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; }; renderSearchTemplate: { (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RenderSearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; }; rollup: ", + "default", + "; scriptsPainlessExecute: { (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScriptsPainlessExecuteResponse", + ", unknown>>; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; }; scroll: { >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScrollResponse", + ">; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScrollResponse", + ", unknown>>; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScrollResponse", + ">; }; searchMvt: { (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; searchShards: { (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchShardsResponse", + ">; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchShardsResponse", + ", unknown>>; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchShardsResponse", + ">; }; searchTemplate: { (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; }; searchableSnapshots: ", + "default", + "; shutdown: ", + "default", + "; slm: ", + "default", + "; snapshot: ", + "default", + "; sql: ", + "default", + "; ssl: ", + "default", + "; tasks: ", + "default", + "; termsEnum: { (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermsEnumResponse", + ">; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermsEnumResponse", + ", unknown>>; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermsEnumResponse", + ">; }; termvectors: { (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermvectorsResponse", + ">; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermvectorsResponse", + ", unknown>>; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermvectorsResponse", + ">; }; textStructure: ", + "default", + "; transform: ", + "default", + "; updateByQuery: { (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; }; updateByQueryRethrottle: { (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryRethrottleResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; }; watcher: ", + "default", + "; xpack: ", + "default", + "; }" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PutPackagePolicyUpdateCallback.$4", + "type": "Object", + "tags": [], "label": "context", "description": [], "signature": [ @@ -8593,7 +15336,8 @@ "docId": "kibKbnCoreHttpRequestHandlerContextServerPluginApi", "section": "def-common.RequestHandlerContext", "text": "RequestHandlerContext" - } + }, + " | undefined" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, @@ -8601,7 +15345,7 @@ }, { "parentPluginId": "fleet", - "id": "def-server.PutPackagePolicyUpdateCallback.$3", + "id": "def-server.PutPackagePolicyUpdateCallback.$5", "type": "Object", "tags": [], "label": "request", @@ -8614,7 +15358,7 @@ "section": "def-common.KibanaRequest", "text": "KibanaRequest" }, - "" + " | undefined" ], "path": "x-pack/plugins/fleet/server/types/extensions.ts", "deprecated": false, @@ -11208,31 +17952,42 @@ "children": [ { "parentPluginId": "fleet", - "id": "def-common.GetAgentsResponse.totalInactive", - "type": "number", + "id": "def-common.GetAgentsResponse.list", + "type": "Array", "tags": [], - "label": "totalInactive", + "label": "list", "description": [], + "signature": [ + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Agent", + "text": "Agent" + }, + "[] | undefined" + ], "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "fleet", - "id": "def-common.GetAgentsResponse.list", - "type": "Array", + "id": "def-common.GetAgentsResponse.statusSummary", + "type": "Object", "tags": [], - "label": "list", + "label": "statusSummary", "description": [], "signature": [ + "Record<", { "pluginId": "fleet", "scope": "common", "docId": "kibFleetPluginApi", - "section": "def-common.Agent", - "text": "Agent" + "section": "def-common.AgentStatus", + "text": "AgentStatus" }, - "[] | undefined" + ", number> | undefined" ], "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", "deprecated": false, diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index b899d6d2de663..9e197485fbf2a 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1040 | 3 | 935 | 25 | +| 1068 | 3 | 963 | 26 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index bcf7038f4b851..7c16d27797d76 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 4d246c79461e5..27f68a3254ce7 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 4854c9abced73..0507c50616f39 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index a6699d0e86c3d..7ede4ab81b28f 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 1d9adb71ac0e5..05b8b99d16587 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 1ceea6db43660..66b0ec7d4064f 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index eb094011f7443..d2454b0f00b38 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 8f1d5f0129a8a..598bad6c3ecff 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 50efdb79e6cfb..771ad0487b1cf 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index e9a76aa665eae..de45dee7e15a3 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 8f0fd576c413b..ec436b12a789a 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 7d18a038dbd52..a81650fa04a47 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index 24a185f0bcafb..fb4e4a24d0956 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index fb7772ac2e53a..244fff7c177e7 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index b590e8eff06d7..8e6292796ba57 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 0d15a40c8abed..e2896b8351cc9 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index ad86db69ebf2a..1495cb62a5629 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index db8d903c06614..a601217b3bba4 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 39db1ca3e617c..1ef8a71783b53 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index 1e7983eba24b8..ecaa539fb20f0 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index b60512f023b35..f490cb8efa1c0 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 186c425d73057..398364e141fb0 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.devdocs.json b/api_docs/kbn_apm_synthtrace_client.devdocs.json index b0422331cb101..7c272f970dbc0 100644 --- a/api_docs/kbn_apm_synthtrace_client.devdocs.json +++ b/api_docs/kbn_apm_synthtrace_client.devdocs.json @@ -2318,7 +2318,7 @@ "GeoLocation", "; 'client.geo.region_iso_code': string; 'client.geo.region_name': string; 'client.ip': string; 'cloud.account.id': string; 'cloud.account.name': string; 'cloud.availability_zone': string; 'cloud.machine.type': string; 'cloud.project.id': string; 'cloud.project.name': string; 'cloud.provider': string; 'cloud.region': string; 'cloud.service.name': string; 'container.id': string; 'destination.address': string; 'destination.port': number; 'device.id': string; 'device.manufacturer': string; 'device.model.identifier': string; 'device.model.name': string; 'ecs.version': string; 'error.exception': ", "ApmException", - "[]; 'error.grouping_key': string; 'error.grouping_name': string; 'error.id': string; 'event.ingested': number; 'event.name': string; 'event.outcome': string; 'event.outcome_numeric': number | { sum: number; value_count: number; }; 'faas.coldstart': boolean; 'faas.execution': string; 'faas.id': string; 'faas.name': string; 'faas.trigger.type': string; 'faas.version': string; 'host.architecture': string; 'host.hostname': string; 'host.name': string; 'host.os.full': string; 'host.os.name': string; 'host.os.platform': string; 'host.os.type': string; 'host.os.version': string; 'http.request.method': string; 'http.response.status_code': number; 'kubernetes.pod.name': string; 'kubernetes.pod.uid': string; 'metricset.name': string; 'network.carrier.icc': string; 'network.carrier.mcc': string; 'network.carrier.mnc': string; 'network.carrier.name': string; 'network.connection.subtype': string; 'network.connection.type': string; 'observer.type': string; 'observer.version_major': number; 'observer.version': string; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'session.id': string; 'trace.id': string; 'transaction.duration.us': number; 'transaction.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'service.environment': string; 'service.framework.name': string; 'service.framework.version': string; 'service.language.name': string; 'service.language.version': string; 'service.name': string; 'service.node.name': string; 'service.runtime.name': string; 'service.runtime.version': string; 'service.target.name': string; 'service.target.type': string; 'service.version': string; 'span.action': string; 'span.destination.service.resource': string; 'span.destination.service.response_time.count': number; 'span.destination.service.response_time.sum.us': number; 'span.duration.us': number; 'span.id': string; 'span.name': string; 'span.self_time.count': number; 'span.self_time.sum.us': number; 'span.subtype': string; 'span.type': string; 'transaction.result': string; 'transaction.sampled': true; 'span.links': { trace: { id: string; }; span: { id: string; }; }[]; 'url.original': string; }> & Partial<{ 'system.process.memory.size': number; 'system.memory.actual.free': number; 'system.memory.total': number; 'system.cpu.total.norm.pct': number; 'system.process.memory.rss.bytes': number; 'system.process.cpu.total.norm.pct': number; 'jvm.memory.heap.used': number; 'jvm.memory.non_heap.used': number; 'jvm.thread.count': number; 'faas.billed_duration': number; 'faas.timeout': number; 'faas.coldstart_duration': number; 'faas.duration': number; }> & Partial<{ 'metricset.interval': string; 'transaction.duration.summary': string; }>" + "[]; 'error.grouping_key': string; 'error.grouping_name': string; 'error.id': string; 'event.ingested': number; 'event.name': string; 'event.outcome': string; 'event.outcome_numeric': number | { sum: number; value_count: number; }; 'faas.coldstart': boolean; 'faas.execution': string; 'faas.id': string; 'faas.name': string; 'faas.trigger.type': string; 'faas.version': string; 'host.architecture': string; 'host.hostname': string; 'host.name': string; 'host.os.full': string; 'host.os.name': string; 'host.os.platform': string; 'host.os.type': string; 'host.os.version': string; 'http.request.method': string; 'http.response.status_code': number; 'kubernetes.pod.name': string; 'kubernetes.pod.uid': string; 'metricset.name': string; 'network.carrier.icc': string; 'network.carrier.mcc': string; 'network.carrier.mnc': string; 'network.carrier.name': string; 'network.connection.subtype': string; 'network.connection.type': string; 'observer.type': string; 'observer.version_major': number; 'observer.version': string; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'session.id': string; 'trace.id': string; 'transaction.aggregation.overflow_count': number; 'transaction.duration.us': number; 'transaction.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'service.environment': string; 'service.framework.name': string; 'service.framework.version': string; 'service.language.name': string; 'service.language.version': string; 'service.name': string; 'service.node.name': string; 'service.runtime.name': string; 'service.runtime.version': string; 'service.target.name': string; 'service.target.type': string; 'service.version': string; 'span.action': string; 'span.destination.service.resource': string; 'span.destination.service.response_time.count': number; 'span.destination.service.response_time.sum.us': number; 'span.duration.us': number; 'span.id': string; 'span.name': string; 'span.self_time.count': number; 'span.self_time.sum.us': number; 'span.subtype': string; 'span.type': string; 'transaction.result': string; 'transaction.sampled': true; 'span.links': { trace: { id: string; }; span: { id: string; }; }[]; 'url.original': string; }> & Partial<{ 'system.process.memory.size': number; 'system.memory.actual.free': number; 'system.memory.total': number; 'system.cpu.total.norm.pct': number; 'system.process.memory.rss.bytes': number; 'system.process.cpu.total.norm.pct': number; 'jvm.memory.heap.used': number; 'jvm.memory.non_heap.used': number; 'jvm.thread.count': number; 'faas.billed_duration': number; 'faas.timeout': number; 'faas.coldstart_duration': number; 'faas.duration': number; }> & Partial<{ 'metricset.interval': string; 'transaction.duration.summary': string; }>" ], "path": "packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts", "deprecated": false, diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 51fb8b8005858..71206324973a4 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index fbb95221542f5..6b8f82962e3ed 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 35c0fe796b9c5..f552890b3c0e8 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index e2eac6071fba4..5a6bf288b1740 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index f0bc5b1fb97e3..bf409b6aa7ec8 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 2c4a3016cc511..67dcb198f75fe 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index 77e1342b6a7b8..821e9db5a538d 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 9b4395d47a864..28c248974ef80 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index 4a84ced7636a9..d6039a50eb4a0 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 6d1ff577044ab..6f943294752f5 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.devdocs.json b/api_docs/kbn_code_editor.devdocs.json new file mode 100644 index 0000000000000..0bd40c08dd6c4 --- /dev/null +++ b/api_docs/kbn_code_editor.devdocs.json @@ -0,0 +1,65 @@ +{ + "id": "@kbn/code-editor", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/code-editor", + "id": "def-common.CodeEditor", + "type": "Function", + "tags": [], + "label": "CodeEditor", + "description": [], + "signature": [ + "({ languageId, value, onChange, width, options, overrideEditorWillMount, editorDidMount, editorWillMount, useDarkTheme, transparentBackground, suggestionProvider, signatureProvider, hoverProvider, placeholder, languageConfiguration, \"aria-label\": ariaLabel, isCopyable, allowFullScreen, }: React.PropsWithChildren<", + "Props", + ">) => JSX.Element" + ], + "path": "packages/shared-ux/code_editor/impl/code_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor", + "id": "def-common.CodeEditor.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n languageId,\n value,\n onChange,\n width,\n options,\n overrideEditorWillMount,\n editorDidMount,\n editorWillMount,\n useDarkTheme,\n transparentBackground,\n suggestionProvider,\n signatureProvider,\n hoverProvider,\n placeholder,\n languageConfiguration,\n 'aria-label': ariaLabel = i18n.translate('sharedUXPackages.codeEditor.ariaLabel', {\n defaultMessage: 'Code Editor',\n }),\n isCopyable = false,\n allowFullScreen = false,\n}", + "description": [], + "signature": [ + "React.PropsWithChildren<", + "Props", + ">" + ], + "path": "packages/shared-ux/code_editor/impl/code_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx new file mode 100644 index 0000000000000..3c502a3603284 --- /dev/null +++ b/api_docs/kbn_code_editor.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCodeEditorPluginApi +slug: /kibana-dev-docs/api/kbn-code-editor +title: "@kbn/code-editor" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/code-editor plugin +date: 2023-02-02 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] +--- +import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 2 | 1 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_code_editor_mocks.devdocs.json b/api_docs/kbn_code_editor_mocks.devdocs.json new file mode 100644 index 0000000000000..1c39d497c16d1 --- /dev/null +++ b/api_docs/kbn_code_editor_mocks.devdocs.json @@ -0,0 +1,547 @@ +{ + "id": "@kbn/code-editor-mocks", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock", + "type": "Class", + "tags": [], + "label": "CodeEditorStorybookMock", + "description": [ + "\nStorybook mock for the `CodeEditor` component" + ], + "signature": [ + { + "pluginId": "@kbn/code-editor-mocks", + "scope": "common", + "docId": "kibKbnCodeEditorMocksPluginApi", + "section": "def-common.CodeEditorStorybookMock", + "text": "CodeEditorStorybookMock" + }, + " extends ", + { + "pluginId": "@kbn/shared-ux-storybook-mock", + "scope": "common", + "docId": "kibKbnSharedUxStorybookMockPluginApi", + "section": "def-common.AbstractStorybookMock", + "text": "AbstractStorybookMock" + }, + "<", + "Props", + ", {}, PropArguments, {}>" + ], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments", + "type": "Object", + "tags": [], + "label": "propArguments", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.languageId", + "type": "Object", + "tags": [], + "label": "languageId", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.languageId.control", + "type": "Object", + "tags": [], + "label": "control", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.languageId.control.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.languageId.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.languageId.defaultValue", + "type": "string", + "tags": [], + "label": "defaultValue", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.value", + "type": "Object", + "tags": [], + "label": "value", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.value.control", + "type": "Object", + "tags": [], + "label": "control", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.value.control.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.value.defaultValue", + "type": "string", + "tags": [], + "label": "defaultValue", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.arialabel", + "type": "Object", + "tags": [], + "label": "'aria-label'", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.arialabel.control", + "type": "Object", + "tags": [], + "label": "control", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.arialabel.control.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.arialabel.defaultValue", + "type": "string", + "tags": [], + "label": "defaultValue", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.allowFullScreen", + "type": "Object", + "tags": [], + "label": "allowFullScreen", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.allowFullScreen.control", + "type": "Object", + "tags": [], + "label": "control", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.allowFullScreen.control.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.allowFullScreen.defaultValue", + "type": "boolean", + "tags": [], + "label": "defaultValue", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.useDarkTheme", + "type": "Object", + "tags": [], + "label": "useDarkTheme", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.useDarkTheme.control", + "type": "Object", + "tags": [], + "label": "control", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.useDarkTheme.control.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.useDarkTheme.defaultValue", + "type": "boolean", + "tags": [], + "label": "defaultValue", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.transparentBackground", + "type": "Object", + "tags": [], + "label": "transparentBackground", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.transparentBackground.control", + "type": "Object", + "tags": [], + "label": "control", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.transparentBackground.control.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.transparentBackground.defaultValue", + "type": "boolean", + "tags": [], + "label": "defaultValue", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.placeholder", + "type": "Object", + "tags": [], + "label": "placeholder", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.placeholder.control", + "type": "Object", + "tags": [], + "label": "control", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.placeholder.control.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.propArguments.placeholder.defaultValue", + "type": "string", + "tags": [], + "label": "defaultValue", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.serviceArguments", + "type": "Object", + "tags": [], + "label": "serviceArguments", + "description": [], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.dependencies", + "type": "Array", + "tags": [], + "label": "dependencies", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.getProps", + "type": "Function", + "tags": [], + "label": "getProps", + "description": [], + "signature": [ + "(params?: ", + { + "pluginId": "@kbn/code-editor-mocks", + "scope": "common", + "docId": "kibKbnCodeEditorMocksPluginApi", + "section": "def-common.Params", + "text": "Params" + }, + " | undefined) => ", + "Props" + ], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.getProps.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + { + "pluginId": "@kbn/code-editor-mocks", + "scope": "common", + "docId": "kibKbnCodeEditorMocksPluginApi", + "section": "def-common.Params", + "text": "Params" + }, + " | undefined" + ], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.CodeEditorStorybookMock.getServices", + "type": "Function", + "tags": [], + "label": "getServices", + "description": [], + "signature": [ + "() => { width?: string | number | undefined; height?: string | number | undefined; languageId: enum; value: string; onChange?: ((value: string, event: ", + "editor", + ".IModelContentChangedEvent) => void) | undefined; options?: ", + "editor", + ".IStandaloneEditorConstructionOptions | undefined; suggestionProvider?: ", + "languages", + ".CompletionItemProvider | undefined; signatureProvider?: ", + "languages", + ".SignatureHelpProvider | undefined; hoverProvider?: ", + "languages", + ".HoverProvider | undefined; languageConfiguration?: ", + "languages", + ".LanguageConfiguration | undefined; editorWillMount?: (() => void) | undefined; overrideEditorWillMount?: (() => void) | undefined; editorDidMount?: ((editor: ", + "editor", + ".IStandaloneCodeEditor) => void) | undefined; useDarkTheme?: boolean | undefined; transparentBackground?: boolean | undefined; fullWidth?: boolean | undefined; placeholder?: string | undefined; 'aria-label'?: string | undefined; isCopyable?: boolean | undefined; allowFullScreen?: boolean | undefined; }" + ], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/code-editor-mocks", + "id": "def-common.Params", + "type": "Type", + "tags": [], + "label": "Params", + "description": [], + "signature": [ + "{ value: any; placeholder: any; \"aria-label\": any; allowFullScreen: any; languageId: any; useDarkTheme: any; transparentBackground: any; }" + ], + "path": "packages/shared-ux/code_editor/mocks/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx new file mode 100644 index 0000000000000..e2804c89ba35f --- /dev/null +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCodeEditorMocksPluginApi +slug: /kibana-dev-docs/api/kbn-code-editor-mocks +title: "@kbn/code-editor-mocks" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/code-editor-mocks plugin +date: 2023-02-02 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] +--- +import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 37 | 0 | 36 | 0 | + +## Common + +### Classes + + +### Consts, variables and types + + diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 069f23a0c6871..049917bd83438 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 444b276790315..2d17452f923a1 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 4f30ccfb5a5ea..25b4b7ebacb53 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index e1cf4c7c81a86..d1e0d72178225 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 90acfb8d3c42d..3cc368eec1b45 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index 989bfddc5ad4c..5ce5ade264cd0 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 9207788b71dcb..b663cf574c948 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index 10ff4b00e813d..855e9ee974252 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index 0a3ad2de810c4..83df4d8d5a2ee 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 5e25155fe99b3..aa02e316636f7 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index c735376e9bb3d..0b3d418bf4b00 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index fca2385c43a2f..181753d1686f1 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index 185c8a8c0d5da..abf9c2389564a 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index eca95e4f9bb4d..e344fd23aedc6 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index e3685fc2066d8..b7392808186a4 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index 65fd987d8573e..120bde7908a52 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index 18e2f252c0c85..fa360f7b3bb2f 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index 6964da198d9a6..7e170abfd7525 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 841808f6c5d2d..5488ce1d2e961 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index c7c6e08deedc7..b05783625281e 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 53833a277ff3b..7bb91092d7b54 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index 4626b08cc0781..55eb1cc972e83 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 4e578e84321dd..533515b954f12 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index 69bbcc7424ca2..9116f0baaa3e8 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index 9f9920fbfcb44..3105448b0f9ca 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index 18f8e597892b9..d068c40608ab4 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 22282d38e237a..b22f6eba8144e 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 6851754193ed1..071caddde22d6 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index 4603c1b5db2c0..bbba9eb95f497 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index d66ae746b8dd1..c6e3906d3816d 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index 4607944fe34f2..3ecd465e22838 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index d36082c846d19..bcacd36e5963a 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index 227fe7e79e6b0..1ff4fb95c5df3 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index e8525bba73e6f..348badb266232 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.devdocs.json b/api_docs/kbn_core_custom_branding_server.devdocs.json index bf777d2385605..ed18f14cb7ff5 100644 --- a/api_docs/kbn_core_custom_branding_server.devdocs.json +++ b/api_docs/kbn_core_custom_branding_server.devdocs.json @@ -76,6 +76,88 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-custom-branding-server", + "id": "def-common.CustomBrandingSetup.getBrandingFor", + "type": "Function", + "tags": [], + "label": "getBrandingFor", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", options: { unauthenticated?: boolean | undefined; }) => Promise<", + { + "pluginId": "@kbn/core-custom-branding-common", + "scope": "common", + "docId": "kibKbnCoreCustomBrandingCommonPluginApi", + "section": "def-common.CustomBranding", + "text": "CustomBranding" + }, + ">" + ], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-custom-branding-server", + "id": "def-common.CustomBrandingSetup.getBrandingFor.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-custom-branding-server", + "id": "def-common.CustomBrandingSetup.getBrandingFor.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-custom-branding-server", + "id": "def-common.CustomBrandingSetup.getBrandingFor.$2.unauthenticated", + "type": "CompoundType", + "tags": [], + "label": "unauthenticated", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -112,7 +194,7 @@ "section": "def-common.KibanaRequest", "text": "KibanaRequest" }, - ") => ", + ", unauthenticated: boolean) => ", { "pluginId": "@kbn/utility-types", "scope": "common", @@ -155,6 +237,17 @@ "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-custom-branding-server", + "id": "def-common.CustomBrandingFetchFn.$2", + "type": "boolean", + "tags": [], + "label": "unauthenticated", + "description": [], + "path": "packages/core/custom-branding/core-custom-branding-server/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index f191340be9c13..39269a898fd3e 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 6 | 0 | 6 | 0 | +| 11 | 0 | 11 | 0 | ## Common diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index b7c7ce11ed2c2..6e69528f61dad 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index bcf53c6b63efc..417766aa34ff0 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index a969e55e05969..9d3e35898c078 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index 74d1f269f1bc5..34560e4104d90 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index af85ababf4a9f..5ba41b82b4d13 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index f5f50113bb830..908bf64b5c01e 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 20535709f9bf4..696330c233e99 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index e6f46741b98db..3f07a5fec9aca 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index 411104ede9491..b6a68b76e926f 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 2e0f829c6f5d4..b39f60c3c5782 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index d046b63b65748..f52c31de801b0 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index db61e55d08c73..ac415407487e0 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index e0b8cf8c6275d..e3f2e6edb9505 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index f81a4b1dca0b2..f289d1dec6bcf 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index 878429f1f432c..77e6d192bc554 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index b74d61fd48bc0..2175f5f7a3320 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index a0f6c751f1322..8fe117bc12329 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 675d43d95945c..7473612873eb4 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index 3468fa27b7967..641154f79ab23 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 9f58849fedb91..d3add23605e96 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 2f5d1108069ed..7d497d9109117 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index ddbdafb1c895b..5dd6425e5ef3e 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index 8e317783178fe..3d0605f229543 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index e18968f71d09c..11c6a6b5928bc 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index 3bbc3dcb1ad3f..279b9a53242e9 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index 7bc1f355f3c50..543e28a53ca03 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index 1412f197a1178..27dca641052a7 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 2f6dde2fb228d..4483f5a225cc5 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index acd6f5098cc1b..82ef18604e62f 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index c27ee0968e3c6..c191d5588429a 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 0228a0b7f398c..00267d77d92be 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index cb8eb78565089..200fca84933ea 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index e807dd4b2bf2e..013e1690c3671 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 4a3e1bb95abf0..c3df4b7c6ed8d 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index ed4d689190141..5ba52aab8a73e 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index 66cbcfc913e75..11e0ca720e811 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index 16f80c2ef88c9..3eb99f97a12bc 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index c76203c29fdd9..12a97f6d6e7fe 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index de8f7fb32b44d..6f23c3acaf59a 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index eaacfeda3d0dc..22ac14b1afef9 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 3520dc1324a6e..0652a13fe44ad 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 6b7d2bcd26a8a..4bc7632c77e86 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 2d88ab176bdcf..c038022919e36 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index d6147f89f809e..5b6e71ec7c156 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index 799798ef3955b..b69d20b56b4ae 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 2061333d751ba..ec23b942a1afe 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index 0fdb792899e32..8eb9c8ab99371 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index 84d4904227011..a9692cbea8ccb 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 742add69ed5a7..7e4d0bf23a837 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index 1f47663a9540e..d55d689f8b80d 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index 1dc629cf9d2e0..1484fd3a35f9b 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.devdocs.json b/api_docs/kbn_core_lifecycle_browser.devdocs.json index 23c3e5ea7bcb4..9b4320221010f 100644 --- a/api_docs/kbn_core_lifecycle_browser.devdocs.json +++ b/api_docs/kbn_core_lifecycle_browser.devdocs.json @@ -803,27 +803,27 @@ }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_selector/use_recently_viewed_monitors.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_selector/use_recently_viewed_monitors.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_locations_api.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts" }, { "plugin": "synthetics", diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index 859261d83c679..c28d44c6db76e 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index e7b57a81de800..666e90e22149d 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index ac5ebf3e71e50..21cd7c5520674 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index a3fed21b9979b..c3151964e6f3a 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 52ef44431a4d5..b630ad85405d6 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 23624669a3f8f..fa1ab058c1719 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 2449965e82474..e569f93915525 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 65ffa182d4220..a5a14834e0d38 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index a0df57e09c979..dc3f576ac27f1 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index c67dd8b1dd1ca..ad1cbfbbde4bd 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index c8ef726cdeb7d..1a4c7d8d067f6 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index 832de383b526e..7f0e8567d5156 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 6ffc31d1fca27..b02f1e59dc749 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 4596f78a805ca..0223092c7ec44 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 069fcffdcb454..32eb3da870095 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index a6bd44f60b7c8..9ca427c60b3c6 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 3b1e9026a48b9..2921e4faadde9 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index fc77245dc3cf3..cbdaccbe466ea 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 4600e9d212c84..ce7ca986d2480 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index 49d93560e3079..fac04fd7b70cf 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index fb4b14a8a8542..7d1dd62f948f2 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 3fdf81609d4d2..f6e30a70dbf11 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index 5c6dbc9a9ccb9..0bfaae8e018f3 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 008e70efbdf43..f1190eed38362 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index f76760c79e8c4..82f9bb2aedf8b 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 7fa45105d4416..a279de8f2d709 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 2fa60c1304427..0e1c587829e15 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 6ab1c7d6d41e2..0979cf54ee54b 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 915d1bee31595..af174f7cc61c5 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 9196688eec7e7..1015d8d299f9e 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index 300ff8710b6d6..333810c192d5d 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index f8a1aeaed572f..7a0672c472a61 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index b3dec96995eec..258159bae1f9e 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index 50e74752502a2..89a5d5c0d463f 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.devdocs.json b/api_docs/kbn_core_saved_objects_api_browser.devdocs.json index 27cd4d841d19d..8041e19b0bf5a 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_browser.devdocs.json @@ -2305,15 +2305,15 @@ }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/hooks/use_location_monitors.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_name.ts" }, { "plugin": "graph", @@ -2333,11 +2333,11 @@ }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.test.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.test.ts" }, { "plugin": "synthetics", - "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/list_filters/use_filters.test.ts" + "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.test.ts" }, { "plugin": "savedObjects", diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 387bf244f3e84..eb7048050917f 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 7b51ae2386e4f..a0e8b9873de71 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index 732c0f388c492..344ddee09febd 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 4dbed3adb50a8..da353638f970c 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index 168a6de3884bf..e1fa8d28c2d2f 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 5ed0f53c38540..f87fae7657223 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 3d3d8c7372407..08db71c3269da 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 66402b1e33977..728397bdc6fd3 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index 05461ace74602..d9caf4392e97e 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.devdocs.json b/api_docs/kbn_core_saved_objects_common.devdocs.json index 0b1701f00d67d..35b92637b9190 100644 --- a/api_docs/kbn_core_saved_objects_common.devdocs.json +++ b/api_docs/kbn_core_saved_objects_common.devdocs.json @@ -1564,6 +1564,14 @@ "plugin": "cases", "path": "x-pack/plugins/cases/server/services/so_references.ts" }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/server/services/so_references.ts" + }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/server/services/so_references.ts" + }, { "plugin": "lists", "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.mock.ts" @@ -3289,6 +3297,18 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/share_action.ts" }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts" + }, { "plugin": "infra", "path": "x-pack/plugins/infra/public/common/visualizations/lens/types.ts" @@ -3360,6 +3380,14 @@ { "plugin": "cases", "path": "x-pack/plugins/cases/server/services/user_actions/test_utils.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.test.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.test.ts" } ], "initialIsOpen": false diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index 12c46e4a55d1f..c4a5e47318655 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index 2df56466d7b39..e85baecb9edd0 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 3e322719e6c3f..8198192ad5acc 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 0c78327852c14..37c68e17d21fe 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index e7c2e6d106e8b..07e789981e008 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 4fccae05fcfb9..a293f35a1d35f 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index 3968ab5e944ea..b35687b6eacb4 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index 5c8209b1dd160..2bd5c250bb9d5 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index a28d111a550b2..b15d0744a8b8e 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 2bab6b5ae9535..d8036e7c4c24f 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index 357fa83730435..6e928cb172f51 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index 8dc92118c079f..bfaf4062eb9ab 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index f8c6c61f848cc..3a4f9d0cf0cdc 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 8f5a6fbadf09b..03a2bbf68f7ab 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 390f909ce2513..5b15259120a93 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index ca0f19aa71b3a..3577d96881301 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index de94f700b713f..c0576c4339907 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index 815aa3601ead0..50190156c7058 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index 3c2f405761f69..90c113785165e 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index df472a1696b82..5af18c30b5165 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index eee3789145151..450a67675ef76 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 105503f3e6a6c..7b48153762dd1 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index 4457d3c3e80d2..7d9dbe4a8c2dc 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index e621db85bb8e1..4170c1807a6b2 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 35c0617de2b6f..7d6e4a161484c 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index dc1710d40a927..4c7fa51e015de 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 50d479a704290..f27c96b032a97 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index 9b4f92f88656a..fd8ce3a327280 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index 655d36907edc1..e06160c1924e1 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index 721850f712396..5023dac6eb8d6 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index d94d8f39d44d2..450bc0448b88a 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index fe32fe6d09742..6ab0e2efd550a 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index f6f73955e945d..9a50441146032 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index aa57bcafa5d95..7e7bd30b27af4 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index 31aa95ab485f3..06e1080a1a560 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 5c7513c4776ef..56dc4c98673cf 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 20efd16d9c4d4..f91f3e5085e9a 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index 93a3bdf8fb699..08c3c994635fa 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index d2574656ee11c..ad333efc510c7 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index b027188acb6d6..e6d02631496c7 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index 02ef2e3f1f149..95bd54c7055c2 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -840,7 +840,7 @@ "label": "fleet", "description": [], "signature": [ - "{ readonly beatsAgentComparison: string; readonly guide: string; readonly fleetServer: string; readonly fleetServerAddFleetServer: string; readonly settings: string; readonly settingsFleetServerHostSettings: string; readonly settingsFleetServerProxySettings: string; readonly troubleshooting: string; readonly elasticAgent: string; readonly datastreams: string; readonly datastreamsILM: string; readonly datastreamsNamingScheme: string; readonly installElasticAgent: string; readonly installElasticAgentStandalone: string; readonly packageSignatures: string; readonly upgradeElasticAgent: string; readonly learnMoreBlog: string; readonly apiKeysLearnMore: string; readonly onPremRegistry: string; readonly secureLogstash: string; readonly agentPolicy: string; }" + "{ readonly beatsAgentComparison: string; readonly guide: string; readonly fleetServer: string; readonly fleetServerAddFleetServer: string; readonly settings: string; readonly settingsFleetServerHostSettings: string; readonly settingsFleetServerProxySettings: string; readonly troubleshooting: string; readonly elasticAgent: string; readonly datastreams: string; readonly datastreamsILM: string; readonly datastreamsNamingScheme: string; readonly datastreamsManualRollover: string; readonly installElasticAgent: string; readonly installElasticAgentStandalone: string; readonly packageSignatures: string; readonly upgradeElasticAgent: string; readonly learnMoreBlog: string; readonly apiKeysLearnMore: string; readonly onPremRegistry: string; readonly secureLogstash: string; readonly agentPolicy: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index a9529773dfc77..9e6fa400e8eb1 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index d809cce3b6274..55b80d6aa632e 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index f6524ce9c0971..ca0b2d1b6773f 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index 84af0a7b98494..30d9309e38692 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 360f4b68d6d87..54ccaac62c880 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 714d4f044fe8c..1507c9921a93a 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index 3c3a74d96af97..4a5ea93c6655f 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.devdocs.json b/api_docs/kbn_es_query.devdocs.json index 4804bbcd473dc..8bfb4b0ebbbd0 100644 --- a/api_docs/kbn_es_query.devdocs.json +++ b/api_docs/kbn_es_query.devdocs.json @@ -1205,7 +1205,9 @@ "section": "def-common.FilterMeta", "text": "FilterMeta" }, - ") => { query: (Record & { query_string?: { query: string; fields?: string[] | undefined; } | undefined; }) | undefined; meta: { alias: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index: string; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; }" + ") => { query: (Record & { query_string?: { query: string; fields?: string[] | undefined; } | undefined; }) | undefined; meta: { alias: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index: string; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: ", + "FilterMetaParams", + " | undefined; value?: string | undefined; }; }" ], "path": "packages/kbn-es-query/src/filters/build_filters/query_string_filter.ts", "deprecated": false, @@ -3393,7 +3395,7 @@ "label": "isRangeFilter", "description": [], "signature": [ - "(filter?: ", + "(filter: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -3401,14 +3403,7 @@ "section": "def-common.Filter", "text": "Filter" }, - " | undefined) => filter is ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.RangeFilter", - "text": "RangeFilter" - } + " | undefined) => boolean" ], "path": "packages/kbn-es-query/src/filters/build_filters/range_filter.ts", "deprecated": false, @@ -3840,7 +3835,9 @@ "section": "def-common.Filter", "text": "Filter" }, - ") => { meta: { disabled: boolean; alias?: string | null | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; $state?: { store: ", + ") => { meta: { disabled: boolean; alias?: string | null | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: ", + "FilterMetaParams", + " | undefined; value?: string | undefined; }; $state?: { store: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -3899,7 +3896,9 @@ "section": "def-common.Filter", "text": "Filter" }, - ") => { meta: { negate: boolean; alias?: string | null | undefined; disabled?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; $state?: { store: ", + ") => { meta: { negate: boolean; alias?: string | null | undefined; disabled?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: ", + "FilterMetaParams", + " | undefined; value?: string | undefined; }; $state?: { store: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -3958,66 +3957,831 @@ "section": "def-common.Filter", "text": "Filter" }, - ") => { $state: { store: ", + ") => { $state: { store: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FilterStateStore", + "text": "FilterStateStore" + }, + "; }; meta: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FilterMeta", + "text": "FilterMeta" + }, + "; query?: Record | undefined; }" + ], + "path": "packages/kbn-es-query/src/filters/helpers/meta_filter.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.toggleFilterPinned.$1", + "type": "Object", + "tags": [], + "label": "filter", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + } + ], + "path": "packages/kbn-es-query/src/filters/helpers/meta_filter.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "A copy of the filter with a toggled pinned state (toggles store from app to global and vice versa)" + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.uniqFilters", + "type": "Function", + "tags": [], + "label": "uniqFilters", + "description": [ + "\nRemove duplicate filters from an array of filters\n" + ], + "signature": [ + "(filters: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[], comparatorOptions?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FilterCompareOptions", + "text": "FilterCompareOptions" + }, + ") => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]" + ], + "path": "packages/kbn-es-query/src/filters/helpers/uniq_filters.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.uniqFilters.$1", + "type": "Array", + "tags": [], + "label": "filters", + "description": [ + "The filters to remove duplicates from" + ], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]" + ], + "path": "packages/kbn-es-query/src/filters/helpers/uniq_filters.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.uniqFilters.$2", + "type": "Object", + "tags": [], + "label": "comparatorOptions", + "description": [ + "- Parameters to use for comparison" + ], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FilterCompareOptions", + "text": "FilterCompareOptions" + } + ], + "path": "packages/kbn-es-query/src/filters/helpers/uniq_filters.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "The original filters array with duplicates removed" + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.unpinFilter", + "type": "Function", + "tags": [], + "label": "unpinFilter", + "description": [], + "signature": [ + "(filter: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ") => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + } + ], + "path": "packages/kbn-es-query/src/filters/helpers/meta_filter.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.unpinFilter.$1", + "type": "Object", + "tags": [], + "label": "filter", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + } + ], + "path": "packages/kbn-es-query/src/filters/helpers/meta_filter.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "An unpinned (app scoped) copy of the filter" + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.updateFilter", + "type": "Function", + "tags": [], + "label": "updateFilter", + "description": [], + "signature": [ + "(filter: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", field?: string | undefined, operator?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FilterMeta", + "text": "FilterMeta" + }, + " | undefined, params?: ", + "FilterMetaParams", + " | undefined, fieldType?: string | undefined) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + " | { meta: { key: string | undefined; field: string | undefined; params: { query: undefined; }; value: undefined; type: undefined; alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; }; query: undefined; $state?: { store: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FilterStateStore", + "text": "FilterStateStore" + }, + "; } | undefined; } | { meta: { negate: boolean | undefined; type: string | undefined; params: { gte: any; lt: any; }; alias?: string | null | undefined; disabled?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; key?: string | undefined; value?: string | undefined; }; query: { range: { [x: string]: { gte: any; lt: any; }; }; }; $state?: { store: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FilterStateStore", + "text": "FilterStateStore" + }, + "; } | undefined; } | { meta: { negate: boolean | undefined; type: string | undefined; params: { query: ", + "FilterMetaParams", + " | undefined; length: number; toString(): string; toLocaleString(): string; pop(): string | undefined; push(...items: string[]): number; concat(...items: ConcatArray[]): string[]; concat(...items: (string | ConcatArray)[]): string[]; join(separator?: string | undefined): string; reverse(): string[]; shift(): string | undefined; slice(start?: number | undefined, end?: number | undefined): string[]; sort(compareFn?: ((a: string, b: string) => number) | undefined): string[]; splice(start: number, deleteCount?: number | undefined): string[]; splice(start: number, deleteCount: number, ...items: string[]): string[]; unshift(...items: string[]): number; indexOf(searchElement: string, fromIndex?: number | undefined): number; lastIndexOf(searchElement: string, fromIndex?: number | undefined): number; every(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): this is S[]; every(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean; some(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean; forEach(callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any): void; map(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any): U[]; filter(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): S[]; filter(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string[]; reduce(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; reduce(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; reduce(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; reduceRight(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; reduceRight(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; find(predicate: (this: void, value: string, index: number, obj: string[]) => value is S, thisArg?: any): S | undefined; find(predicate: (value: string, index: number, obj: string[]) => unknown, thisArg?: any): string | undefined; findIndex(predicate: (value: string, index: number, obj: string[]) => unknown, thisArg?: any): number; fill(value: string, start?: number | undefined, end?: number | undefined): string[]; copyWithin(target: number, start: number, end?: number | undefined): string[]; entries(): IterableIterator<[number, string]>; keys(): IterableIterator; values(): IterableIterator; includes(searchElement: string, fromIndex?: number | undefined): boolean; flatMap(callback: (this: This, value: string, index: number, array: string[]) => U | readonly U[], thisArg?: This | undefined): U[]; flat(this: A, depth?: D | undefined): FlatArray[]; [Symbol.iterator](): IterableIterator; [Symbol.unscopables](): { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }; at(index: number): string | undefined; } | { query: ", + "FilterMetaParams", + " | undefined; length: number; toString(): string; toLocaleString(): string; pop(): number | undefined; push(...items: number[]): number; concat(...items: ConcatArray[]): number[]; concat(...items: (number | ConcatArray)[]): number[]; join(separator?: string | undefined): string; reverse(): number[]; shift(): number | undefined; slice(start?: number | undefined, end?: number | undefined): number[]; sort(compareFn?: ((a: number, b: number) => number) | undefined): number[]; splice(start: number, deleteCount?: number | undefined): number[]; splice(start: number, deleteCount: number, ...items: number[]): number[]; unshift(...items: number[]): number; indexOf(searchElement: number, fromIndex?: number | undefined): number; lastIndexOf(searchElement: number, fromIndex?: number | undefined): number; every(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; every(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; some(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void; map(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): U[]; filter(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; filter(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; find(predicate: (this: void, value: number, index: number, obj: number[]) => value is S, thisArg?: any): S | undefined; find(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number | undefined; findIndex(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; fill(value: number, start?: number | undefined, end?: number | undefined): number[]; copyWithin(target: number, start: number, end?: number | undefined): number[]; entries(): IterableIterator<[number, number]>; keys(): IterableIterator; values(): IterableIterator; includes(searchElement: number, fromIndex?: number | undefined): boolean; flatMap(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This | undefined): U[]; flat(this: A, depth?: D | undefined): FlatArray[]; [Symbol.iterator](): IterableIterator; [Symbol.unscopables](): { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }; at(index: number): number | undefined; } | { query: ", + "FilterMetaParams", + " | undefined; length: number; toString(): string; toLocaleString(): string; pop(): boolean | undefined; push(...items: boolean[]): number; concat(...items: ConcatArray[]): boolean[]; concat(...items: (boolean | ConcatArray)[]): boolean[]; join(separator?: string | undefined): string; reverse(): boolean[]; shift(): boolean | undefined; slice(start?: number | undefined, end?: number | undefined): boolean[]; sort(compareFn?: ((a: boolean, b: boolean) => number) | undefined): boolean[]; splice(start: number, deleteCount?: number | undefined): boolean[]; splice(start: number, deleteCount: number, ...items: boolean[]): boolean[]; unshift(...items: boolean[]): number; indexOf(searchElement: boolean, fromIndex?: number | undefined): number; lastIndexOf(searchElement: boolean, fromIndex?: number | undefined): number; every(predicate: (value: boolean, index: number, array: boolean[]) => value is S, thisArg?: any): this is S[]; every(predicate: (value: boolean, index: number, array: boolean[]) => unknown, thisArg?: any): boolean; some(predicate: (value: boolean, index: number, array: boolean[]) => unknown, thisArg?: any): boolean; forEach(callbackfn: (value: boolean, index: number, array: boolean[]) => void, thisArg?: any): void; map(callbackfn: (value: boolean, index: number, array: boolean[]) => U, thisArg?: any): U[]; filter(predicate: (value: boolean, index: number, array: boolean[]) => value is S, thisArg?: any): S[]; filter(predicate: (value: boolean, index: number, array: boolean[]) => unknown, thisArg?: any): boolean[]; reduce(callbackfn: (previousValue: boolean, currentValue: boolean, currentIndex: number, array: boolean[]) => boolean): boolean; reduce(callbackfn: (previousValue: boolean, currentValue: boolean, currentIndex: number, array: boolean[]) => boolean, initialValue: boolean): boolean; reduce(callbackfn: (previousValue: U, currentValue: boolean, currentIndex: number, array: boolean[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: boolean, currentValue: boolean, currentIndex: number, array: boolean[]) => boolean): boolean; reduceRight(callbackfn: (previousValue: boolean, currentValue: boolean, currentIndex: number, array: boolean[]) => boolean, initialValue: boolean): boolean; reduceRight(callbackfn: (previousValue: U, currentValue: boolean, currentIndex: number, array: boolean[]) => U, initialValue: U): U; find(predicate: (this: void, value: boolean, index: number, obj: boolean[]) => value is S, thisArg?: any): S | undefined; find(predicate: (value: boolean, index: number, obj: boolean[]) => unknown, thisArg?: any): boolean | undefined; findIndex(predicate: (value: boolean, index: number, obj: boolean[]) => unknown, thisArg?: any): number; fill(value: boolean, start?: number | undefined, end?: number | undefined): boolean[]; copyWithin(target: number, start: number, end?: number | undefined): boolean[]; entries(): IterableIterator<[number, boolean]>; keys(): IterableIterator; values(): IterableIterator; includes(searchElement: boolean, fromIndex?: number | undefined): boolean; flatMap(callback: (this: This, value: boolean, index: number, array: boolean[]) => U | readonly U[], thisArg?: This | undefined): U[]; flat(this: A, depth?: D | undefined): FlatArray[]; [Symbol.iterator](): IterableIterator; [Symbol.unscopables](): { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }; at(index: number): boolean | undefined; } | { query: ", + "FilterMetaParams", + " | undefined; $state?: { store: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FilterStateStore", + "text": "FilterStateStore" + }, + "; } | undefined; meta: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FilterMeta", + "text": "FilterMeta" + }, + "; } | { query: ", + "FilterMetaParams", + " | undefined; from?: string | number | undefined; to?: string | number | undefined; gt?: string | number | undefined; lt?: string | number | undefined; gte?: string | number | undefined; lte?: string | number | undefined; format?: string | undefined; } | { query: ", + "FilterMetaParams", + " | undefined; alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type: \"range\"; key?: string | undefined; params?: (", + "FilterMetaParams", + " & ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.RangeFilterParams", + "text": "RangeFilterParams" + }, + ") | undefined; value?: string | undefined; field?: string | undefined; formattedValue?: string | undefined; } | { query: ", + "FilterMetaParams", + " | undefined; length: number; toString(): string; toLocaleString(): string; pop(): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + " | undefined; push(...items: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]): number; concat(...items: ConcatArray<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ">[]): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; concat(...items: (", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + " | ConcatArray<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ">)[]): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; join(separator?: string | undefined): string; reverse(): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; shift(): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + " | undefined; slice(start?: number | undefined, end?: number | undefined): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; sort(compareFn?: ((a: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", b: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ") => number) | undefined): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; splice(start: number, deleteCount?: number | undefined): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; splice(start: number, deleteCount: number, ...items: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; unshift(...items: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]): number; indexOf(searchElement: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", fromIndex?: number | undefined): number; lastIndexOf(searchElement: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", fromIndex?: number | undefined): number; every(predicate: (value: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", index: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => value is S, thisArg?: any): this is S[]; every(predicate: (value: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", index: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => unknown, thisArg?: any): boolean; some(predicate: (value: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", index: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => unknown, thisArg?: any): boolean; forEach(callbackfn: (value: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", index: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => void, thisArg?: any): void; map(callbackfn: (value: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", index: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => U, thisArg?: any): U[]; filter(predicate: (value: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", index: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => value is S, thisArg?: any): S[]; filter(predicate: (value: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", index: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => unknown, thisArg?: any): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; reduce(callbackfn: (previousValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", currentValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", currentIndex: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "; reduce(callbackfn: (previousValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", currentValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", currentIndex: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", initialValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "; reduce(callbackfn: (previousValue: U, currentValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", currentIndex: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", currentValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", currentIndex: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "; reduceRight(callbackfn: (previousValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", currentValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", currentIndex: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", initialValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "): ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "; reduceRight(callbackfn: (previousValue: U, currentValue: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", currentIndex: number, array: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => U, initialValue: U): U; find(predicate: (this: void, value: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ", index: number, obj: ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterStateStore", - "text": "FilterStateStore" + "section": "def-common.Filter", + "text": "Filter" }, - "; }; meta: ", + "[]) => value is S, thisArg?: any): S | undefined; find(predicate: (value: ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterMeta", - "text": "FilterMeta" + "section": "def-common.Filter", + "text": "Filter" }, - "; query?: Record | undefined; }" - ], - "path": "packages/kbn-es-query/src/filters/helpers/meta_filter.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + ", index: number, obj: ", { - "parentPluginId": "@kbn/es-query", - "id": "def-common.toggleFilterPinned.$1", - "type": "Object", - "tags": [], - "label": "filter", - "description": [], - "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - } - ], - "path": "packages/kbn-es-query/src/filters/helpers/meta_filter.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [ - "A copy of the filter with a toggled pinned state (toggles store from app to global and vice versa)" - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/es-query", - "id": "def-common.uniqFilters", - "type": "Function", - "tags": [], - "label": "uniqFilters", - "description": [ - "\nRemove duplicate filters from an array of filters\n" - ], - "signature": [ - "(filters: ", + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]) => unknown, thisArg?: any): ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -4025,15 +4789,15 @@ "section": "def-common.Filter", "text": "Filter" }, - "[], comparatorOptions?: ", + " | undefined; findIndex(predicate: (value: ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterCompareOptions", - "text": "FilterCompareOptions" + "section": "def-common.Filter", + "text": "Filter" }, - ") => ", + ", index: number, obj: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -4041,74 +4805,23 @@ "section": "def-common.Filter", "text": "Filter" }, - "[]" - ], - "path": "packages/kbn-es-query/src/filters/helpers/uniq_filters.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + "[]) => unknown, thisArg?: any): number; fill(value: ", { - "parentPluginId": "@kbn/es-query", - "id": "def-common.uniqFilters.$1", - "type": "Array", - "tags": [], - "label": "filters", - "description": [ - "The filters to remove duplicates from" - ], - "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - }, - "[]" - ], - "path": "packages/kbn-es-query/src/filters/helpers/uniq_filters.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" }, + ", start?: number | undefined, end?: number | undefined): ", { - "parentPluginId": "@kbn/es-query", - "id": "def-common.uniqFilters.$2", - "type": "Object", - "tags": [], - "label": "comparatorOptions", - "description": [ - "- Parameters to use for comparison" - ], - "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterCompareOptions", - "text": "FilterCompareOptions" - } - ], - "path": "packages/kbn-es-query/src/filters/helpers/uniq_filters.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [ - "The original filters array with duplicates removed" - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/es-query", - "id": "def-common.unpinFilter", - "type": "Function", - "tags": [], - "label": "unpinFilter", - "description": [], - "signature": [ - "(filter: ", + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; copyWithin(target: number, start: number, end?: number | undefined): ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -4116,55 +4829,23 @@ "section": "def-common.Filter", "text": "Filter" }, - ") => ", + "[]; entries(): IterableIterator<[number, ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", "section": "def-common.Filter", "text": "Filter" - } - ], - "path": "packages/kbn-es-query/src/filters/helpers/meta_filter.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, + "]>; keys(): IterableIterator; values(): IterableIterator<", { - "parentPluginId": "@kbn/es-query", - "id": "def-common.unpinFilter.$1", - "type": "Object", - "tags": [], - "label": "filter", - "description": [], - "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - } - ], - "path": "packages/kbn-es-query/src/filters/helpers/meta_filter.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [ - "An unpinned (app scoped) copy of the filter" - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/es-query", - "id": "def-common.updateFilter", - "type": "Function", - "tags": [], - "label": "updateFilter", - "description": [], - "signature": [ - "(filter: ", + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + ">; includes(searchElement: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -4172,39 +4853,59 @@ "section": "def-common.Filter", "text": "Filter" }, - ", field?: string | undefined, operator?: FilterOperator | undefined, params?: any, fieldType?: string | undefined) => { meta: { key: string | undefined; field: string | undefined; params: { query: undefined; }; value: undefined; type: undefined; alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; }; query: undefined; $state?: { store: ", + ", fromIndex?: number | undefined): boolean; flatMap(callback: (this: This, value: ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterStateStore", - "text": "FilterStateStore" + "section": "def-common.Filter", + "text": "Filter" }, - "; } | undefined; } | { meta: { negate: boolean | undefined; type: string | undefined; params: undefined; value: string; alias?: string | null | undefined; disabled?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; key?: string | undefined; }; query: { exists: { field: string | undefined; }; }; $state?: { store: ", + ", index: number, array: ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterStateStore", - "text": "FilterStateStore" + "section": "def-common.Filter", + "text": "Filter" }, - "; } | undefined; } | { meta: { negate: boolean | undefined; type: string | undefined; params: { gte: string | number | undefined; lt: string | number | undefined; }; alias?: string | null | undefined; disabled?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; key?: string | undefined; value?: string | undefined; }; query: { range: { [x: string]: { gte: string | number | undefined; lt: string | number | undefined; }; }; }; $state?: { store: ", + "[]) => U | readonly U[], thisArg?: This | undefined): U[]; flat(this: A, depth?: D | undefined): FlatArray[]; [Symbol.iterator](): IterableIterator<", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterStateStore", - "text": "FilterStateStore" + "section": "def-common.Filter", + "text": "Filter" }, - "; } | undefined; } | { meta: { negate: boolean | undefined; type: string | undefined; params: any[] | undefined; alias?: string | null | undefined; disabled?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; key?: string | undefined; value?: string | undefined; }; query: { bool: any; }; $state?: { store: ", + ">; [Symbol.unscopables](): { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }; at(index: number): ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterStateStore", - "text": "FilterStateStore" + "section": "def-common.Filter", + "text": "Filter" }, - "; } | undefined; } | { meta: { negate: boolean | undefined; type: string | undefined; params: any; value: undefined; alias?: string | null | undefined; disabled?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; key?: string | undefined; }; query: { match_phrase: { [x: string]: any; }; }; $state?: { store: ", + " | undefined; } | { query: ", + "FilterMetaParams", + " | undefined; alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: (", + "FilterMetaParams", + " & ", + "PhraseFilterMetaParams", + ") | undefined; value?: string | undefined; field?: string | undefined; } | { query: ", + "FilterMetaParams", + " | undefined; } | { query: ", + "FilterMetaParams", + " | undefined; alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params: (", + "FilterMetaParams", + " | undefined) & ", + "PhraseFilterValue", + "[]; value?: string | undefined; field?: string | undefined; } | { query: ", + "FilterMetaParams", + " | undefined; field: string; formattedValue: string; alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: ", + "FilterMetaParams", + " | undefined; value?: string | undefined; }; value: undefined; alias?: string | null | undefined; disabled?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; key?: string | undefined; }; query: { match_phrase: { [x: string]: ", + "FilterMetaParams", + "; }; }; $state?: { store: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -4262,7 +4963,14 @@ "label": "operator", "description": [], "signature": [ - "FilterOperator | undefined" + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FilterMeta", + "text": "FilterMeta" + }, + " | undefined" ], "path": "packages/kbn-es-query/src/filters/helpers/update_filter.ts", "deprecated": false, @@ -4272,17 +4980,18 @@ { "parentPluginId": "@kbn/es-query", "id": "def-common.updateFilter.$4", - "type": "Any", + "type": "CompoundType", "tags": [], "label": "params", "description": [], "signature": [ - "any" + "FilterMetaParams", + " | undefined" ], "path": "packages/kbn-es-query/src/filters/helpers/update_filter.ts", "deprecated": false, "trackAdoption": false, - "isRequired": true + "isRequired": false }, { "parentPluginId": "@kbn/es-query", @@ -4831,6 +5540,23 @@ "description": [ "\nAn interface for all possible range filter params\nIt is similar, but not identical to estypes.QueryDslRangeQuery" ], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.RangeFilterParams", + "text": "RangeFilterParams" + }, + " extends ", + { + "pluginId": "@kbn/utility-types", + "scope": "common", + "docId": "kibKbnUtilityTypesPluginApi", + "section": "def-common.SerializableRecord", + "text": "SerializableRecord" + } + ], "path": "packages/kbn-es-query/src/filters/build_filters/range_filter.ts", "deprecated": false, "trackAdoption": false, @@ -5183,24 +5909,24 @@ "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.PhrasesFilter", - "text": "PhrasesFilter" + "section": "def-common.PhraseFilter", + "text": "PhraseFilter" }, " | ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.ExistsFilter", - "text": "ExistsFilter" + "section": "def-common.PhrasesFilter", + "text": "PhrasesFilter" }, " | ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.PhraseFilter", - "text": "PhraseFilter" + "section": "def-common.ExistsFilter", + "text": "ExistsFilter" }, " | ", { @@ -5255,7 +5981,9 @@ "label": "FilterMeta", "description": [], "signature": [ - "{ alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }" + "{ alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: ", + "FilterMetaParams", + " | undefined; value?: string | undefined; }" ], "path": "packages/kbn-es-query/src/filters/build_filters/types.ts", "deprecated": false, @@ -5459,7 +6187,7 @@ "section": "def-common.FilterMeta", "text": "FilterMeta" }, - " & { params: ", + " & { params?: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -5467,7 +6195,7 @@ "section": "def-common.RangeFilterParams", "text": "RangeFilterParams" }, - "; field?: string | undefined; formattedValue?: string | undefined; }" + " | undefined; field?: string | undefined; formattedValue?: string | undefined; type: \"range\"; }" ], "path": "packages/kbn-es-query/src/filters/build_filters/range_filter.ts", "deprecated": false, diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index da781ad196acd..8861b6735b9cf 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 250 | 2 | 192 | 13 | +| 250 | 1 | 192 | 15 | ## Common diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 5c495dd649658..1c225b95d6794 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 7c43007947950..83c40ea9fa494 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 159dc92930b43..73d840aaea6bb 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index d3c1d8f62910f..b8b1df36612bd 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index b91ea5b281d51..e12319c4cffef 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 413dd63d35735..aa372f6f1f8f7 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index 64a9c1e5b7bf2..5da9b8bf77842 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/get-repo-files plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] --- import kbnGetRepoFilesObj from './kbn_get_repo_files.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.devdocs.json b/api_docs/kbn_guided_onboarding.devdocs.json index 09b27f31eede7..246ef8f89dd34 100644 --- a/api_docs/kbn_guided_onboarding.devdocs.json +++ b/api_docs/kbn_guided_onboarding.devdocs.json @@ -21,31 +21,31 @@ "functions": [ { "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.GuideCard", + "id": "def-common.GuideCards", "type": "Function", "tags": [], - "label": "GuideCard", + "label": "GuideCards", "description": [], "signature": [ - "({ useCase, guides, activateGuide, isDarkTheme, addBasePath, }: ", - "GuideCardProps", + "(props: ", + "GuideCardsProps", ") => JSX.Element" ], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/guide_card.tsx", + "path": "packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.tsx", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.GuideCard.$1", + "id": "def-common.GuideCards.$1", "type": "Object", "tags": [], - "label": "{\n useCase,\n guides,\n activateGuide,\n isDarkTheme,\n addBasePath,\n}", + "label": "props", "description": [], "signature": [ - "GuideCardProps" + "GuideCardsProps" ], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/guide_card.tsx", + "path": "packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.tsx", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -56,143 +56,32 @@ }, { "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.InfrastructureLinkCard", + "id": "def-common.GuideFilters", "type": "Function", "tags": [], - "label": "InfrastructureLinkCard", + "label": "GuideFilters", "description": [], "signature": [ - "({ navigateToApp, isDarkTheme, addBasePath, }: { navigateToApp: (appId: string, options?: ", - { - "pluginId": "@kbn/core-application-browser", - "scope": "common", - "docId": "kibKbnCoreApplicationBrowserPluginApi", - "section": "def-common.NavigateToAppOptions", - "text": "NavigateToAppOptions" - }, - " | undefined) => Promise; isDarkTheme: boolean; addBasePath: (url: string) => string; }) => JSX.Element" + "({ activeFilter, setActiveFilter }: GuideFiltersProps) => JSX.Element" ], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.tsx", + "path": "packages/kbn-guided-onboarding/src/components/landing_page/guide_filters.tsx", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.InfrastructureLinkCard.$1", + "id": "def-common.GuideFilters.$1", "type": "Object", "tags": [], - "label": "{\n navigateToApp,\n isDarkTheme,\n addBasePath,\n}", + "label": "{ activeFilter, setActiveFilter }", "description": [], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.tsx", + "signature": [ + "GuideFiltersProps" + ], + "path": "packages/kbn-guided-onboarding/src/components/landing_page/guide_filters.tsx", "deprecated": false, "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.InfrastructureLinkCard.$1.navigateToApp", - "type": "Function", - "tags": [], - "label": "navigateToApp", - "description": [], - "signature": [ - "(appId: string, options?: ", - { - "pluginId": "@kbn/core-application-browser", - "scope": "common", - "docId": "kibKbnCoreApplicationBrowserPluginApi", - "section": "def-common.NavigateToAppOptions", - "text": "NavigateToAppOptions" - }, - " | undefined) => Promise" - ], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.InfrastructureLinkCard.$1.navigateToApp.$1", - "type": "string", - "tags": [], - "label": "appId", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.InfrastructureLinkCard.$1.navigateToApp.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - { - "pluginId": "@kbn/core-application-browser", - "scope": "common", - "docId": "kibKbnCoreApplicationBrowserPluginApi", - "section": "def-common.NavigateToAppOptions", - "text": "NavigateToAppOptions" - }, - " | undefined" - ], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.InfrastructureLinkCard.$1.isDarkTheme", - "type": "boolean", - "tags": [], - "label": "isDarkTheme", - "description": [], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.InfrastructureLinkCard.$1.addBasePath", - "type": "Function", - "tags": [], - "label": "addBasePath", - "description": [], - "signature": [ - "(url: string) => string" - ], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.InfrastructureLinkCard.$1.addBasePath.$1", - "type": "string", - "tags": [], - "label": "url", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - } - ] + "isRequired": true } ], "returnComment": [], @@ -638,15 +527,16 @@ "misc": [ { "parentPluginId": "@kbn/guided-onboarding", - "id": "def-common.GuideCardUseCase", + "id": "def-common.GuideFilterValues", "type": "Type", "tags": [], - "label": "GuideCardUseCase", + "label": "GuideFilterValues", "description": [], "signature": [ - "\"search\" | \"kubernetes\" | \"siem\"" + "\"all\" | ", + "GuideCardSolutions" ], - "path": "packages/kbn-guided-onboarding/src/components/landing_page/guide_card.tsx", + "path": "packages/kbn-guided-onboarding/src/components/landing_page/guide_filters.tsx", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index e078a0ec24285..af01c0d7256f2 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 58 | 0 | 56 | 1 | +| 52 | 0 | 50 | 2 | ## Common diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 126aba552fcff..177de30fa7546 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index 6adb4eb34c18e..6fa1d6f9f1122 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index 2e095a65db292..22c0fd0a50667 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 2b295a805e432..9513e554824d4 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index c397501db921f..61f5a3e462a23 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index 2a49529919548..3f5772d15740c 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index 5fcd0953e989d..f09be75044bbe 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 2c7b594374be4..bc84da7492c2c 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index b578d6f0ff054..39b9d75d35825 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index a5fd9cbf4e7dd..4c98355a61e2c 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 11853327bf239..79c7d63e68da3 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index 11b23f85db0ea..1831a0ea52ab3 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index 3927f61297815..b825cd4a3c8da 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.devdocs.json b/api_docs/kbn_kibana_manifest_schema.devdocs.json index fb46dedb5099d..e9509c172d6e8 100644 --- a/api_docs/kbn_kibana_manifest_schema.devdocs.json +++ b/api_docs/kbn_kibana_manifest_schema.devdocs.json @@ -1034,6 +1034,269 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build", + "type": "Object", + "tags": [], + "label": "build", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties", + "type": "Object", + "tags": [], + "label": "properties", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties.extraExcludes", + "type": "Object", + "tags": [], + "label": "extraExcludes", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties.extraExcludes.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties.extraExcludes.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties.extraExcludes.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties.extraExcludes.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties.noParse", + "type": "Object", + "tags": [], + "label": "noParse", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties.noParse.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties.noParse.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties.noParse.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.build.properties.noParse.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.serviceFolders", + "type": "Object", + "tags": [], + "label": "serviceFolders", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.serviceFolders.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.serviceFolders.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.serviceFolders.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.serviceFolders.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.serviceFolders.deprecated", + "type": "boolean", + "tags": [], + "label": "deprecated", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.description", + "type": "Object", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.description.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.description.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ] }, @@ -1045,7 +1308,7 @@ "label": "oneOf", "description": [], "signature": [ - "({ type: string; properties: { type: { enum: string[]; }; plugin: { type: string; required: string[]; properties: { id: { type: string; pattern: string; }; configPath: { description: string; type: string; items: { type: string; pattern: string; }; }; requiredPlugins: { type: string; items: { type: string; pattern: string; }; }; optionalPlugins: { type: string; items: { type: string; pattern: string; }; }; description: { description: string; type: string; }; enabledOnAnonymousPages: { description: string; type: string; }; serviceFolders: { description: string; type: string; items: { type: string; }; }; }; }; }; } | { type: string; properties: { type: { const: string; }; sharedBrowserBundle: { type: string; description: string; }; }; } | { type: string; properties: { type: { enum: string[]; }; }; })[]" + "({ type: string; required: string[]; properties: { type: { const: string; }; plugin: { type: string; required: string[]; properties: { id: { type: string; pattern: string; }; configPath: { description: string; oneOf: ({ type: string; items: { type: string; }; } | { type: string; })[]; }; requiredPlugins: { type: string; items: { type: string; pattern: string; }; }; optionalPlugins: { type: string; items: { type: string; pattern: string; }; }; enabledOnAnonymousPages: { description: string; type: string; }; type: { description: string; enum: string[]; }; browser: { type: string; description: string; }; server: { type: string; description: string; }; }; }; }; } | { type: string; properties: { type: { const: string; }; sharedBrowserBundle: { type: string; description: string; }; }; } | { type: string; properties: { type: { enum: string[]; }; }; })[]" ], "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", "deprecated": false, diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 3e923c48e785f..1adb8b7f9e840 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 86 | 0 | 85 | 0 | +| 108 | 0 | 107 | 0 | ## Common diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index ec6755a037e54..098537901fd52 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 2884e1a95e53e..ab0353e48ef49 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 1f5e5a603529a..62c9fe2946366 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index e4fa5b1fc4406..126b1274f5531 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 9073884e3e906..d6b294c8d4cc2 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 50230f0c37c4e..3690e2ce1807e 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index e0a807ae7fcfc..d4e7c740685a9 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 8663b0973a4fa..43318dd19459b 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index bd86f138c7f45..3c32f728ed4e9 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index e8945cdbae852..3fc42474b9a4c 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index 32b05613d6862..3dcfccff45b6b 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 19e67e69be75c..21c6c0055951e 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 726577ba097d1..25a823c6daf76 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index a5b520a359366..513656ce6bd12 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 9b21b62a02005..b5b8f06bff4e4 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index c1c4409f67b83..ef49a2ac71f40 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index b6f86dc0cd4fa..c9d3f3f69f9a0 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index 0229ccceff20c..e5c5e416f823f 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 1ac67e0201af6..1467e62c3cbba 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 182b3d18d1317..e76b91a98c71c 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index 4a2f7a1548390..c70133738de2e 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index bcc9621f6eaf5..ec70e3dbe6a85 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index a8aa63023c828..156bef37af0be 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index 323e39b6192ff..a699292a465fc 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index eae842c432038..c4b121b721edc 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 4dd0be8388470..b849e2b7cd41a 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 977aecbf64ae4..11074475cfac1 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.devdocs.json b/api_docs/kbn_rule_data_utils.devdocs.json index 41036062e5590..1b93a3e0236c8 100644 --- a/api_docs/kbn_rule_data_utils.devdocs.json +++ b/api_docs/kbn_rule_data_utils.devdocs.json @@ -328,6 +328,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/rule-data-utils", + "id": "def-common.ALERT_LAST_DETECTED", + "type": "string", + "tags": [], + "label": "ALERT_LAST_DETECTED", + "description": [], + "signature": [ + "\"kibana.alert.last_detected\"" + ], + "path": "packages/kbn-rule-data-utils/src/default_alerts_as_data.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/rule-data-utils", "id": "def-common.ALERT_NAMESPACE", @@ -1296,7 +1311,7 @@ "label": "DefaultAlertFieldName", "description": [], "signature": [ - "\"kibana\" | \"kibana.alert.rule.rule_type_id\" | \"kibana.alert.rule.consumer\" | \"kibana.alert.rule.execution.uuid\" | \"kibana.alert\" | \"kibana.alert.rule\" | \"kibana.alert.rule.parameters\" | \"kibana.alert.rule.producer\" | \"kibana.space_ids\" | \"kibana.alert.uuid\" | \"kibana.alert.start\" | \"kibana.alert.time_range\" | \"kibana.alert.end\" | \"kibana.alert.duration.us\" | \"kibana.alert.status\" | \"kibana.alert.flapping\" | \"kibana.version\" | \"kibana.alert.workflow_status\" | \"kibana.alert.action_group\" | \"kibana.alert.reason\" | \"kibana.alert.rule.category\" | \"kibana.alert.rule.uuid\" | \"kibana.alert.rule.name\" | \"kibana.alert.rule.tags\" | \"kibana.alert.id\"" + "\"kibana\" | \"kibana.alert.rule.rule_type_id\" | \"kibana.alert.rule.consumer\" | \"kibana.alert.rule.execution.uuid\" | \"kibana.alert\" | \"kibana.alert.rule\" | \"kibana.alert.rule.parameters\" | \"kibana.alert.rule.producer\" | \"kibana.space_ids\" | \"kibana.alert.uuid\" | \"kibana.alert.start\" | \"kibana.alert.time_range\" | \"kibana.alert.end\" | \"kibana.alert.duration.us\" | \"kibana.alert.status\" | \"kibana.alert.flapping\" | \"kibana.version\" | \"kibana.alert.workflow_status\" | \"kibana.alert.action_group\" | \"kibana.alert.reason\" | \"kibana.alert.rule.category\" | \"kibana.alert.rule.uuid\" | \"kibana.alert.rule.name\" | \"kibana.alert.rule.tags\" | \"kibana.alert.last_detected\" | \"kibana.alert.id\"" ], "path": "packages/kbn-rule-data-utils/src/default_alerts_as_data.ts", "deprecated": false, diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index aaae88b9fcbb1..38899d6e2dec0 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 99 | 0 | 96 | 0 | +| 100 | 0 | 97 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 9d59e07d5c104..b2d6bd3ea035f 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index 4dd583520714e..6cdc69c441a91 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index dd045c36fcb75..b7a486c967492 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 0d5b772d8a0dd..53238d3b163ae 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 1ae1b5f1ccda9..e7d37388c9f03 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index bbe20a810001c..ed33cedfefe74 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 7d5c918352fe6..9f6886c027809 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index df64ef56e48f6..00e28e1871679 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index 515fbef38847a..2f0133ec06b2f 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index 2bb6eec809af1..e1b4c34903283 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 69ccb1593f4d8..c4dd45085cb94 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index f85008faeacc6..3415f5b27fdf6 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 0e2bbcefdc4d8..7fa3b2d99f96e 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 3ba4c88efcf2e..1dd1c6302ebcc 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index b3be901ccd130..50973440f0df3 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 1354047ee3695..cbb8e8782b039 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 576d0e5930ffd..38f300c94bd1c 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index 3db2e619c3bad..52638c374cde5 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 0ddbad2831322..f83f2d488e04f 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index 613403ecdf068..a2b598d004748 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index 129b0d1f90c20..0d3f31b8865c0 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 0ddb60c7d6611..a5466eca5acc1 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index a1ae33e86b3f9..14bb9e5dc00c2 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 64cc68ecff23d..6d53b085aa5ba 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 30dd624ba0889..7c677fb1f9a6b 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index 4c8a14cb5301d..8b001b1fa983c 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 1f27f197f6aa7..b5195258c7f7b 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index d6fa862f09ead..01066d325d4aa 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index 36d55cb5d5ca2..f10c380e1ae7b 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 6b94f4f24bbca..1f009db9e7a52 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index 060c2573ec94b..d32cc40fd978e 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index bb9b58d2daf61..2456510388acf 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index 663368f7eff44..49ba637448c8e 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index 729eaf3eaaa43..86256fa2df322 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 837d0a3e579df..f4e6a2cd3be4c 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index 03ee5c2618af1..9b127f869879d 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index cd35f7e5cf03d..92d102c63395d 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.devdocs.json b/api_docs/kbn_shared_ux_page_analytics_no_data.devdocs.json index 82ac24347e054..217e49d5572e4 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.devdocs.json +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.devdocs.json @@ -66,7 +66,7 @@ "\nA pure component of an entire page that can be displayed when Kibana \"has no data\", specifically for Analytics." ], "signature": [ - "({ kibanaGuideDocLink, onDataViewCreated, allowAdHocDataView, }: ", + "({ kibanaGuideDocLink, onDataViewCreated, allowAdHocDataView, showPlainSpinner, }: ", "Props", ") => JSX.Element" ], @@ -79,7 +79,7 @@ "id": "def-common.AnalyticsNoDataPage.$1", "type": "Object", "tags": [], - "label": "{\n kibanaGuideDocLink,\n onDataViewCreated,\n allowAdHocDataView,\n}", + "label": "{\n kibanaGuideDocLink,\n onDataViewCreated,\n allowAdHocDataView,\n showPlainSpinner,\n}", "description": [], "signature": [ "Props" diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index dac874e621691..7cdb30d5f9eee 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json index ee58f4094a5e3..7bd3c319b4cfd 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json @@ -108,6 +108,54 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "@kbn/shared-ux-page-analytics-no-data-mocks", + "id": "def-common.StorybookMock.serviceArguments.customBranding", + "type": "Object", + "tags": [], + "label": "customBranding", + "description": [], + "path": "packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-page-analytics-no-data-mocks", + "id": "def-common.StorybookMock.serviceArguments.customBranding.hasCustomBranding$", + "type": "Object", + "tags": [], + "label": "hasCustomBranding$", + "description": [], + "path": "packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-page-analytics-no-data-mocks", + "id": "def-common.StorybookMock.serviceArguments.customBranding.hasCustomBranding$.control", + "type": "string", + "tags": [], + "label": "control", + "description": [], + "path": "packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-page-analytics-no-data-mocks", + "id": "def-common.StorybookMock.serviceArguments.customBranding.hasCustomBranding$.defaultValue", + "type": "boolean", + "tags": [], + "label": "defaultValue", + "description": [], + "path": "packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] } ] }, @@ -219,6 +267,24 @@ "children": [], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-page-analytics-no-data-mocks", + "id": "def-common.getServicesMockCustomBranding", + "type": "Function", + "tags": [], + "label": "getServicesMockCustomBranding", + "description": [], + "signature": [ + "() => ", + "AnalyticsNoDataPageServices" + ], + "path": "packages/shared-ux/page/analytics_no_data/mocks/src/jest.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [], diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 246359c3f1671..b8a232f981982 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 12 | 0 | 12 | 0 | +| 17 | 0 | 17 | 0 | ## Common diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.devdocs.json b/api_docs/kbn_shared_ux_page_kibana_no_data.devdocs.json index e313723153e65..0076a7f37dc80 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.devdocs.json +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.devdocs.json @@ -29,7 +29,7 @@ "\nA page to display when Kibana has no data, prompting a person to add integrations or create a new data view." ], "signature": [ - "({ onDataViewCreated, noDataConfig, allowAdHocDataView, }: ", + "({ onDataViewCreated, noDataConfig, allowAdHocDataView, showPlainSpinner, }: ", "KibanaNoDataPageProps", ") => JSX.Element | null" ], @@ -42,7 +42,7 @@ "id": "def-common.KibanaNoDataPage.$1", "type": "Object", "tags": [], - "label": "{\n onDataViewCreated,\n noDataConfig,\n allowAdHocDataView,\n}", + "label": "{\n onDataViewCreated,\n noDataConfig,\n allowAdHocDataView,\n showPlainSpinner,\n}", "description": [], "signature": [ "KibanaNoDataPageProps" diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index b9c8d7b042dac..450465d2939ca 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json index 6baad57602555..2134860750376 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json @@ -293,7 +293,7 @@ "section": "def-common.Params", "text": "Params" }, - ") => { noDataConfig: { solution: any; logo: any; action: { elasticAgent: { title: string; }; }; docsLink: string; }; onDataViewCreated: ", + ") => { showPlainSpinner: boolean; noDataConfig: { solution: any; logo: any; action: { elasticAgent: { title: string; }; }; docsLink: string; }; onDataViewCreated: ", "HandlerFunction", "; }" ], diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index 4ab936ba53775..e429bb8e86ec5 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index d67cbc6398ee7..8557219bb0b5a 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index 0295d42172b33..370f919ce3167 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 6e7c46200490d..ec18f3876cdeb 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index 5306cc61ad823..7f2b6f240d65e 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index 953a303571a2e..fcf612a947a83 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index c975b40c2d3c0..1f3a59872b448 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index ce5590d9103d9..860162cd68f54 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 66bdc9c0ac3f5..45fb1fa015506 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index fea5b27e93efe..fb4e407f3374a 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.devdocs.json b/api_docs/kbn_shared_ux_prompt_not_found.devdocs.json index 99a5e86068dfb..8b804e94e8b1d 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.devdocs.json +++ b/api_docs/kbn_shared_ux_prompt_not_found.devdocs.json @@ -29,7 +29,7 @@ "\nPredefined `EuiEmptyPrompt` for 404 pages." ], "signature": [ - "({ actions }: NotFoundProps) => JSX.Element" + "({ actions, title, body }: NotFoundProps) => JSX.Element" ], "path": "packages/shared-ux/prompt/not_found/src/not_found_prompt.tsx", "deprecated": false, @@ -40,7 +40,7 @@ "id": "def-common.NotFoundPrompt.$1", "type": "Object", "tags": [], - "label": "{ actions }", + "label": "{ actions, title, body }", "description": [], "signature": [ "NotFoundProps" diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index 0d60150cd58b8..df687e859cd46 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index e6ce595ac568e..91505f0b67758 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index 920afbc20a329..6ec532aa2cca2 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index 5baf421f29c62..7d18757c04a7e 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index 5c97689451e8b..ea127e62a8ab8 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index ff2270fc7658b..a0be397983c80 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.devdocs.json b/api_docs/kbn_slo_schema.devdocs.json index e0241d83dc24d..74ba519202778 100644 --- a/api_docs/kbn_slo_schema.devdocs.json +++ b/api_docs/kbn_slo_schema.devdocs.json @@ -520,7 +520,7 @@ "label": "FindSLOResponse", "description": [], "signature": [ - "{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }" + "{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -535,7 +535,7 @@ "label": "GetSLOResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -557,6 +557,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.ManageSLOParams", + "type": "Type", + "tags": [], + "label": "ManageSLOParams", + "description": [], + "signature": [ + "{ id: string; }" + ], + "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/slo-schema", "id": "def-common.SLOResponse", @@ -565,7 +580,7 @@ "label": "SLOResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -580,7 +595,7 @@ "label": "SLOWithSummaryResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -665,7 +680,7 @@ "label": "UpdateSLOResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -1527,7 +1542,9 @@ "section": "def-common.Duration", "text": "Duration" }, - ", string, unknown>; }>; createdAt: ", + ", string, unknown>; }>; enabled: ", + "BooleanC", + "; createdAt: ", "Type", "; updatedAt: ", "Type", @@ -1787,7 +1804,9 @@ "section": "def-common.Duration", "text": "Duration" }, - ", string, unknown>; }>; createdAt: ", + ", string, unknown>; }>; enabled: ", + "BooleanC", + "; createdAt: ", "Type", "; updatedAt: ", "Type", @@ -2092,6 +2111,26 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.manageSLOParamsSchema", + "type": "Object", + "tags": [], + "label": "manageSLOParamsSchema", + "description": [], + "signature": [ + "TypeC", + "<{ path: ", + "TypeC", + "<{ id: ", + "StringC", + "; }>; }>" + ], + "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/slo-schema", "id": "def-common.objectiveSchema", @@ -2447,7 +2486,9 @@ "section": "def-common.Duration", "text": "Duration" }, - ", string, unknown>; }>; createdAt: ", + ", string, unknown>; }>; enabled: ", + "BooleanC", + "; createdAt: ", "Type", "; updatedAt: ", "Type", @@ -2659,6 +2700,8 @@ }, ", string, unknown>; }>; revision: ", "NumberC", + "; enabled: ", + "BooleanC", "; createdAt: ", "Type", "; updatedAt: ", @@ -2873,7 +2916,9 @@ "section": "def-common.Duration", "text": "Duration" }, - ", string, unknown>; }>; createdAt: ", + ", string, unknown>; }>; enabled: ", + "BooleanC", + "; createdAt: ", "Type", "; updatedAt: ", "Type", @@ -3113,6 +3158,8 @@ }, ", string, unknown>; }>; revision: ", "NumberC", + "; enabled: ", + "BooleanC", "; createdAt: ", "Type", "; updatedAt: ", @@ -3685,7 +3732,9 @@ "section": "def-common.Duration", "text": "Duration" }, - ", string, unknown>; }>; createdAt: ", + ", string, unknown>; }>; enabled: ", + "BooleanC", + "; createdAt: ", "Type", "; updatedAt: ", "Type", diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index e9ef7a7af2377..1e9250cb4a88b 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 73 | 0 | 73 | 0 | +| 75 | 0 | 75 | 0 | ## Common diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index cae8b252bb919..2d9bf6029fa81 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index d03ada1ae175e..c26b9ae3fb5d9 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index e3a26453bf082..c8d825a25bbee 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index 04ac3608174b9..4049c743592eb 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 960d40b23b79c..f1809b7d4e4f1 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 1e2ace57b7990..8a6846b8a6da8 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 935d7de444987..4e4f03901b4b3 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index 44fd681205e76..dbee14eda1fd3 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 951d9b19af659..42bf35af75490 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 68479d0480a5f..675f87d17019a 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 70dad33d210fa..67c01b37c5473 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 1c2e1f15f9654..16ccdd39ef13a 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index 673383ef97b67..81b24c5ffe6e5 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 89f4e93704d27..cb1ea4b5b8f52 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index fce46e8052a66..22ff86de7ea75 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index e71ed78870a3b..f2bb5aa9041fd 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index 6e4fd9785a7dc..81793b6da6787 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index 00e072693095c..18d029130053e 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index db5e4488f9758..af01a9fbae92d 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index e5fd98c4ccc0b..3a85ede27a7b0 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 6352f703cc561..9d72153e81fce 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index fe1bcfa772cf6..33fa401d042a7 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 96d556658dbdf..a324aff5ac673 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 29a49dcef5bb7..887306f54c954 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 54828ce874a67..a7822f1638329 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index d627d5d0c6893..e0162e2602462 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index e9199a657935a..678c2884c02ee 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 2096ce8598128..6121094fed9d3 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 713b6e659790a..4c1a8a812b598 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 362ddfc8be3aa..80ea229325c91 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index a0ec73841ae03..692ac23d1ec9f 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index f8db2ec390c7a..def1256f632d4 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 7372a92b62978..9e606d365b4b7 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index dfad2c51573b2..951f25cb24321 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index e2be5b8a93ce7..5100e87a3357b 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index e935ba982517e..8d96e2b5007fe 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 38a0fe38afb2b..bfa39194c308e 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index d369b808ecb52..79bf3acafe827 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -4353,6 +4353,26 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "observability", + "id": "def-public.ObservabilityPublicPluginsStart.unifiedSearch", + "type": "Object", + "tags": [], + "label": "unifiedSearch", + "description": [], + "signature": [ + { + "pluginId": "unifiedSearch", + "scope": "public", + "docId": "kibUnifiedSearchPluginApi", + "section": "def-public.UnifiedSearchPublicPluginStart", + "text": "UnifiedSearchPublicPluginStart" + } + ], + "path": "x-pack/plugins/observability/public/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "observability", "id": "def-public.ObservabilityPublicPluginsStart.home", @@ -4633,16 +4653,16 @@ "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.ExistsFilter", - "text": "ExistsFilter" + "section": "def-common.PhraseFilter", + "text": "PhraseFilter" }, " | ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.PhraseFilter", - "text": "PhraseFilter" + "section": "def-common.ExistsFilter", + "text": "ExistsFilter" }, " | ", { @@ -4671,16 +4691,16 @@ "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.ExistsFilter", - "text": "ExistsFilter" + "section": "def-common.PhraseFilter", + "text": "PhraseFilter" }, " | ", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.PhraseFilter", - "text": "PhraseFilter" + "section": "def-common.ExistsFilter", + "text": "ExistsFilter" }, " | ", { @@ -10002,7 +10022,85 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; }, ", + ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteCreateOptions", + "text": "ObservabilityRouteCreateOptions" + }, + "> | undefined; \"GET /api/observability/slos/{id}\"?: ", + { + "pluginId": "@kbn/server-route-repository", + "scope": "public", + "docId": "kibKbnServerRouteRepositoryPluginApi", + "section": "def-public.ServerRoute", + "text": "ServerRoute" + }, + "<\"GET /api/observability/slos/{id}\", ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ id: ", + "StringC", + "; }>; }>, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteHandlerResources", + "text": "ObservabilityRouteHandlerResources" + }, + ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteCreateOptions", + "text": "ObservabilityRouteCreateOptions" + }, + "> | undefined; \"GET /api/observability/slos\"?: ", + { + "pluginId": "@kbn/server-route-repository", + "scope": "public", + "docId": "kibKbnServerRouteRepositoryPluginApi", + "section": "def-public.ServerRoute", + "text": "ServerRoute" + }, + "<\"GET /api/observability/slos\", ", + "PartialC", + "<{ query: ", + "PartialC", + "<{ name: ", + "StringC", + "; indicatorTypes: ", + "Type", + "; page: ", + "StringC", + "; perPage: ", + "StringC", + "; sortBy: ", + "UnionC", + "<[", + "LiteralC", + "<\"name\">, ", + "LiteralC", + "<\"indicatorType\">]>; sortDirection: ", + "UnionC", + "<[", + "LiteralC", + "<\"asc\">, ", + "LiteralC", + "<\"desc\">]>; }>; }>, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteHandlerResources", + "text": "ObservabilityRouteHandlerResources" + }, + ", { page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }, ", { "pluginId": "observability", "scope": "server", @@ -10042,7 +10140,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"GET /api/observability/slos/{id}\"?: ", + "> | undefined; \"POST /api/observability/slos/{id}/enable\"?: ", { "pluginId": "@kbn/server-route-repository", "scope": "public", @@ -10050,7 +10148,7 @@ "section": "def-public.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/observability/slos/{id}\", ", + "<\"POST /api/observability/slos/{id}/enable\", ", "TypeC", "<{ path: ", "TypeC", @@ -10064,7 +10162,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }, ", + ", void, ", { "pluginId": "observability", "scope": "server", @@ -10072,7 +10170,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"GET /api/observability/slos\"?: ", + "> | undefined; \"POST /api/observability/slos/{id}/disable\"?: ", { "pluginId": "@kbn/server-route-repository", "scope": "public", @@ -10080,31 +10178,13 @@ "section": "def-public.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/observability/slos\", ", - "PartialC", - "<{ query: ", - "PartialC", - "<{ name: ", - "StringC", - "; indicatorTypes: ", - "Type", - "; page: ", - "StringC", - "; perPage: ", + "<\"POST /api/observability/slos/{id}/disable\", ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ id: ", "StringC", - "; sortBy: ", - "UnionC", - "<[", - "LiteralC", - "<\"name\">, ", - "LiteralC", - "<\"indicatorType\">]>; sortDirection: ", - "UnionC", - "<[", - "LiteralC", - "<\"asc\">, ", - "LiteralC", - "<\"desc\">]>; }>; }>, ", + "; }>; }>, ", { "pluginId": "observability", "scope": "server", @@ -10112,7 +10192,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }, ", + ", void, ", { "pluginId": "observability", "scope": "server", @@ -10656,7 +10736,85 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; }, ", + ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteCreateOptions", + "text": "ObservabilityRouteCreateOptions" + }, + "> | undefined; \"GET /api/observability/slos/{id}\"?: ", + { + "pluginId": "@kbn/server-route-repository", + "scope": "public", + "docId": "kibKbnServerRouteRepositoryPluginApi", + "section": "def-public.ServerRoute", + "text": "ServerRoute" + }, + "<\"GET /api/observability/slos/{id}\", ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ id: ", + "StringC", + "; }>; }>, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteHandlerResources", + "text": "ObservabilityRouteHandlerResources" + }, + ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteCreateOptions", + "text": "ObservabilityRouteCreateOptions" + }, + "> | undefined; \"GET /api/observability/slos\"?: ", + { + "pluginId": "@kbn/server-route-repository", + "scope": "public", + "docId": "kibKbnServerRouteRepositoryPluginApi", + "section": "def-public.ServerRoute", + "text": "ServerRoute" + }, + "<\"GET /api/observability/slos\", ", + "PartialC", + "<{ query: ", + "PartialC", + "<{ name: ", + "StringC", + "; indicatorTypes: ", + "Type", + "; page: ", + "StringC", + "; perPage: ", + "StringC", + "; sortBy: ", + "UnionC", + "<[", + "LiteralC", + "<\"name\">, ", + "LiteralC", + "<\"indicatorType\">]>; sortDirection: ", + "UnionC", + "<[", + "LiteralC", + "<\"asc\">, ", + "LiteralC", + "<\"desc\">]>; }>; }>, ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteHandlerResources", + "text": "ObservabilityRouteHandlerResources" + }, + ", { page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }, ", { "pluginId": "observability", "scope": "server", @@ -10696,7 +10854,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"GET /api/observability/slos/{id}\"?: ", + "> | undefined; \"POST /api/observability/slos/{id}/enable\"?: ", { "pluginId": "@kbn/server-route-repository", "scope": "public", @@ -10704,7 +10862,7 @@ "section": "def-public.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/observability/slos/{id}\", ", + "<\"POST /api/observability/slos/{id}/enable\", ", "TypeC", "<{ path: ", "TypeC", @@ -10718,7 +10876,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }, ", + ", void, ", { "pluginId": "observability", "scope": "server", @@ -10726,7 +10884,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"GET /api/observability/slos\"?: ", + "> | undefined; \"POST /api/observability/slos/{id}/disable\"?: ", { "pluginId": "@kbn/server-route-repository", "scope": "public", @@ -10734,31 +10892,13 @@ "section": "def-public.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/observability/slos\", ", - "PartialC", - "<{ query: ", - "PartialC", - "<{ name: ", - "StringC", - "; indicatorTypes: ", - "Type", - "; page: ", - "StringC", - "; perPage: ", + "<\"POST /api/observability/slos/{id}/disable\", ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ id: ", "StringC", - "; sortBy: ", - "UnionC", - "<[", - "LiteralC", - "<\"name\">, ", - "LiteralC", - "<\"indicatorType\">]>; sortDirection: ", - "UnionC", - "<[", - "LiteralC", - "<\"asc\">, ", - "LiteralC", - "<\"desc\">]>; }>; }>, ", + "; }>; }>, ", { "pluginId": "observability", "scope": "server", @@ -10766,7 +10906,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; createdAt: string; updatedAt: string; } & { summary: { status: \"NO_DATA\" | \"HEALTHY\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }, ", + ", void, ", { "pluginId": "observability", "scope": "server", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index eee80c7d07501..4fdd36a64409b 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Observability UI](https://github.com/orgs/elastic/teams/observability-u | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 606 | 41 | 600 | 32 | +| 607 | 41 | 601 | 32 | ## Client diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index dd794c0fbfba7..1988fe6de618d 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 96bd8997cd457..f1e1d8e7cd38c 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 564 | 464 | 43 | +| 568 | 467 | 44 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 70017 | 527 | 59226 | 1206 | +| 70145 | 526 | 59352 | 1213 | ## Plugin Directory @@ -47,8 +47,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | cloudLinks | [Kibana Core](https://github.com/orgs/elastic/teams/@kibana-core) | Adds the links to the Elastic Cloud console | 0 | 0 | 0 | 0 | | | [Cloud Security Posture](https://github.com/orgs/elastic/teams/cloud-posture-security) | The cloud security posture plugin | 17 | 0 | 2 | 2 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 13 | 0 | 13 | 1 | +| | [@elastic/kibana-global-experience](https://github.com/orgs/elastic/teams/@elastic/kibana-global-experience) | Content management app | 5 | 0 | 5 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 268 | 0 | 264 | 9 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2832 | 17 | 1016 | 0 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2845 | 17 | 1029 | 0 | | crossClusterReplication | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | customBranding | [global-experience](https://github.com/orgs/elastic/teams/kibana-global-experience) | Enables customization of Kibana | 0 | 0 | 0 | 0 | | | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 107 | 0 | 88 | 1 | @@ -89,7 +90,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 62 | 0 | 62 | 2 | | | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/team:AppServicesUx) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 254 | 1 | 45 | 5 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/@elastic/appex-sharedux) | Simple UI for managing files in Kibana | 2 | 1 | 2 | 0 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1040 | 3 | 935 | 25 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1068 | 3 | 963 | 26 | | ftrApis | [Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | @@ -126,7 +127,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 34 | 0 | 34 | 2 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 2 | 0 | 2 | 1 | -| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 606 | 41 | 600 | 32 | +| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 607 | 41 | 601 | 32 | | | [Security asset management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 24 | 0 | 24 | 7 | | painlessLab | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 217 | 7 | 161 | 11 | @@ -134,7 +135,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 4 | 0 | 4 | 0 | | | [Kibana Reporting Services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Reporting Services enables applications to feature reports that the user can automate with Watcher and download later. | 36 | 0 | 16 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 21 | 0 | 21 | 0 | -| | [RAC](https://github.com/orgs/elastic/teams/rac) | - | 241 | 0 | 213 | 10 | +| | [RAC](https://github.com/orgs/elastic/teams/rac) | - | 251 | 0 | 223 | 11 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 24 | 0 | 19 | 2 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 196 | 2 | 155 | 5 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 16 | 0 | 16 | 0 | @@ -150,7 +151,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Security Team](https://github.com/orgs/elastic/teams/security-team) | - | 7 | 0 | 7 | 1 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds URL Service and sharing capabilities to Kibana | 118 | 0 | 59 | 10 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 22 | 1 | 22 | 1 | -| | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 260 | 0 | 64 | 0 | +| | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 261 | 0 | 65 | 0 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 12 | 0 | 12 | 2 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 4 | 0 | 4 | 0 | | synthetics | [Uptime](https://github.com/orgs/elastic/teams/uptime) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 0 | @@ -159,11 +160,11 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 31 | 0 | 26 | 6 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 1 | 0 | 1 | 0 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 5 | 0 | 0 | 0 | -| | [Protections Experience Team](https://github.com/orgs/elastic/teams/protections-experience) | Elastic threat intelligence helps you see if you are open to or have been subject to current or historical known threats | 35 | 0 | 15 | 5 | +| | [Protections Experience Team](https://github.com/orgs/elastic/teams/protections-experience) | Elastic threat intelligence helps you see if you are open to or have been subject to current or historical known threats | 35 | 0 | 14 | 5 | | | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 257 | 1 | 214 | 20 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [Kibana Localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 587 | 11 | 558 | 53 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 589 | 11 | 560 | 53 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds UI Actions service to Kibana | 134 | 2 | 92 | 9 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends UI Actions plugin with more functionality | 206 | 0 | 140 | 9 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list which can be integrated into apps | 267 | 0 | 242 | 7 | @@ -217,6 +218,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 3 | 0 | 3 | 0 | | | [Owner missing] | - | 62 | 0 | 17 | 1 | | | [Owner missing] | - | 2 | 0 | 2 | 0 | +| | [Owner missing] | - | 2 | 0 | 2 | 1 | +| | [Owner missing] | - | 37 | 0 | 36 | 0 | | | [Owner missing] | - | 106 | 0 | 80 | 1 | | | Kibana Core | - | 73 | 0 | 44 | 9 | | | Kibana Core | - | 24 | 0 | 24 | 0 | @@ -251,7 +254,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 5 | 0 | 5 | 0 | | | [Owner missing] | - | 4 | 0 | 4 | 0 | | | [Owner missing] | - | 6 | 0 | 1 | 0 | -| | [Owner missing] | - | 6 | 0 | 6 | 0 | +| | [Owner missing] | - | 11 | 0 | 11 | 0 | | | [Owner missing] | - | 6 | 0 | 6 | 1 | | | [Owner missing] | - | 4 | 0 | 4 | 0 | | | Kibana Core | - | 9 | 0 | 3 | 0 | @@ -393,7 +396,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 4 | 0 | 4 | 0 | | | [Owner missing] | - | 27 | 0 | 14 | 1 | | | Kibana Core | - | 7 | 0 | 3 | 0 | -| | [Owner missing] | - | 250 | 2 | 192 | 13 | +| | [Owner missing] | - | 250 | 1 | 192 | 15 | | | Kibana Core | - | 11 | 0 | 11 | 0 | | | [Owner missing] | - | 2 | 0 | 1 | 0 | | | [Owner missing] | - | 20 | 0 | 16 | 0 | @@ -401,7 +404,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 29 | 0 | 29 | 1 | | | [Owner missing] | - | 1 | 0 | 0 | 0 | | | [Owner missing] | - | 6 | 0 | 0 | 0 | -| | [Owner missing] | - | 58 | 0 | 56 | 1 | +| | [Owner missing] | - | 52 | 0 | 50 | 2 | | | [Owner missing] | - | 19 | 1 | 12 | 0 | | | [Owner missing] | - | 3 | 0 | 3 | 0 | | | Kibana Core | - | 1 | 0 | 1 | 0 | @@ -415,7 +418,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 13 | 0 | 13 | 0 | | | [Owner missing] | - | 67 | 0 | 62 | 5 | | | [Owner missing] | - | 32 | 2 | 28 | 0 | -| | [Owner missing] | - | 86 | 0 | 85 | 0 | +| | [Owner missing] | - | 108 | 0 | 107 | 0 | | | [Owner missing] | - | 7 | 0 | 5 | 0 | | | Kibana Core | - | 27 | 0 | 1 | 2 | | | Kibana Core | - | 8 | 0 | 8 | 0 | @@ -443,7 +446,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 13 | 0 | 9 | 0 | | | [Owner missing] | - | 6 | 0 | 6 | 1 | | | [Owner missing] | - | 13 | 2 | 8 | 0 | -| | [Owner missing] | - | 99 | 0 | 96 | 0 | +| | [Owner missing] | - | 100 | 0 | 97 | 0 | | | [Owner missing] | Security Solution auto complete | 56 | 1 | 41 | 1 | | | [Owner missing] | - | 341 | 1 | 337 | 32 | | | [Owner missing] | security solution elastic search utilities to use across plugins such lists, security_solution, cases, etc... | 67 | 0 | 61 | 1 | @@ -482,7 +485,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 2 | 0 | 2 | 1 | | | [Owner missing] | - | 32 | 0 | 31 | 0 | | | [Owner missing] | - | 14 | 0 | 5 | 1 | -| | [Owner missing] | - | 12 | 0 | 12 | 0 | +| | [Owner missing] | - | 17 | 0 | 17 | 0 | | | [Owner missing] | - | 8 | 0 | 3 | 0 | | | [Owner missing] | - | 25 | 0 | 24 | 0 | | | [Owner missing] | - | 11 | 0 | 6 | 0 | @@ -500,7 +503,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Owner missing] | - | 2 | 0 | 0 | 0 | | | [Owner missing] | - | 15 | 0 | 4 | 0 | | | [Owner missing] | - | 9 | 0 | 3 | 0 | -| | [Owner missing] | SLO io-ts schema definition and common models shared between public and server. | 73 | 0 | 73 | 0 | +| | [Owner missing] | SLO io-ts schema definition and common models shared between public and server. | 75 | 0 | 75 | 0 | | | [Owner missing] | - | 20 | 0 | 12 | 0 | | | Kibana Core | - | 97 | 1 | 64 | 1 | | | [Owner missing] | - | 4 | 0 | 2 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index ad4d121202873..9d16b8a845090 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 148ce4163f187..19b3f879685c7 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index f86b5ea1ad68b..ecf6c10ae12cc 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index 10a64ef76481e..02ae49c2688d7 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 15e34aff07d77..a862b950c7bcc 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.devdocs.json b/api_docs/rule_registry.devdocs.json index 34a50ca6688ef..f59c56fd76922 100644 --- a/api_docs/rule_registry.devdocs.json +++ b/api_docs/rule_registry.devdocs.json @@ -2475,7 +2475,7 @@ "signature": [ "(request: TSearchRequest) => Promise<", + ", TAlertDoc = Partial> & OutputOf>>>(request: TSearchRequest) => Promise<", { "pluginId": "@kbn/es-types", "scope": "common", @@ -2483,7 +2483,7 @@ "section": "def-common.ESSearchResponse", "text": "ESSearchResponse" }, - "> & OutputOf>>, TSearchRequest, { restTotalHitsAsInt: false; }>>" + ">" ], "path": "x-pack/plugins/rule_registry/server/rule_data_client/types.ts", "deprecated": false, @@ -3112,7 +3112,7 @@ "label": "getAlertByAlertUuid", "description": [], "signature": [ - "(alertUuid: string) => { [x: string]: any; } | null" + "(alertUuid: string) => Promise<{ [id: string]: any; }[] | null>" ], "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", "deprecated": false, @@ -3295,6 +3295,86 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "ruleRegistry", + "id": "def-server.PersistenceServices.alertWithSuppression", + "type": "Function", + "tags": [], + "label": "alertWithSuppression", + "description": [], + "signature": [ + "(alerts: { _id: string; _source: T; }[], suppressionWindow: string, enrichAlerts?: ((alerts: { _id: string; _source: T; }[], params: { spaceId: string; }) => Promise<{ _id: string; _source: T; }[]>) | undefined, currentTimeOverride?: Date | undefined) => Promise, \"alertsWereTruncated\">>" + ], + "path": "x-pack/plugins/rule_registry/server/utils/persistence_types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "ruleRegistry", + "id": "def-server.PersistenceServices.alertWithSuppression.$1", + "type": "Array", + "tags": [], + "label": "alerts", + "description": [], + "signature": [ + "{ _id: string; _source: T; }[]" + ], + "path": "x-pack/plugins/rule_registry/server/utils/persistence_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "ruleRegistry", + "id": "def-server.PersistenceServices.alertWithSuppression.$2", + "type": "string", + "tags": [], + "label": "suppressionWindow", + "description": [], + "path": "x-pack/plugins/rule_registry/server/utils/persistence_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "ruleRegistry", + "id": "def-server.PersistenceServices.alertWithSuppression.$3", + "type": "Function", + "tags": [], + "label": "enrichAlerts", + "description": [], + "signature": [ + "((alerts: { _id: string; _source: T; }[], params: { spaceId: string; }) => Promise<{ _id: string; _source: T; }[]>) | undefined" + ], + "path": "x-pack/plugins/rule_registry/server/utils/persistence_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "ruleRegistry", + "id": "def-server.PersistenceServices.alertWithSuppression.$4", + "type": "Object", + "tags": [], + "label": "currentTimeOverride", + "description": [], + "signature": [ + "Date | undefined" + ], + "path": "x-pack/plugins/rule_registry/server/utils/persistence_types.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ], "initialIsOpen": false @@ -3973,6 +4053,87 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "ruleRegistry", + "id": "def-server.SuppressedAlertService", + "type": "Type", + "tags": [], + "label": "SuppressedAlertService", + "description": [], + "signature": [ + "(alerts: { _id: string; _source: T; }[], suppressionWindow: string, enrichAlerts?: ((alerts: { _id: string; _source: T; }[], params: { spaceId: string; }) => Promise<{ _id: string; _source: T; }[]>) | undefined, currentTimeOverride?: Date | undefined) => Promise, \"alertsWereTruncated\">>" + ], + "path": "x-pack/plugins/rule_registry/server/utils/persistence_types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "ruleRegistry", + "id": "def-server.SuppressedAlertService.$1", + "type": "Array", + "tags": [], + "label": "alerts", + "description": [], + "signature": [ + "{ _id: string; _source: T; }[]" + ], + "path": "x-pack/plugins/rule_registry/server/utils/persistence_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "ruleRegistry", + "id": "def-server.SuppressedAlertService.$2", + "type": "string", + "tags": [], + "label": "suppressionWindow", + "description": [], + "path": "x-pack/plugins/rule_registry/server/utils/persistence_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "ruleRegistry", + "id": "def-server.SuppressedAlertService.$3", + "type": "Function", + "tags": [], + "label": "enrichAlerts", + "description": [], + "signature": [ + "((alerts: { _id: string; _source: T; }[], params: { spaceId: string; }) => Promise<{ _id: string; _source: T; }[]>) | undefined" + ], + "path": "x-pack/plugins/rule_registry/server/utils/persistence_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "ruleRegistry", + "id": "def-server.SuppressedAlertService.$4", + "type": "Object", + "tags": [], + "label": "currentTimeOverride", + "description": [], + "signature": [ + "Date | undefined" + ], + "path": "x-pack/plugins/rule_registry/server/utils/persistence_types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "ruleRegistry", "id": "def-server.Version", diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 2bec837f88bb2..d8acccaefd520 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; @@ -21,7 +21,7 @@ Contact [RAC](https://github.com/orgs/elastic/teams/rac) for questions regarding | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 241 | 0 | 213 | 10 | +| 251 | 0 | 223 | 11 | ## Server diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 795ac6a0fb15d..7f8ba0eca103c 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 7123a0630fdc9..b653d951059d9 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index a85d08d1dd6cb..3b4f7e08db7f3 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index bcf415146653e..7403994b1c81f 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 7e69c9aa7f330..3875315e6508c 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index fc23fab122614..6d16aefa4a78f 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.devdocs.json b/api_docs/saved_search.devdocs.json index ba9542747b9ab..44ff3e2ef982f 100644 --- a/api_docs/saved_search.devdocs.json +++ b/api_docs/saved_search.devdocs.json @@ -394,7 +394,7 @@ "section": "def-common.SearchSourceFields", "text": "SearchSourceFields" }, - "[K]; getActiveIndexFilter: () => string[]; getOwnField: any[]; getOwnField: (params: Record, replace?: boolean | undefined) => void; getFlyoutComponent: () => React.NamedExoticComponent<", + "{ canWriteBlocklist: boolean; exceptionListApiClient: unknown; useSetUrlParams: () => (params: Record, replace?: boolean | undefined) => void; getFlyoutComponent: () => React.NamedExoticComponent<", "BlockListFlyoutProps", ">; getFormComponent: () => React.NamedExoticComponent<", "BlockListFormProps", diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index fdefb06e38d46..e86ae35f8506e 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Protections Experience Team](https://github.com/orgs/elastic/teams/prot | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 35 | 0 | 15 | 5 | +| 35 | 0 | 14 | 5 | ## Client diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index efdbc8646e154..139941e426aca 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index d47d5255bcf4f..a869cb9acf0b2 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index 8beabd8957f66..2692b9d183efb 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -3546,6 +3546,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.AlertsTableProps.showAlertStatusWithFlapping", + "type": "CompoundType", + "tags": [], + "label": "showAlertStatusWithFlapping", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.AlertsTableProps.trailingControlColumns", @@ -8707,6 +8721,22 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TriggersAndActionsUIPublicPluginStart.getRulesSettingsLink", + "type": "Function", + "tags": [], + "label": "getRulesSettingsLink", + "description": [], + "signature": [ + "() => React.ReactElement>" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "lifecycle": "start", diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 0eb4b68b7548e..311bcb693ec3f 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 587 | 11 | 558 | 53 | +| 589 | 11 | 560 | 53 | ## Client diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 495102dc60bea..47e64697da7e4 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 53e3048ef591a..d965eb5ced992 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index 2a5c72c19a055..7f7cf259143f0 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index a23bcfb92f3d4..78d6c39a64f3e 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 03f7841649b46..3619bb67bc237 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 31747fe10f66a..e7a8c05972a36 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 4b0a87e5f55a3..727e129a4968e 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 22d3654c6a69d..9629ff388d96f 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 22d7e86e365a2..5d590d242f97f 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index f5d28f485cf74..e324433932df1 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index 0b3d5343a149e..69d71a491e556 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index 672d59a578bc6..95897099a6762 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 827923c9236e3..98381d286bc43 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index eef6fd0a968e6..fb77b045a1cfe 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 2052315ac60fc..3bcee400921c9 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 9a668ca887a6f..41480c69677c7 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 09a9a548e978c..97978aaa2942c 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 6495677661504..9a0930c3b76b4 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index 20790046ae6e9..78a7b2e97e105 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 2d7bc6c01527c..44996f2eb442d 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-01-30 +date: 2023-02-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/docs/api-generated/README.md b/docs/api-generated/README.md index 97a94e512e5c6..97fd32119b8bc 100644 --- a/docs/api-generated/README.md +++ b/docs/api-generated/README.md @@ -25,7 +25,7 @@ or a similar tool that can generate HTML output from OAS. . Rename the output files. For example: ``` - mv $GIT_HOME/kibana/docs/api-generated/rules/index.html $GIT_HOME/kibana/docs/api-generated/rules/rule-apis-passthru.asciidoc + mv $GIT_HOME/kibana/docs/api-generated/rules/index.html $GIT_HOME/kibana/docs/api-generated/rules/rule-apis-passthru.asciidoc mv $GIT_HOME/kibana/docs/api-generated/cases/index.html $GIT_HOME/kibana/docs/api-generated/cases/case-apis-passthru.asciidoc mv $GIT_HOME/kibana/docs/api-generated/connectors/index.html $GIT_HOME/kibana/docs/api-generated/connectors/connector-apis-passthru.asciidoc mv $GIT_HOME/kibana/docs/api-generated/machine-learning/index.html $GIT_HOME/kibana/docs/api-generated/machine-learning/ml-apis-passthru.asciidoc diff --git a/docs/api-generated/cases/case-apis-passthru.asciidoc b/docs/api-generated/cases/case-apis-passthru.asciidoc index 5801fe57ec075..d6d5b0d589ac1 100644 --- a/docs/api-generated/cases/case-apis-passthru.asciidoc +++ b/docs/api-generated/cases/case-apis-passthru.asciidoc @@ -78,7 +78,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -198,7 +198,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -306,7 +306,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -362,7 +362,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -410,7 +410,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -482,7 +482,6 @@ Any modifications made to this file will be overwritten.
{
   "userActions" : [ {
     "owner" : "cases",
-    "case_id" : "22df07d0-03b1-11ed-920c-974bfa104448",
     "action" : "create",
     "created_at" : "2022-05-13T09:16:17.416Z",
     "id" : "22fd3e30-03b1-11ed-920c-974bfa104448",
@@ -497,7 +496,6 @@ Any modifications made to this file will be overwritten.
     "version" : "WzM1ODg4LDFd"
   }, {
     "owner" : "cases",
-    "case_id" : "22df07d0-03b1-11ed-920c-974bfa104448",
     "action" : "create",
     "created_at" : "2022-05-13T09:16:17.416Z",
     "id" : "22fd3e30-03b1-11ed-920c-974bfa104448",
@@ -1519,7 +1517,7 @@ Any modifications made to this file will be overwritten.
     
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -1639,7 +1637,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -1740,7 +1738,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -1862,7 +1860,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -1984,7 +1982,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -3001,7 +2999,6 @@ Any modifications made to this file will be overwritten.
action
-
case_id
comment_id
created_at
Date format: date-time
created_by
@@ -3009,7 +3006,9 @@ Any modifications made to this file will be overwritten.
owner
payload
version
-
type
+
type
String The type of action.
+
Enum:
+
assignees
create_case
comment
connector
description
pushed
tags
title
status
settings
severity
diff --git a/docs/management/action-types.asciidoc b/docs/management/action-types.asciidoc index 9a48f3c9b9ae4..288c0d1fe0c66 100644 --- a/docs/management/action-types.asciidoc +++ b/docs/management/action-types.asciidoc @@ -74,6 +74,10 @@ a| <> a| <> | Send actionable alerts to on-call xMatters resources. + +a| <> + +| Trigger a Torq workflow. |=== [NOTE] @@ -141,6 +145,9 @@ image::images/connector-select-type.png[Connector select type] After you create a connector, it is available for use any time you set up an action in the current space. +For out-of-the-box and standardized connectors, refer to +<>. + [float] [[importing-and-exporting-connectors]] === Importing and exporting connectors @@ -157,13 +164,6 @@ button appears in *{connectors-ui}*. [role="screenshot"] image::images/connectors-with-missing-secrets.png[Connectors with missing secrets] -[float] -[[create-connectors]] -=== Preconfigured connectors - -For out-of-the-box and standardized connectors, you can <> -before {kib} starts. - [float] [[montoring-connectors]] === Monitoring connectors diff --git a/docs/management/connectors/action-types/server-log.asciidoc b/docs/management/connectors/action-types/server-log.asciidoc index 7d9171ca99ed8..dca6eee379b52 100644 --- a/docs/management/connectors/action-types/server-log.asciidoc +++ b/docs/management/connectors/action-types/server-log.asciidoc @@ -1,50 +1,64 @@ -[role="xpack"] [[server-log-action-type]] -=== Server log connector and action +== Server log connector and action ++++ Server log ++++ -This connector writes an entry to the {kib} server log. +A server log connector writes an entry to the {kib} server log. -[float] -[[server-log-connector-configuration]] -==== Connector configuration - -Server log connectors have the following configuration properties. - -Name:: The name of the connector. +You can create a server log connector in {kib} or by using the +<>. If you are running {kib} +on-prem, you can also create a preconfigured server log connector. [float] -[[Preconfigured-server-log-configuration]] -==== Preconfigured connector type +[[server-log-connector-configuration]] +=== Connector configuration -[source,text] --- - my-server-log: - name: preconfigured-server-log-connector-type - actionTypeId: .server-log --- +Server log connectors do not have any configuration properties other than a name. [float] [[define-serverlog-ui]] -==== Define connector in {stack-manage-app} +=== Create a connector in {kib} -Define Server log connector properties. +You can create a server log connector in *{stack-manage-app} > {connectors-ui}* +or as needed when you're creating a rule. For example: [role="screenshot"] image::management/connectors/images/serverlog-connector.png[Server log connector] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. -Test Server log action parameters. +[float] +[[preconfigured-server-log-configuration]] +=== Create a preconfigured connector -[role="screenshot"] -image::management/connectors/images/serverlog-params-test.png[Server log params test] +If you are running {kib} on-prem, you can define a server log connector by +adding `xpack.actions.preconfigured` settings to your `kibana.yml` file. +For example: + +[source,text] +-- +xpack.actions.preconfigured: + my-server-log: + name: preconfigured-server-log-connector-type + actionTypeId: .server-log +-- + +For more information, go to <>. [float] [[server-log-action-configuration]] -==== Action configuration +=== Test the connector -Server log actions have the following properties. +You can test your server log connector with the +<> or as you're creating or editing +the connector in {kib}. For example: + +[role="screenshot"] +image::management/connectors/images/serverlog-params-test.png[Server log connector test] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. + +Server log actions have the following properties: Message:: The message to log. Level:: The log level of the message: `trace`, `debug`, `info`, `warn`, `error` or `fatal`. Defaults to `info`. + diff --git a/docs/management/connectors/action-types/torq.asciidoc b/docs/management/connectors/action-types/torq.asciidoc new file mode 100644 index 0000000000000..70ac19be25ec3 --- /dev/null +++ b/docs/management/connectors/action-types/torq.asciidoc @@ -0,0 +1,65 @@ +[role="xpack"] +[[torq-action-type]] +=== Torq connector and action +++++ +Torq +++++ + +The Torq connector uses a Torq webhook to trigger workflows with Kibana actions. + +[float] +[[torq-connector-configuration]] +==== Connector configuration +Torq connectors have the following configuration properties. + +Name:: The name of the connector. The name is used to identify a connector in the Stack Management UI connector listing, and in the connector list when configuring an action. + +Torq endpoint URL:: Endpoint URL (webhook) of the Elastic Security integration you created in Torq. + +Torq authentication header secret:: Secret of the webhook authentication header. + +[float] +[[Preconfigured-torq-configuration]] +==== Preconfigured connector type + +[source,yaml] +-- + my-torq: + name: preconfigured-torq-connector-type + actionTypeId: .torq + config: + webhookIntegrationUrl: https://hooks.torq.io/v1/somehook + secrets: + token: mytorqtoken +-- + +Config defines information for the connector type. + +`webhookIntegrationUrl`:: An address that corresponds to **Torq endpoint URL**. + +Secrets defines sensitive information for the connector type. + +`token`:: A string that corresponds to **Torq authentication header secret**. + +[float] +[[define-torq-ui]] +==== Define connector in Stack Management + +Define Torq connector properties. + +[role="screenshot"] +image::management/connectors/images/torq-configured-connector.png[configured Torq connector] + +Test Torq action parameters. + +[role="screenshot"] +image::management/connectors/images/torq-connector-test.png[Torq connector test] + +[float] +[[torq-action-configuration]] +==== Action configuration + + Torq actions have the following configuration properties. + + Body:: JSON payload to send to Torq. + diff --git a/docs/management/connectors/images/pre-configured-connectors-view-screen.png b/docs/management/connectors/images/pre-configured-connectors-view-screen.png deleted file mode 100644 index b2d00b307000e..0000000000000 Binary files a/docs/management/connectors/images/pre-configured-connectors-view-screen.png and /dev/null differ diff --git a/docs/management/connectors/images/pre-configured-connectors-managing.png b/docs/management/connectors/images/preconfigured-connectors-managing.png similarity index 100% rename from docs/management/connectors/images/pre-configured-connectors-managing.png rename to docs/management/connectors/images/preconfigured-connectors-managing.png diff --git a/docs/management/connectors/images/serverlog-connector.png b/docs/management/connectors/images/serverlog-connector.png index 983bb6afadd65..cc0b8745b2d6e 100644 Binary files a/docs/management/connectors/images/serverlog-connector.png and b/docs/management/connectors/images/serverlog-connector.png differ diff --git a/docs/management/connectors/images/serverlog-params-test.png b/docs/management/connectors/images/serverlog-params-test.png index 762721c7ead45..789381949bd43 100644 Binary files a/docs/management/connectors/images/serverlog-params-test.png and b/docs/management/connectors/images/serverlog-params-test.png differ diff --git a/docs/management/connectors/images/torq-configured-connector.png b/docs/management/connectors/images/torq-configured-connector.png new file mode 100644 index 0000000000000..1732276fa4f23 Binary files /dev/null and b/docs/management/connectors/images/torq-configured-connector.png differ diff --git a/docs/management/connectors/images/torq-connector-test.png b/docs/management/connectors/images/torq-connector-test.png new file mode 100644 index 0000000000000..13f416a1486c6 Binary files /dev/null and b/docs/management/connectors/images/torq-connector-test.png differ diff --git a/docs/management/connectors/index.asciidoc b/docs/management/connectors/index.asciidoc index b443ffd967a6f..7a3f8f9cb927c 100644 --- a/docs/management/connectors/index.asciidoc +++ b/docs/management/connectors/index.asciidoc @@ -5,14 +5,15 @@ include::action-types/jira.asciidoc[] include::action-types/teams.asciidoc[] include::action-types/opsgenie.asciidoc[] include::action-types/pagerduty.asciidoc[] -include::action-types/server-log.asciidoc[] +include::action-types/server-log.asciidoc[leveloffset=+1] include::action-types/servicenow.asciidoc[leveloffset=+1] include::action-types/servicenow-sir.asciidoc[leveloffset=+1] include::action-types/servicenow-itom.asciidoc[leveloffset=+1] include::action-types/swimlane.asciidoc[] include::action-types/slack.asciidoc[] include::action-types/tines.asciidoc[leveloffset=+1] +include::action-types/torq.asciidoc[] include::action-types/webhook.asciidoc[] include::action-types/cases-webhook.asciidoc[leveloffset=+1] include::action-types/xmatters.asciidoc[] -include::pre-configured-connectors.asciidoc[] +include::pre-configured-connectors.asciidoc[leveloffset=+1] diff --git a/docs/management/connectors/pre-configured-connectors.asciidoc b/docs/management/connectors/pre-configured-connectors.asciidoc index ad580d87e712b..43643f0f611ba 100644 --- a/docs/management/connectors/pre-configured-connectors.asciidoc +++ b/docs/management/connectors/pre-configured-connectors.asciidoc @@ -1,24 +1,28 @@ -[role="xpack"] [[pre-configured-connectors]] -=== Preconfigured connectors +== Preconfigured connectors -You can preconfigure a connector to have all the information it needs prior to -startup by adding it to the `kibana.yml` file. +If you are running {kib} on-prem, you can preconfigure a connector to have all +the information it needs prior to startup by adding it to the `kibana.yml` file. + +NOTE: {ess} provides a preconfigured email connector but you cannot create +additional preconfigured connectors. Preconfigured connectors offer the following benefits: -- Require no setup. Configuration and credentials needed to execute an -action are predefined, including the connector name and ID. +- Require no setup. Configuration and credentials needed to run an action are +predefined, including the connector name and ID. - Appear in all spaces because they are not saved objects. - Cannot be edited or deleted. [float] -[[preconfigured-connector-example]] -==== Preconfigured connectors example +[[create-preconfigured-connectors]] +=== Create preconfigured connectors + +Add `xpack.actions.preconfigured` settings to your `kibana.yml` file. The +settings vary depending on which type of connector you're adding. -This example shows a valid configuration for -two out-of-the box connectors: <> and -<>. +This example shows a valid configuration for a Slack connector and a Webhook +connector: ```js xpack.actions.preconfigured: @@ -50,31 +54,29 @@ two out-of-the box connectors: <> and [NOTE] ============================================== Sensitive properties, such as passwords, can also be stored in the -<>. +<>. ============================================== [float] [[build-in-preconfigured-connectors]] -==== Built-in preconfigured connectors +=== Built-in preconfigured connectors {kib} provides the following built-in preconfigured connectors: -* <> -* <> +* <> +* <> [float] [[managing-pre-configured-connectors]] -==== View preconfigured connectors +=== View preconfigured connectors When you open the main menu, click *{stack-manage-app} > {connectors-ui}*. Preconfigured connectors appear regardless of which space you are in. They are tagged as “preconfigured”, and you cannot delete them. [role="screenshot"] -image::images/pre-configured-connectors-managing.png[Connectors managing tab with pre-configured] +image::images/preconfigured-connectors-managing.png[Connectors managing tab with pre-configured] Clicking a preconfigured connector shows the description, but not the -configuration. A message indicates that this is a preconfigured connector. +configuration. -[role="screenshot"] -image::images/pre-configured-connectors-view-screen.png[Pre-configured connector view details] diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index 0f7763379e560..a2ac56be64ebf 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -131,7 +131,7 @@ A list of allowed email domains which can be used with the email connector. When WARNING: This feature is available in {kib} 7.17.4 and 8.3.0 onwards but is not supported in {kib} 8.0, 8.1 or 8.2. As such, this setting should be removed before upgrading from 7.17 to 8.0, 8.1 or 8.2. It is possible to configure the settings in 7.17.4 and then upgrade to 8.3.0 directly. `xpack.actions.enabledActionTypes` {ess-icon}:: -A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.email`, `.index`, `.jira`, `.opsgenie`, `.pagerduty`, `.resilient`, `.server-log`, `.servicenow`, .`servicenow-itom`, `.servicenow-sir`, `.slack`, `.swimlane`, `.teams`, `.tines`, `.xmatters`, and `.webhook`. An empty list `[]` will disable all action types. +A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.email`, `.index`, `.jira`, `.opsgenie`, `.pagerduty`, `.resilient`, `.server-log`, `.servicenow`, .`servicenow-itom`, `.servicenow-sir`, `.slack`, `.swimlane`, `.teams`, `.tines`, `.torq`, `.xmatters`, and `.webhook`. An empty list `[]` will disable all action types. + Disabled action types will not appear as an option when creating new connectors, but existing connectors and actions of that type will remain in {kib} and will not function. diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index cb3d84af79150..e4fd9111b216a 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -380,6 +380,10 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | User has accessed a case comment. | `failure` | User is not authorized to access a case comment. +.2+| `case_comment_bulk_get` +| `success` | User has accessed multiple case comments. +| `failure` | User is not authorized to access multiple case comments. + .2+| `case_comment_get_all` | `success` | User has accessed case comments. | `failure` | User is not authorized to access case comments. @@ -404,7 +408,6 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | User has accessed the user activity of a case. | `failure` | User is not authorized to access the user activity of a case. - .2+| `case_user_actions_find` | `success` | User has accessed the user activity of a case as part of a search operation. | `failure` | User is not authorized to access the user activity of a case. diff --git a/fleet_packages.json b/fleet_packages.json index 63195b19e2209..fcb628f52610f 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -25,7 +25,7 @@ }, { "name": "elastic_agent", - "version": "1.4.1" + "version": "1.5.0" }, { "name": "endpoint", diff --git a/package.json b/package.json index 884f16533f16b..cccd5253cc381 100644 --- a/package.json +++ b/package.json @@ -421,8 +421,8 @@ "@opentelemetry/semantic-conventions": "^1.4.0", "@reduxjs/toolkit": "1.7.2", "@slack/webhook": "^5.0.4", - "@tanstack/react-query": "^4.20.9", - "@tanstack/react-query-devtools": "^4.20.9", + "@tanstack/react-query": "^4.23.0", + "@tanstack/react-query-devtools": "^4.23.0", "@turf/along": "6.0.1", "@turf/area": "6.0.1", "@turf/bbox": "6.0.1", @@ -583,7 +583,7 @@ "react-fast-compare": "^2.0.4", "react-focus-on": "^3.7.0", "react-grid-layout": "^1.3.4", - "react-hook-form": "^7.41.3", + "react-hook-form": "^7.42.1", "react-intl": "^2.8.0", "react-is": "^17.0.2", "react-markdown": "^6.0.3", diff --git a/packages/core/application/core-application-browser-internal/src/ui/app_container.tsx b/packages/core/application/core-application-browser-internal/src/ui/app_container.tsx index c7fafa498bef4..11899a938da9f 100644 --- a/packages/core/application/core-application-browser-internal/src/ui/app_container.tsx +++ b/packages/core/application/core-application-browser-internal/src/ui/app_container.tsx @@ -126,7 +126,15 @@ export const AppContainer: FC = ({ const AppLoadingPlaceholder: FC<{ showPlainSpinner: boolean }> = ({ showPlainSpinner }) => { if (showPlainSpinner) { - return ; + return ( + + ); } return ( server.publicBaseUrl, }} />{' '} - + 0 1`] = ` /> `; -exports[`kbnLoadingIndicator shows EuiLoadingSpinner when showPlainSpinner is true 1`] = ` - `; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_logo.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_logo.tsx index 70e761838fe42..8b98e52f61c42 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_logo.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_logo.tsx @@ -102,10 +102,7 @@ export function HeaderLogo({ href, navigateToApp, loadingCount$, ...observables defaultMessage: 'Elastic home', })} > - + {customizedLogo ? ( custom mark ) : ( diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.test.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.test.tsx index 7bc609bc03c30..6c168fe305b52 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.test.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.test.tsx @@ -28,9 +28,9 @@ describe('kbnLoadingIndicator', () => { expect(wrapper).toMatchSnapshot(); }); - it('shows EuiLoadingSpinner when showPlainSpinner is true', () => { + it('shows logo image when customLogo is set', () => { const wrapper = shallow( - + ); // Pause the check beyond the 250ms delay that it has setTimeout(() => { diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.tsx index efac13e1cf372..13084f9500e58 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { EuiLoadingSpinner, EuiProgress, EuiIcon } from '@elastic/eui'; +import { EuiLoadingSpinner, EuiProgress, EuiIcon, EuiImage } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import classNames from 'classnames'; @@ -18,7 +18,7 @@ import './loading_indicator.scss'; export interface LoadingIndicatorProps { loadingCount$: ReturnType; showAsBar?: boolean; - showPlainSpinner?: boolean; + customLogo?: string; } export class LoadingIndicator extends React.Component { @@ -58,10 +58,9 @@ export class LoadingIndicator extends React.Component - ) : ( - - ); + const logoImage = this.props.customLogo ? ( + + ) : ( + + ); + + const logo = this.state.visible ? ( + + ) : ( + logoImage + ); return !this.props.showAsBar ? ( logo diff --git a/packages/core/custom-branding/core-custom-branding-server-internal/custom_branding_service.test.ts b/packages/core/custom-branding/core-custom-branding-server-internal/custom_branding_service.test.ts index 3f0f60e4187d4..7f26966533f98 100644 --- a/packages/core/custom-branding/core-custom-branding-server-internal/custom_branding_service.test.ts +++ b/packages/core/custom-branding/core-custom-branding-server-internal/custom_branding_service.test.ts @@ -61,4 +61,16 @@ describe('#setup', () => { expect(fetchFn).toHaveBeenCalledTimes(1); expect(customBranding).toEqual({ logo: 'myLogo' }); }); + + it('calls fetchFn correctly when unauthenticated', async () => { + const service = new CustomBrandingService(coreContext); + const { register, getBrandingFor } = service.setup(); + service.start(); + const fetchFn = jest.fn(); + fetchFn.mockImplementation(() => Promise.resolve({ logo: 'myLogo' })); + register('customBranding', fetchFn); + const kibanaRequest: jest.Mocked = {} as unknown as jest.Mocked; + await getBrandingFor(kibanaRequest, { unauthenticated: true }); + expect(fetchFn).toHaveBeenCalledWith(kibanaRequest, true); + }); }); diff --git a/packages/core/custom-branding/core-custom-branding-server-internal/custom_branding_service.ts b/packages/core/custom-branding/core-custom-branding-server-internal/custom_branding_service.ts index d0d157fc90b77..fa871c3e3bc26 100644 --- a/packages/core/custom-branding/core-custom-branding-server-internal/custom_branding_service.ts +++ b/packages/core/custom-branding/core-custom-branding-server-internal/custom_branding_service.ts @@ -10,13 +10,15 @@ import type { CustomBranding } from '@kbn/core-custom-branding-common'; import type { KibanaRequest } from '@kbn/core-http-server'; import type { CoreContext } from '@kbn/core-base-server-internal'; import type { Logger } from '@kbn/logging'; - /** * @internal */ export interface InternalCustomBrandingSetup { register: (pluginName: string, fetchFn: CustomBrandingFetchFn) => void; - getBrandingFor: (request: KibanaRequest) => Promise; + getBrandingFor: ( + request: KibanaRequest, + options?: { unauthenticated?: boolean } + ) => Promise; } export class CustomBrandingService { @@ -55,13 +57,16 @@ export class CustomBrandingService { public stop() {} - private getBrandingFor = async (request: KibanaRequest): Promise => { + private getBrandingFor = async ( + request: KibanaRequest, + options?: { unauthenticated?: boolean } + ): Promise => { if (!this.startCalled) { throw new Error('Cannot be called before #start'); } if (!this.pluginName || this.pluginName !== 'customBranding' || !this.fetchFn) { return {}; } - return this.fetchFn!(request); + return this.fetchFn!(request, Boolean(options?.unauthenticated)); }; } diff --git a/packages/core/custom-branding/core-custom-branding-server/types.ts b/packages/core/custom-branding/core-custom-branding-server/types.ts index f62a1343f0bac..b5f1aa2639807 100644 --- a/packages/core/custom-branding/core-custom-branding-server/types.ts +++ b/packages/core/custom-branding/core-custom-branding-server/types.ts @@ -13,9 +13,16 @@ import type { MaybePromise } from '@kbn/utility-types'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface CustomBrandingStart {} -export type CustomBrandingFetchFn = (request: KibanaRequest) => MaybePromise; +export type CustomBrandingFetchFn = ( + request: KibanaRequest, + unauthenticated: boolean +) => MaybePromise; /** @public */ export interface CustomBrandingSetup { register: (fetchFn: CustomBrandingFetchFn) => void; + getBrandingFor: ( + request: KibanaRequest, + options: { unauthenticated?: boolean } + ) => Promise; } diff --git a/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts b/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts index 3ed12781022f9..944ec94554b25 100644 --- a/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts +++ b/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts @@ -71,5 +71,5 @@ export interface InjectedMetadata { user: Record; // unreferencing UserProvidedValues here }; }; - customBranding: Pick; + customBranding: Pick; } diff --git a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts index 7e33cc1dd36fe..62d2f2de6383e 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts @@ -198,6 +198,7 @@ export function createPluginSetupContext( register: (fetchFn) => { deps.customBranding.register(plugin.name, fetchFn); }, + getBrandingFor: deps.customBranding.getBrandingFor, }, docLinks: deps.docLinks, elasticsearch: { diff --git a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx index 102a89a07b42b..3a3ff0790a998 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx +++ b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx @@ -113,15 +113,18 @@ export class RenderingService { let branding: CustomBranding = {}; try { // Only provide the clusterInfo if the request is authenticated and the elasticsearch service is available. - if (isAuthenticated(http.auth, request) && elasticsearch) { + const authenticated = isAuthenticated(http.auth, request); + if (authenticated && elasticsearch) { clusterInfo = await firstValueFrom( elasticsearch.clusterInfo$.pipe( timeout(50), // If not available, just return undefined catchError(() => of({})) ) ); - branding = await customBranding?.getBrandingFor(request); } + branding = await customBranding?.getBrandingFor(request, { + unauthenticated: !authenticated, + })!; } catch (err) { // swallow error } @@ -151,6 +154,7 @@ export class RenderingService { faviconSVG: branding?.faviconSVG, faviconPNG: branding?.faviconPNG, pageTitle: branding?.pageTitle, + logo: branding?.logo, }, injectedMetadata: { version: env.packageInfo.version, diff --git a/packages/core/rendering/core-rendering-server-internal/src/views/template.tsx b/packages/core/rendering/core-rendering-server-internal/src/views/template.tsx index 1910616da22c2..bbc8109e403da 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/views/template.tsx +++ b/packages/core/rendering/core-rendering-server-internal/src/views/template.tsx @@ -34,6 +34,11 @@ export const Template: FunctionComponent = ({ const title = customBranding.pageTitle ?? 'Elastic'; const favIcon = customBranding.faviconSVG ?? `${uiPublicUrl}/favicons/favicon.svg`; const favIconPng = customBranding.faviconPNG ?? `${uiPublicUrl}/favicons/favicon.png`; + const logo = customBranding.logo ? ( + logo + ) : ( + + ); return ( @@ -67,7 +72,7 @@ export const Template: FunctionComponent = ({ data-test-subj="kbnLoadingMessage" >
- + {logo}
= ({ 'Elastic did not load properly. Check the server output for more information.', })} > - {i18n('core.ui.welcomeMessage', { defaultMessage: 'Loading Elastic' })} + {i18n('core.ui.welcomeMessage', { + defaultMessage: 'Loading Elastic', + })}
- + {logo}

{i18n('core.ui.legacyBrowserTitle', { diff --git a/packages/kbn-apm-synthtrace/src/scenarios/other_transaction_group_bucket.ts b/packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts similarity index 93% rename from packages/kbn-apm-synthtrace/src/scenarios/other_transaction_group_bucket.ts rename to packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts index 9289617c0119a..8ad994e4ff848 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/other_transaction_group_bucket.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts @@ -24,9 +24,12 @@ const scenario: Scenario = async ({ logger, scenarioOpts }) => { const BUCKET_SIZE = (MAX_DURATION - MIN_DURATION) / MAX_BUCKETS; - const instances = lodashRange(0, numServices).flatMap((serviceId) => { - const serviceName = `service-${serviceId}`; + const serviceRange = [ + ...lodashRange(0, numServices).map((groupId) => `service-${groupId}`), + '_other', + ]; + const instances = serviceRange.flatMap((serviceName) => { const services = ENVIRONMENTS.map((env) => apm.service(serviceName, env, 'go')); return lodashRange(0, 2).flatMap((serviceNodeId) => diff --git a/packages/kbn-apm-synthtrace/src/scenarios/other_service_group_bucket.ts b/packages/kbn-apm-synthtrace/src/scenarios/other_service_group_bucket.ts deleted file mode 100644 index fbe264d581182..0000000000000 --- a/packages/kbn-apm-synthtrace/src/scenarios/other_service_group_bucket.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -import { range as lodashRange } from 'lodash'; -import { ApmFields, apm } from '@kbn/apm-synthtrace-client'; -import { Scenario } from '../cli/scenario'; - -const scenario: Scenario = async (runOptions) => { - const { logger } = runOptions; - const numServices = 10; - - return { - generate: ({ range }) => { - const TRANSACTION_TYPES = ['request']; - const ENVIRONMENTS = ['production', 'development']; - - const MIN_DURATION = 10; - const MAX_DURATION = 1000; - - const MAX_BUCKETS = 50; - - const BUCKET_SIZE = (MAX_DURATION - MIN_DURATION) / MAX_BUCKETS; - - const serviceRange = [ - ...lodashRange(0, numServices).map((groupId) => `service-${groupId}`), - '_other', - ]; - - const instances = serviceRange.flatMap((serviceName) => { - const services = ENVIRONMENTS.map((env) => apm.service(serviceName, env, 'go')); - - return lodashRange(0, 2).flatMap((serviceNodeId) => - services.map((service) => service.instance(`${serviceName}-${serviceNodeId}`)) - ); - }); - - const transactionGroupRange = [ - ...lodashRange(0, 10).map((groupId) => `transaction-${groupId}`), - '_other', - ]; - - return range.ratePerMinute(60).generator((timestamp, timestampIndex) => { - return logger.perf( - 'generate_events_for_timestamp ' + new Date(timestamp).toISOString(), - () => { - const events = instances.flatMap((instance) => - transactionGroupRange.flatMap((groupId, groupIndex) => { - const duration = Math.round( - (timestampIndex % MAX_BUCKETS) * BUCKET_SIZE + MIN_DURATION - ); - - return instance - .transaction(groupId, TRANSACTION_TYPES[groupIndex % TRANSACTION_TYPES.length]) - .timestamp(timestamp) - .duration(duration) - .outcome('success' as const); - }) - ); - - return events; - } - ); - }); - }, - }; -}; - -export default scenario; diff --git a/packages/kbn-es-query/src/filters/build_filters/get_filter_params.ts b/packages/kbn-es-query/src/filters/build_filters/get_filter_params.ts index 46585acd9efc0..1b3a0c780b95f 100644 --- a/packages/kbn-es-query/src/filters/build_filters/get_filter_params.ts +++ b/packages/kbn-es-query/src/filters/build_filters/get_filter_params.ts @@ -6,25 +6,26 @@ * Side Public License, v 1. */ -import type { PhrasesFilter } from './phrases_filter'; -import type { PhraseFilter } from './phrase_filter'; -import type { RangeFilter } from './range_filter'; -import { Filter, FILTERS } from './types'; +import { isPhrasesFilter, PhrasesFilter } from './phrases_filter'; +import { isPhraseFilter } from './phrase_filter'; +import { isRangeFilter } from './range_filter'; +import { Filter } from './types'; /** * @internal used only by the filter bar to create filter pills. */ -export function getFilterParams(filter: Filter) { - switch (filter.meta.type) { - case FILTERS.PHRASE: - return (filter as PhraseFilter).meta.params.query; - case FILTERS.PHRASES: - return (filter as PhrasesFilter).meta.params; - case FILTERS.RANGE: - const { gte, gt, lte, lt } = (filter as RangeFilter).meta.params; - return { - from: gte ?? gt, - to: lt ?? lte, - }; +export function getFilterParams(filter: Filter): Filter['meta']['params'] { + if (isPhraseFilter(filter)) { + return filter.meta.params?.query; + } else if (isPhrasesFilter(filter)) { + return (filter as PhrasesFilter).meta.params; + } else if (isRangeFilter(filter) && filter.meta.params) { + const { gte, gt, lte, lt } = filter.meta.params; + return { + from: gte ?? gt, + to: lt ?? lte, + }; + } else { + return filter.meta.params; } } diff --git a/packages/kbn-es-query/src/filters/build_filters/match_all_filter.ts b/packages/kbn-es-query/src/filters/build_filters/match_all_filter.ts index 5e8083c1d1415..41dd8860ab63f 100644 --- a/packages/kbn-es-query/src/filters/build_filters/match_all_filter.ts +++ b/packages/kbn-es-query/src/filters/build_filters/match_all_filter.ts @@ -7,10 +7,11 @@ */ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { SerializableRecord } from '@kbn/utility-types'; import { has } from 'lodash'; import type { Filter, FilterMeta } from './types'; -export interface MatchAllFilterMeta extends FilterMeta { +export interface MatchAllFilterMeta extends FilterMeta, SerializableRecord { field: string; formattedValue: string; } diff --git a/packages/kbn-es-query/src/filters/build_filters/phrase_filter.ts b/packages/kbn-es-query/src/filters/build_filters/phrase_filter.ts index 4adc8fdc0fdd4..0b990bd9cdfa7 100644 --- a/packages/kbn-es-query/src/filters/build_filters/phrase_filter.ts +++ b/packages/kbn-es-query/src/filters/build_filters/phrase_filter.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { SerializableRecord } from '@kbn/utility-types'; import { get, has, isPlainObject } from 'lodash'; import type { Filter, FilterMeta } from './types'; import type { DataViewFieldBase, DataViewBase } from '../../es_query'; @@ -14,10 +15,12 @@ import { hasRangeKeys } from './range_filter'; export type PhraseFilterValue = string | number | boolean; +export interface PhraseFilterMetaParams extends SerializableRecord { + query: PhraseFilterValue; // The unformatted value +} + export type PhraseFilterMeta = FilterMeta & { - params?: { - query: PhraseFilterValue; // The unformatted value - }; + params?: PhraseFilterMetaParams; field?: string; index?: string; }; diff --git a/packages/kbn-es-query/src/filters/build_filters/range_filter.ts b/packages/kbn-es-query/src/filters/build_filters/range_filter.ts index e9bafade964b7..b1b0fdb0590ab 100644 --- a/packages/kbn-es-query/src/filters/build_filters/range_filter.ts +++ b/packages/kbn-es-query/src/filters/build_filters/range_filter.ts @@ -7,7 +7,9 @@ */ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { map, reduce, mapValues, has, get, keys, pickBy } from 'lodash'; -import type { Filter, FilterMeta } from './types'; +import type { SerializableRecord } from '@kbn/utility-types'; +import type { Filter, FilterMeta, FilterMetaParams } from './types'; +import { FILTERS } from './types'; import type { DataViewBase, DataViewFieldBase } from '../../es_query'; const OPERANDS_IN_RANGE = 2; @@ -37,7 +39,7 @@ const dateComparators = { * It is similar, but not identical to estypes.QueryDslRangeQuery * @public */ -export interface RangeFilterParams { +export interface RangeFilterParams extends SerializableRecord { from?: number | string; to?: number | string; gt?: number | string; @@ -53,9 +55,10 @@ export const hasRangeKeys = (params: RangeFilterParams) => ); export type RangeFilterMeta = FilterMeta & { - params: RangeFilterParams; + params?: RangeFilterParams; field?: string; formattedValue?: string; + type: 'range'; }; export type ScriptedRangeFilter = Filter & { @@ -90,7 +93,16 @@ export type RangeFilter = Filter & { * * @public */ -export const isRangeFilter = (filter?: Filter): filter is RangeFilter => has(filter, 'query.range'); +export function isRangeFilter(filter?: Filter): filter is RangeFilter { + if (filter?.meta?.type) return filter.meta.type === FILTERS.RANGE; + return has(filter, 'query.range'); +} + +export function isRangeFilterParams( + params: FilterMetaParams | undefined +): params is RangeFilterParams { + return typeof params === 'object' && get(params, 'type', '') === 'range'; +} /** * @@ -140,7 +152,9 @@ export const buildRangeFilter = ( const totalInfinite = ['gt', 'lt'].reduce((acc, op) => { const key = op in params ? op : `${op}e`; - const isInfinite = Math.abs(get(params, key)) === Infinity; + const value = get(params, key); + const numericValue = typeof value === 'number' ? value : 0; + const isInfinite = Math.abs(numericValue) === Infinity; if (isInfinite) { acc++; @@ -152,7 +166,7 @@ export const buildRangeFilter = ( return acc; }, 0); - const meta: RangeFilterMeta = { + const meta = { index: indexPattern?.id, params: {}, field: field.name, diff --git a/packages/kbn-es-query/src/filters/build_filters/types.ts b/packages/kbn-es-query/src/filters/build_filters/types.ts index 27140c9a72e99..da3404f8621c8 100644 --- a/packages/kbn-es-query/src/filters/build_filters/types.ts +++ b/packages/kbn-es-query/src/filters/build_filters/types.ts @@ -7,10 +7,10 @@ */ import { ExistsFilter } from './exists_filter'; -import { PhrasesFilter } from './phrases_filter'; -import { PhraseFilter } from './phrase_filter'; -import { RangeFilter } from './range_filter'; -import { MatchAllFilter } from './match_all_filter'; +import { PhrasesFilter, PhrasesFilterMeta } from './phrases_filter'; +import { PhraseFilter, PhraseFilterMeta, PhraseFilterMetaParams } from './phrase_filter'; +import { RangeFilter, RangeFilterMeta, RangeFilterParams } from './range_filter'; +import { MatchAllFilter, MatchAllFilterMeta } from './match_all_filter'; /** * A common type for filters supported by this package @@ -50,6 +50,22 @@ export enum FilterStateStore { GLOBAL_STATE = 'globalState', } +export type FilterMetaParams = + | Filter + | Filter[] + | RangeFilterMeta + | RangeFilterParams + | PhraseFilterMeta + | PhraseFilterMetaParams + | PhrasesFilterMeta + | MatchAllFilterMeta + | string + | string[] + | boolean + | boolean[] + | number + | number[]; + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type FilterMeta = { alias?: string | null; @@ -64,7 +80,7 @@ export type FilterMeta = { isMultiIndex?: boolean; type?: string; key?: string; - params?: any; + params?: FilterMetaParams; value?: string; }; diff --git a/packages/kbn-es-query/src/filters/helpers/update_filter.ts b/packages/kbn-es-query/src/filters/helpers/update_filter.ts index ea61545d557be..29241ca1d1b80 100644 --- a/packages/kbn-es-query/src/filters/helpers/update_filter.ts +++ b/packages/kbn-es-query/src/filters/helpers/update_filter.ts @@ -6,16 +6,14 @@ * Side Public License, v 1. */ -import { identity, pickBy } from 'lodash'; - -import type { Filter, FilterMeta, RangeFilterParams } from '..'; - -type FilterOperator = Pick; +import { get } from 'lodash'; +import { isRangeFilterParams } from '../build_filters/range_filter'; +import type { Filter, FilterMeta } from '..'; export const updateFilter = ( filter: Filter, field?: string, - operator?: FilterOperator, + operator?: FilterMeta, params?: Filter['meta']['params'], fieldType?: string ) => { @@ -52,7 +50,7 @@ function updateField(filter: Filter, field?: string) { }; } -function updateWithExistsOperator(filter: Filter, operator?: FilterOperator) { +function updateWithExistsOperator(filter: Filter, operator?: FilterMeta) { return { ...filter, meta: { @@ -68,76 +66,114 @@ function updateWithExistsOperator(filter: Filter, operator?: FilterOperator) { function updateWithIsOperator( filter: Filter, - operator?: FilterOperator, + operator?: FilterMeta, params?: Filter['meta']['params'], fieldType?: string ) { const safeParams = fieldType === 'number' && !params ? 0 : params; - return { - ...filter, - meta: { - ...filter.meta, - negate: operator?.negate, - type: operator?.type, - params: { ...filter.meta.params, query: params }, - value: undefined, - }, - query: { match_phrase: { [filter.meta.key!]: safeParams ?? '' } }, - }; + if (typeof filter.meta.params === 'object') { + return { + ...filter, + meta: { + ...filter.meta, + negate: operator?.negate, + type: operator?.type, + params: { ...filter.meta.params, query: params }, + value: undefined, + }, + query: { match_phrase: { [filter.meta.key!]: safeParams ?? '' } }, + }; + } else { + return { + ...filter, + meta: { + ...filter.meta, + negate: operator?.negate, + type: operator?.type, + params: { query: params }, + value: undefined, + }, + query: { match_phrase: { [filter.meta.key!]: safeParams ?? '' } }, + }; + } } function updateWithRangeOperator( filter: Filter, - operator: FilterOperator, - rawParams: RangeFilterParams, + operator: FilterMeta, + rawParams: Filter['meta']['params'] | undefined, field: string ) { - const rangeParams = { - ...pickBy(rawParams, identity), - }; - - const params = { - gte: rangeParams?.from, - lt: rangeParams?.to, - }; - - const updatedFilter = { - ...filter, - meta: { - ...filter.meta, - negate: operator?.negate, - type: operator?.type, - params, - }, - query: { - range: { - [field]: params, + if (isRangeFilterParams(rawParams)) { + const { from, to } = rawParams; + const params = { + gte: from, + lt: to, + }; + const updatedFilter = { + ...filter, + meta: { + ...filter.meta, + negate: operator?.negate, + type: operator?.type, + params, }, - }, - }; + query: { + range: { + [field]: params, + }, + }, + }; - return updatedFilter; + return updatedFilter; + } else { + const from = get(rawParams, 'from', undefined); + const to = get(rawParams, 'to', undefined); + const params = { + gte: from, + lt: to, + }; + const updatedFilter = { + ...filter, + meta: { + ...filter.meta, + negate: operator?.negate, + type: operator?.type, + params, + }, + query: { + range: { + [field]: params, + }, + }, + }; + return updatedFilter; + } } function updateWithIsOneOfOperator( filter: Filter, - operator?: FilterOperator, - params?: Array + operator?: FilterMeta, + params?: Filter['meta']['params'] ) { - return { - ...filter, - meta: { - ...filter.meta, - negate: operator?.negate, - type: operator?.type, - params, - }, - query: { - bool: { - minimum_should_match: 1, - ...filter!.query?.should, - should: params?.map((param) => ({ match_phrase: { [filter.meta.key!]: param } })), + if (Array.isArray(params)) { + return { + ...filter, + meta: { + ...filter.meta, + negate: operator?.negate, + type: operator?.type, + params, }, - }, - }; + query: { + bool: { + minimum_should_match: 1, + ...filter!.query?.should, + should: params?.map((param) => ({ match_phrase: { [filter.meta.key!]: param } })), + }, + }, + }; + } else { + return filter; + } } diff --git a/packages/kbn-es-query/src/filters/stubs/phrase_filter.ts b/packages/kbn-es-query/src/filters/stubs/phrase_filter.ts index 6b818f632af32..482be741ec741 100644 --- a/packages/kbn-es-query/src/filters/stubs/phrase_filter.ts +++ b/packages/kbn-es-query/src/filters/stubs/phrase_filter.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import { PhraseFilter, FilterStateStore } from '..'; +import { FilterStateStore } from '..'; -export const phraseFilter: PhraseFilter = { +export const phraseFilter = { meta: { negate: false, index: 'logstash-*', @@ -24,5 +24,7 @@ export const phraseFilter: PhraseFilter = { $state: { store: FilterStateStore.APP_STATE, }, - query: {}, + query: { + match_phrase: {}, + }, }; diff --git a/packages/kbn-es-query/src/filters/stubs/range_filter.ts b/packages/kbn-es-query/src/filters/stubs/range_filter.ts index e2058f8c07359..9eaa7db7e0563 100644 --- a/packages/kbn-es-query/src/filters/stubs/range_filter.ts +++ b/packages/kbn-es-query/src/filters/stubs/range_filter.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import { RangeFilter, FilterStateStore } from '..'; +import { FilterStateStore } from '..'; -export const rangeFilter: RangeFilter = { +export const rangeFilter = { meta: { index: 'logstash-*', negate: false, diff --git a/packages/kbn-guided-onboarding/index.ts b/packages/kbn-guided-onboarding/index.ts index 00f2447a1f0ab..9ccaae3901b48 100644 --- a/packages/kbn-guided-onboarding/index.ts +++ b/packages/kbn-guided-onboarding/index.ts @@ -17,6 +17,6 @@ export type { StepConfig, StepDescriptionWithLink, } from './src/types'; -export { GuideCard, InfrastructureLinkCard } from './src/components/landing_page'; -export type { GuideCardUseCase } from './src/components/landing_page'; +export { GuideCards, GuideFilters } from './src/components/landing_page'; +export type { GuideFilterValues } from './src/components/landing_page'; export { testGuideId, testGuideConfig } from './src/common/test_guide_config'; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/guide_card.test.tsx.snap b/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/guide_card.test.tsx.snap deleted file mode 100644 index b286cba768f03..0000000000000 --- a/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/guide_card.test.tsx.snap +++ /dev/null @@ -1,55 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`guide card snapshots should render use case card component for kubernetes 1`] = ` - - } - isDarkTheme={false} - title="Observe my Kubernetes infrastructure" - useCase="kubernetes" -/> -`; - -exports[`guide card snapshots should render use case card component for search 1`] = ` - - } - isDarkTheme={false} - title="Search my data" - useCase="search" -/> -`; - -exports[`guide card snapshots should render use case card component for siem 1`] = ` - - } - isDarkTheme={false} - title="Protect my environment" - useCase="siem" -/> -`; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/guide_card_footer.test.tsx.snap b/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/guide_card_footer.test.tsx.snap deleted file mode 100644 index a5fe07d1a872c..0000000000000 --- a/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/guide_card_footer.test.tsx.snap +++ /dev/null @@ -1,181 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`guide card footer snapshots should render the footer when the guide has been completed 1`] = ` - - - - - - - View guide - - - - -`; - -exports[`guide card footer snapshots should render the footer when the guide has not started yet 1`] = ` - - - - View guide - - - -`; - -exports[`guide card footer snapshots should render the footer when the guide is in progress 1`] = ` - - - - - - - Continue - - - - -`; - -exports[`guide card footer snapshots should render the footer when the guide is ready to complete 1`] = ` - - - - - - - Continue - - - - -`; - -exports[`guide card footer snapshots should render the footer when the guided onboarding has not started yet 1`] = ` - - - - View guide - - - -`; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/guide_cards.test.tsx.snap b/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/guide_cards.test.tsx.snap new file mode 100644 index 0000000000000..796cd57b3396e --- /dev/null +++ b/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/guide_cards.test.tsx.snap @@ -0,0 +1,255 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`guide cards snapshots should render all cards 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/infrastructure_link_card.test.tsx.snap b/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/infrastructure_link_card.test.tsx.snap deleted file mode 100644 index e79ebcdeca5be..0000000000000 --- a/packages/kbn-guided-onboarding/src/components/landing_page/__snapshots__/infrastructure_link_card.test.tsx.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`observability link card snapshots should render link card for observability 1`] = ` - - - - View integrations - - - - } - isDarkTheme={false} - title="Observe my data" - useCase="infrastructure" -/> -`; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/guide_card.test.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/guide_card.test.tsx deleted file mode 100644 index c078c5ce8f170..0000000000000 --- a/packages/kbn-guided-onboarding/src/components/landing_page/guide_card.test.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { shallow } from 'enzyme'; - -import { GuideCard, GuideCardProps } from './guide_card'; - -const defaultProps: GuideCardProps = { - useCase: 'search', - guides: [], - activateGuide: jest.fn(), - isDarkTheme: false, - addBasePath: jest.fn(), -}; - -describe('guide card', () => { - describe('snapshots', () => { - test('should render use case card component for search', async () => { - const component = await shallow(); - - expect(component).toMatchSnapshot(); - }); - test('should render use case card component for kubernetes', async () => { - const component = await shallow(); - - expect(component).toMatchSnapshot(); - }); - test('should render use case card component for siem', async () => { - const component = await shallow(); - - expect(component).toMatchSnapshot(); - }); - }); -}); diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/guide_card.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/guide_card.tsx index 1fed74c7ec346..a6f1ea67bb50b 100644 --- a/packages/kbn-guided-onboarding/src/components/landing_page/guide_card.tsx +++ b/packages/kbn-guided-onboarding/src/components/landing_page/guide_card.tsx @@ -6,108 +6,115 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { useCallback, useState } from 'react'; +import { css } from '@emotion/react'; + +import { EuiCard, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer, EuiTextColor } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { GuideState } from '../../types'; -import { GuideCardFooter } from './guide_card_footer'; -import { UseCaseCard } from './use_case_card'; +import { GuideCardConstants } from './guide_cards.constants'; +import { GuideCardsProps } from './guide_cards'; -// separate type for GuideCardUseCase that includes some of GuideIds -export type GuideCardUseCase = 'search' | 'kubernetes' | 'siem'; -type GuideCardConstants = { - [key in GuideCardUseCase]: { - i18nTexts: { - title: string; - description: string; - }; - // duplicate the telemetry id from the guide config to not load the config from the endpoint - // this might change if we decide to use the guide config for the cards - // see this issue https://github.com/elastic/kibana/issues/146672 - telemetryId: string; - }; -}; +const cardCss = css` + position: relative; + min-height: 110px; + width: 380px; + .euiCard__content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } +`; -const constants: GuideCardConstants = { - search: { - i18nTexts: { - title: i18n.translate('guidedOnboardingPackage.gettingStarted.guideCard.search.cardTitle', { - defaultMessage: 'Search my data', - }), - description: i18n.translate( - 'guidedOnboardingPackage.gettingStarted.guideCard.search.cardDescription', - { - defaultMessage: - 'Create a search experience for your websites, applications, workplace content, or anything in between.', - } - ), - }, - telemetryId: 'search', - }, - kubernetes: { - i18nTexts: { - title: i18n.translate( - 'guidedOnboardingPackage.gettingStarted.guideCard.kubernetes.cardTitle', - { - defaultMessage: 'Observe my Kubernetes infrastructure', - } - ), - description: i18n.translate( - 'guidedOnboardingPackage.gettingStarted.guideCard.kubernetes.cardDescription', - { - defaultMessage: - 'Monitor your Kubernetes infrastructure by consolidating your logs and metrics.', - } - ), - }, - telemetryId: 'kubernetes', - }, - siem: { - i18nTexts: { - title: i18n.translate('guidedOnboardingPackage.gettingStarted.guideCard.siem.cardTitle', { - defaultMessage: 'Protect my environment', - }), - description: i18n.translate( - 'guidedOnboardingPackage.gettingStarted.guideCard.siem.cardDescription', - { - defaultMessage: - 'Investigate threats and get your SIEM up and running by installing the Elastic Defend integration.', - } - ), +const getProgressLabel = (guideState: GuideState | undefined): string | undefined => { + if (!guideState) { + return undefined; + } + const { steps } = guideState; + const numberSteps = steps.length; + const numberCompleteSteps = steps.filter((step) => step.status === 'complete').length; + if (numberCompleteSteps < 1 || numberCompleteSteps === numberSteps) { + return undefined; + } + return i18n.translate('guidedOnboardingPackage.gettingStarted.cards.progressLabel', { + defaultMessage: '{numberCompleteSteps} of {numberSteps} steps complete', + values: { + numberCompleteSteps, + numberSteps, }, - telemetryId: 'siem', - }, + }); }; -export interface GuideCardProps { - useCase: GuideCardUseCase; - guides: GuideState[]; - activateGuide: (useCase: GuideCardUseCase, guide?: GuideState) => Promise; - isDarkTheme: boolean; - addBasePath: (url: string) => string; -} export const GuideCard = ({ - useCase, - guides, + card, + guidesState, activateGuide, - isDarkTheme, - addBasePath, -}: GuideCardProps) => { + navigateToApp, + activeFilter, +}: GuideCardsProps & { card: GuideCardConstants }) => { + const [isLoading, setIsLoading] = useState(false); + let guideState: GuideState | undefined; + if (card.guideId) { + guideState = guidesState.find((state) => state.guideId === card.guideId); + } + + const onClick = useCallback(async () => { + setIsLoading(true); + if (card.guideId) { + await activateGuide(card.guideId, guideState); + } else if (card.navigateTo) { + await navigateToApp(card.navigateTo?.appId, { + path: card.navigateTo.path, + }); + } + setIsLoading(false); + }, [activateGuide, card.guideId, card.navigateTo, guideState, navigateToApp]); + + const isHighlighted = activeFilter === 'all' || activeFilter === card.solution; + const isComplete = guideState && guideState.status === 'complete'; + const progress = getProgressLabel(guideState); return ( - + + +

{card.title}

+ + } + titleSize="xs" + betaBadgeProps={{ + label: card.solution, + }} + description={ + <> + {progress && ( + + {progress} + + )} + {isComplete && ( + + + + + + + {i18n.translate('guidedOnboardingPackage.gettingStarted.cards.completeLabel', { + defaultMessage: 'Guide complete', + })} + + + + )} + } - isDarkTheme={isDarkTheme} - addBasePath={addBasePath} /> ); }; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/guide_card_footer.test.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/guide_card_footer.test.tsx deleted file mode 100644 index d70316c7604f7..0000000000000 --- a/packages/kbn-guided-onboarding/src/components/landing_page/guide_card_footer.test.tsx +++ /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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { shallow } from 'enzyme'; - -import { GuideCardFooter, GuideCardFooterProps } from './guide_card_footer'; -import { GuideState } from '../../types'; - -const defaultProps: GuideCardFooterProps = { - guides: [], - useCase: 'search', - telemetryId: 'search', - activateGuide: jest.fn(), -}; - -const searchGuideState: GuideState = { - guideId: 'search', - status: 'not_started', - steps: [ - { id: 'add_data', status: 'complete' }, - { id: 'search_experience', status: 'in_progress' }, - ], - isActive: true, -}; -describe('guide card footer', () => { - describe('snapshots', () => { - test('should render the footer when the guided onboarding has not started yet', async () => { - const component = await shallow(); - expect(component).toMatchSnapshot(); - }); - - test('should render the footer when the guide has not started yet', async () => { - const component = await shallow( - - ); - expect(component).toMatchSnapshot(); - }); - - test('should render the footer when the guide is in progress', async () => { - const component = await shallow( - - ); - expect(component).toMatchSnapshot(); - }); - - test('should render the footer when the guide is ready to complete', async () => { - const component = await shallow( - - ); - expect(component).toMatchSnapshot(); - }); - - test('should render the footer when the guide has been completed', async () => { - const component = await shallow( - - ); - expect(component).toMatchSnapshot(); - }); - }); -}); diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/guide_card_footer.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/guide_card_footer.tsx deleted file mode 100644 index 9646870782ff2..0000000000000 --- a/packages/kbn-guided-onboarding/src/components/landing_page/guide_card_footer.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { useState, useCallback } from 'react'; -import { css } from '@emotion/react'; -import { EuiButton, EuiProgress, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { GuideId, GuideState } from '../../types'; -import type { GuideCardUseCase } from './guide_card'; - -const viewGuideLabel = i18n.translate( - 'guidedOnboardingPackage.gettingStarted.guideCard.startGuide.buttonLabel', - { - defaultMessage: 'View guide', - } -); - -const continueGuideLabel = i18n.translate( - 'guidedOnboardingPackage.gettingStarted.guideCard.continueGuide.buttonLabel', - { - defaultMessage: 'Continue', - } -); - -const completedLabel = i18n.translate( - 'guidedOnboardingPackage.gettingStarted.guideCard.progress.completedLabel', - { - defaultMessage: 'Completed', - } -); - -const inProgressLabel = i18n.translate( - 'guidedOnboardingPackage.gettingStarted.guideCard.progress.inProgressLabel', - { - defaultMessage: 'In progress', - } -); - -// The progress bar is rendered within EuiCard, which centers content by default -const progressBarLabelCss = css` - text-align: 'left'; -`; - -export interface GuideCardFooterProps { - guides: GuideState[]; - useCase: GuideCardUseCase; - telemetryId: string; - activateGuide: (useCase: GuideCardUseCase, guideState?: GuideState) => Promise; -} -export const GuideCardFooter = ({ - guides, - useCase, - telemetryId, - activateGuide, -}: GuideCardFooterProps) => { - const guideState = guides.find((guide) => guide.guideId === (useCase as GuideId)); - const [isLoading, setIsLoading] = useState(false); - const activateGuideCallback = useCallback(async () => { - setIsLoading(true); - await activateGuide(useCase, guideState); - setIsLoading(false); - }, [activateGuide, guideState, useCase]); - const viewGuideButton = ( - - - - {viewGuideLabel} - - - - ); - // guide has not started yet - if (!guideState || guideState.status === 'not_started') { - return viewGuideButton; - } - const { status, steps } = guideState; - const numberSteps = steps.length; - const numberCompleteSteps = steps.filter((step) => step.status === 'complete').length; - const stepsLabel = i18n.translate('guidedOnboardingPackage.gettingStarted.guideCard.stepsLabel', { - defaultMessage: '{progress} steps', - values: { - progress: `${numberCompleteSteps}/${numberSteps}`, - }, - }); - // guide is completed - if (status === 'complete') { - return ( - <> - - - {viewGuideButton} - - ); - } - // guide is in progress or ready to complete - return ( - <> - - - - - - {continueGuideLabel} - - - - - ); -}; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.constants.ts b/packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.constants.ts new file mode 100644 index 0000000000000..889a17b660e24 --- /dev/null +++ b/packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.constants.ts @@ -0,0 +1,139 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { GuideId } from '../../..'; +import { GuideCardSolutions } from './guide_cards'; + +export interface GuideCardConstants { + solution: GuideCardSolutions; + title: string; + // if present, guideId indicates which guide is opened when clicking the card + guideId?: GuideId; + // if present, navigateTo indicates where the user will be redirected, when clicking the card + navigateTo?: { + appId: string; + path?: string; + }; + // duplicate the telemetry id from the guide config to not load the config from the endpoint + // this might change if we decide to use the guide config for the cards + // see this issue https://github.com/elastic/kibana/issues/146672 + telemetryId: string; + order: number; +} + +export const guideCards: GuideCardConstants[] = [ + { + solution: 'search', + title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.appSearch.title', { + defaultMessage: 'Build an application on top of Elasticsearch', + }), + guideId: 'search', + telemetryId: 'guided-onboarding--search--application', + order: 1, + }, + { + solution: 'search', + title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.websiteSearch.title', { + defaultMessage: 'Add search to my website', + }), + guideId: 'search', + telemetryId: 'guided-onboarding--search--website', + order: 4, + }, + { + solution: 'search', + title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.databaseSearch.title', { + defaultMessage: 'Search across databases and business systems', + }), + guideId: 'search', + telemetryId: 'guided-onboarding--search--database', + order: 7, + }, + { + solution: 'observability', + title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.logsObservability.title', { + defaultMessage: 'Collect and analyze my logs', + }), + navigateTo: { + appId: 'integrations', + path: '/browse?q=log', + }, + telemetryId: 'guided-onboarding--observability--logs', + order: 2, + }, + { + solution: 'observability', + title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.apmObservability.title', { + defaultMessage: 'Monitor my application performance (APM / tracing)', + }), + navigateTo: { + appId: 'home', + path: '#/tutorial/apm', + }, + telemetryId: 'guided-onboarding--observability--apm', + order: 5, + }, + { + solution: 'observability', + title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.hostsObservability.title', { + defaultMessage: 'Monitor my host metrics', + }), + navigateTo: { + appId: 'integrations', + path: '/browse/os_system', + }, + telemetryId: 'guided-onboarding--observability--hosts', + order: 8, + }, + { + solution: 'observability', + title: i18n.translate( + 'guidedOnboardingPackage.gettingStarted.cards.kubernetesObservability.title', + { + defaultMessage: 'Monitor Kubernetes clusters', + } + ), + guideId: 'kubernetes', + telemetryId: 'guided-onboarding--observability--kubernetes', + order: 11, + }, + { + solution: 'security', + title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.siemSecurity.title', { + defaultMessage: 'Detect threats in my data with SIEM', + }), + guideId: 'siem', + telemetryId: 'guided-onboarding--security--siem', + order: 3, + }, + { + solution: 'security', + title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.hostsSecurity.title', { + defaultMessage: 'Secure my hosts with endpoint security', + }), + navigateTo: { + appId: 'integrations', + path: '/detail/endpoint/overview', + }, + telemetryId: 'guided-onboarding--security--hosts', + order: 6, + }, + { + solution: 'security', + title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.cloudSecurity.title', { + defaultMessage: 'Secure my cloud assets with posture management', + }), + navigateTo: { + appId: 'integrations', + path: '/detail/cloud_security_posture/overview', + }, + telemetryId: 'guided-onboarding--security--cloud', + order: 9, + }, +].sort((cardA, cardB) => cardA.order - cardB.order) as GuideCardConstants[]; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.test.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.test.tsx similarity index 61% rename from packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.test.tsx rename to packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.test.tsx index d1ac3c20c6c6d..de14e79a3b10a 100644 --- a/packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.test.tsx +++ b/packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.test.tsx @@ -8,18 +8,20 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { InfrastructureLinkCard } from './infrastructure_link_card'; -const defaultProps = { +import { GuideCards, GuideCardsProps } from './guide_cards'; + +const defaultProps: GuideCardsProps = { + activateGuide: jest.fn(), navigateToApp: jest.fn(), - isDarkTheme: false, - addBasePath: jest.fn(), + activeFilter: 'all', + guidesState: [], }; -describe('observability link card', () => { +describe('guide cards', () => { describe('snapshots', () => { - test('should render link card for observability', async () => { - const component = await shallow(); + test('should render all cards', async () => { + const component = await shallow(); expect(component).toMatchSnapshot(); }); }); diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.tsx new file mode 100644 index 0000000000000..45d32d8089ef3 --- /dev/null +++ b/packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.tsx @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; + +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +import { ApplicationStart } from '@kbn/core-application-browser'; + +import { GuideId, GuideState } from '../../types'; +import { GuideFilterValues } from './guide_filters'; +import { guideCards } from './guide_cards.constants'; +import { GuideCard } from './guide_card'; + +export type GuideCardSolutions = 'search' | 'observability' | 'security'; + +export interface GuideCardsProps { + activateGuide: (guideId: GuideId, guideState?: GuideState) => Promise; + navigateToApp: ApplicationStart['navigateToApp']; + activeFilter: GuideFilterValues; + guidesState: GuideState[]; +} +export const GuideCards = (props: GuideCardsProps) => { + return ( + + {guideCards.map((card, index) => ( + + + + + ))} + + ); +}; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/guide_filters.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/guide_filters.tsx new file mode 100644 index 0000000000000..7dd1641a0ee4d --- /dev/null +++ b/packages/kbn-guided-onboarding/src/components/landing_page/guide_filters.tsx @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { GuideCardSolutions } from './guide_cards'; + +const filterButtonCss = css` + border-radius: 20px !important; + min-width: 0 !important; + padding: 0 18px !important; + height: 32px !important; + &:hover { + text-decoration: none !important; + transform: none !important; + transition: none !important; + } + &:focus { + text-decoration: none; + } +`; +export type GuideFilterValues = GuideCardSolutions | 'all'; +interface GuideFiltersProps { + activeFilter: GuideFilterValues; + setActiveFilter: React.Dispatch>; +} +export const GuideFilters = ({ activeFilter, setActiveFilter }: GuideFiltersProps) => { + const { euiTheme } = useEuiTheme(); + const activeFilterFill = css` + background: ${euiTheme.colors.darkestShade}; + color: ${euiTheme.colors.lightestShade}; + `; + + return ( + + + setActiveFilter('all')} + color="text" + css={[filterButtonCss, activeFilter === 'all' && activeFilterFill]} + > + + + + + setActiveFilter('search')} + color="text" + css={[filterButtonCss, activeFilter === 'search' && activeFilterFill]} + > + + + + + setActiveFilter('observability')} + color="text" + css={[filterButtonCss, activeFilter === 'observability' && activeFilterFill]} + > + + + + + setActiveFilter('security')} + color="text" + css={[filterButtonCss, activeFilter === 'security' && activeFilterFill]} + > + + + + + ); +}; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/index.ts b/packages/kbn-guided-onboarding/src/components/landing_page/index.ts index db466a89e18dc..ee7933dd8fd4a 100644 --- a/packages/kbn-guided-onboarding/src/components/landing_page/index.ts +++ b/packages/kbn-guided-onboarding/src/components/landing_page/index.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -export { GuideCard } from './guide_card'; -export type { GuideCardUseCase } from './guide_card'; -export { InfrastructureLinkCard } from './infrastructure_link_card'; -export type { UseCase } from './use_case_card'; +export { GuideCards } from './guide_cards'; +export { GuideFilters } from './guide_filters'; +export type { GuideFilterValues } from './guide_filters'; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.tsx deleted file mode 100644 index 34dc3910ed125..0000000000000 --- a/packages/kbn-guided-onboarding/src/components/landing_page/infrastructure_link_card.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import type { NavigateToAppOptions } from '@kbn/core-application-browser'; -import { UseCaseCard } from './use_case_card'; - -interface LinkCardConstants { - infrastructure: { - i18nTexts: { - title: string; - description: string; - }; - }; -} - -const constants: LinkCardConstants = { - infrastructure: { - i18nTexts: { - title: i18n.translate( - 'guidedOnboardingPackage.gettingStarted.infrastructure.linkCard.cardTitle', - { - defaultMessage: 'Observe my data', - } - ), - description: i18n.translate( - 'guidedOnboardingPackage.gettingStarted.infrastructure.linkCard.cardDescription', - { - defaultMessage: - 'Add application, infrastructure, and user data through our pre-built integrations.', - } - ), - }, - }, -}; - -export const InfrastructureLinkCard = ({ - navigateToApp, - isDarkTheme, - addBasePath, -}: { - navigateToApp: (appId: string, options?: NavigateToAppOptions) => Promise; - isDarkTheme: boolean; - addBasePath: (url: string) => string; -}) => { - const navigateToIntegrations = () => { - navigateToApp('integrations', { - path: '/browse/infrastructure', - }); - }; - const button = ( - - - - {i18n.translate( - 'guidedOnboardingPackage.gettingStarted.infrastructure.linkCard.buttonLabel', - { - defaultMessage: 'View integrations', - } - )} - - - - ); - return ( - - ); -}; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/use_case_card.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/use_case_card.tsx deleted file mode 100644 index d726bf624d6fe..0000000000000 --- a/packages/kbn-guided-onboarding/src/components/landing_page/use_case_card.tsx +++ /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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { ReactNode } from 'react'; -import { EuiCard, EuiText, EuiImage } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import type { GuideCardUseCase } from './guide_card'; - -type UseCaseConstants = { - [key in UseCase]: { - logAltText: string; - betaBadgeLabel: string; - imageUrlPrefix: string; - }; -}; -const constants: UseCaseConstants = { - search: { - logAltText: i18n.translate('guidedOnboardingPackage.gettingStarted.search.iconName', { - defaultMessage: 'Enterprise Search logo', - }), - betaBadgeLabel: i18n.translate('guidedOnboardingPackage.gettingStarted.search.betaBadgeLabel', { - defaultMessage: 'search', - }), - imageUrlPrefix: '/plugins/home/assets/solution_logos/search', - }, - kubernetes: { - logAltText: i18n.translate('guidedOnboardingPackage.gettingStarted.kubernetes.iconName', { - defaultMessage: 'Observability logo', - }), - betaBadgeLabel: i18n.translate( - 'guidedOnboardingPackage.gettingStarted.kubernetes.betaBadgeLabel', - { - defaultMessage: 'observe', - } - ), - imageUrlPrefix: '/plugins/home/assets/solution_logos/kubernetes', - }, - infrastructure: { - logAltText: i18n.translate('guidedOnboardingPackage.gettingStarted.infrastructure.iconName', { - defaultMessage: 'Observability logo', - }), - betaBadgeLabel: i18n.translate( - 'guidedOnboardingPackage.gettingStarted.infrastructure.betaBadgeLabel', - { - defaultMessage: 'observe', - } - ), - imageUrlPrefix: '/plugins/home/assets/solution_logos/observability', - }, - siem: { - logAltText: i18n.translate('guidedOnboardingPackage.gettingStarted.siem.iconName', { - defaultMessage: 'Security logo', - }), - betaBadgeLabel: i18n.translate('guidedOnboardingPackage.gettingStarted.siem.betaBadgeLabel', { - defaultMessage: 'protect', - }), - imageUrlPrefix: '/plugins/home/assets/solution_logos/security', - }, -}; - -export type UseCase = GuideCardUseCase | 'infrastructure'; - -export interface UseCaseCardProps { - useCase: UseCase; - title: string; - description: string; - footer: ReactNode; - isDarkTheme: boolean; - addBasePath: (url: string) => string; -} - -export const UseCaseCard = ({ - useCase, - title, - description, - footer, - isDarkTheme, - addBasePath, -}: UseCaseCardProps) => { - const getImageUrl = (imageUrlPrefix: string) => { - const imagePath = `${imageUrlPrefix}${isDarkTheme ? '_dark' : ''}.png`; - return addBasePath(imagePath); - }; - - const titleElement = ( - -

- {title} -

-
- ); - - return ( - - } - title={titleElement} - description={description} - footer={footer} - paddingSize="l" - betaBadgeProps={{ - label: constants[useCase].betaBadgeLabel, - }} - /> - ); -}; diff --git a/packages/kbn-guided-onboarding/tsconfig.json b/packages/kbn-guided-onboarding/tsconfig.json index 5f0ba6f1c54f6..e1a8f2ab96b92 100644 --- a/packages/kbn-guided-onboarding/tsconfig.json +++ b/packages/kbn-guided-onboarding/tsconfig.json @@ -13,7 +13,8 @@ ], "kbn_references": [ "@kbn/i18n", - "@kbn/core-application-browser" + "@kbn/core-application-browser", + "@kbn/i18n-react" ], "exclude": [ "target/**/*", diff --git a/packages/kbn-handlebars/.gitignore b/packages/kbn-handlebars/.gitignore deleted file mode 100644 index d36977dc47615..0000000000000 --- a/packages/kbn-handlebars/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.tmp diff --git a/packages/kbn-handlebars/.patches/basic.patch b/packages/kbn-handlebars/.patches/basic.patch deleted file mode 100644 index e41c3b1cc9a85..0000000000000 --- a/packages/kbn-handlebars/.patches/basic.patch +++ /dev/null @@ -1,608 +0,0 @@ -1c1,6 -< global.handlebarsEnv = null; ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -3,5c8,9 -< beforeEach(function() { -< global.handlebarsEnv = Handlebars.create(); -< }); ---- -> import Handlebars from '../..'; -> import { expectTemplate } from '../__jest__/test_bench'; -7,11c11,13 -< describe('basic context', function() { -< it('most basic', function() { -< expectTemplate('{{foo}}') -< .withInput({ foo: 'foo' }) -< .toCompileTo('foo'); ---- -> describe('basic context', () => { -> it('most basic', () => { -> expectTemplate('{{foo}}').withInput({ foo: 'foo' }).toCompileTo('foo'); -14,33c16,21 -< it('escaping', function() { -< expectTemplate('\\{{foo}}') -< .withInput({ foo: 'food' }) -< .toCompileTo('{{foo}}'); -< -< expectTemplate('content \\{{foo}}') -< .withInput({ foo: 'food' }) -< .toCompileTo('content {{foo}}'); -< -< expectTemplate('\\\\{{foo}}') -< .withInput({ foo: 'food' }) -< .toCompileTo('\\food'); -< -< expectTemplate('content \\\\{{foo}}') -< .withInput({ foo: 'food' }) -< .toCompileTo('content \\food'); -< -< expectTemplate('\\\\ {{foo}}') -< .withInput({ foo: 'food' }) -< .toCompileTo('\\\\ food'); ---- -> it('escaping', () => { -> expectTemplate('\\{{foo}}').withInput({ foo: 'food' }).toCompileTo('{{foo}}'); -> expectTemplate('content \\{{foo}}').withInput({ foo: 'food' }).toCompileTo('content {{foo}}'); -> expectTemplate('\\\\{{foo}}').withInput({ foo: 'food' }).toCompileTo('\\food'); -> expectTemplate('content \\\\{{foo}}').withInput({ foo: 'food' }).toCompileTo('content \\food'); -> expectTemplate('\\\\ {{foo}}').withInput({ foo: 'food' }).toCompileTo('\\\\ food'); -36c24 -< it('compiling with a basic context', function() { ---- -> it('compiling with a basic context', () => { -40c28 -< world: 'world' ---- -> world: 'world', -42d29 -< .withMessage('It works if all the required keys are provided') -46,49c33,34 -< it('compiling with a string context', function() { -< expectTemplate('{{.}}{{length}}') -< .withInput('bye') -< .toCompileTo('bye3'); ---- -> it('compiling with a string context', () => { -> expectTemplate('{{.}}{{length}}').withInput('bye').toCompileTo('bye3'); -52c37 -< it('compiling with an undefined context', function() { ---- -> it('compiling with an undefined context', () => { -62c47 -< it('comments', function() { ---- -> it('comments', () => { -66c51 -< world: 'world' ---- -> world: 'world', -68d52 -< .withMessage('comments are ignored') -72,76c56 -< -< expectTemplate(' {{~!-- long-comment --~}} blah').toCompileTo( -< 'blah' -< ); -< ---- -> expectTemplate(' {{~!-- long-comment --~}} blah').toCompileTo('blah'); -78,82c58 -< -< expectTemplate(' {{!-- long-comment --~}} blah').toCompileTo( -< ' blah' -< ); -< ---- -> expectTemplate(' {{!-- long-comment --~}} blah').toCompileTo(' blah'); -84,87c60 -< -< expectTemplate(' {{~!-- long-comment --}} blah').toCompileTo( -< ' blah' -< ); ---- -> expectTemplate(' {{~!-- long-comment --}} blah').toCompileTo(' blah'); -90,91c63,64 -< it('boolean', function() { -< var string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!'; ---- -> it('boolean', () => { -> const string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!'; -95c68 -< world: 'world' ---- -> world: 'world', -97d69 -< .withMessage('booleans show the contents when true') -103c75 -< world: 'world' ---- -> world: 'world', -105d76 -< .withMessage('booleans do not show the contents when false') -109c80 -< it('zeros', function() { ---- -> it('zeros', () => { -113c84 -< num2: 0 ---- -> num2: 0, -117,119c88 -< expectTemplate('num: {{.}}') -< .withInput(0) -< .toCompileTo('num: 0'); ---- -> expectTemplate('num: {{.}}').withInput(0).toCompileTo('num: 0'); -126c95 -< it('false', function() { ---- -> it('false', () => { -131c100 -< val2: new Boolean(false) ---- -> val2: new Boolean(false), -135,137c104 -< expectTemplate('val: {{.}}') -< .withInput(false) -< .toCompileTo('val: false'); ---- -> expectTemplate('val: {{.}}').withInput(false).toCompileTo('val: false'); -146c113 -< val2: new Boolean(false) ---- -> val2: new Boolean(false), -156c123 -< it('should handle undefined and null', function() { ---- -> it('should handle undefined and null', () => { -159,167c126,128 -< awesome: function(_undefined, _null, options) { -< return ( -< (_undefined === undefined) + -< ' ' + -< (_null === null) + -< ' ' + -< typeof options -< ); -< } ---- -> awesome(_undefined: any, _null: any, options: any) { -> return (_undefined === undefined) + ' ' + (_null === null) + ' ' + typeof options; -> }, -173c134 -< undefined: function() { ---- -> undefined() { -175c136 -< } ---- -> }, -181c142 -< null: function() { ---- -> null() { -183c144 -< } ---- -> }, -188c149 -< it('newlines', function() { ---- -> it('newlines', () => { -190d150 -< -194,216c154,159 -< it('escaping text', function() { -< expectTemplate("Awesome's") -< .withMessage( -< "text is escaped so that it doesn't get caught on single quotes" -< ) -< .toCompileTo("Awesome's"); -< -< expectTemplate('Awesome\\') -< .withMessage("text is escaped so that the closing quote can't be ignored") -< .toCompileTo('Awesome\\'); -< -< expectTemplate('Awesome\\\\ foo') -< .withMessage("text is escaped so that it doesn't mess up backslashes") -< .toCompileTo('Awesome\\\\ foo'); -< -< expectTemplate('Awesome {{foo}}') -< .withInput({ foo: '\\' }) -< .withMessage("text is escaped so that it doesn't mess up backslashes") -< .toCompileTo('Awesome \\'); -< -< expectTemplate(" ' ' ") -< .withMessage('double quotes never produce invalid javascript') -< .toCompileTo(" ' ' "); ---- -> it('escaping text', () => { -> expectTemplate("Awesome's").toCompileTo("Awesome's"); -> expectTemplate('Awesome\\').toCompileTo('Awesome\\'); -> expectTemplate('Awesome\\\\ foo').toCompileTo('Awesome\\\\ foo'); -> expectTemplate('Awesome {{foo}}').withInput({ foo: '\\' }).toCompileTo('Awesome \\'); -> expectTemplate(" ' ' ").toCompileTo(" ' ' "); -219,223c162,163 -< it('escaping expressions', function() { -< expectTemplate('{{{awesome}}}') -< .withInput({ awesome: "&'\\<>" }) -< .withMessage("expressions with 3 handlebars aren't escaped") -< .toCompileTo("&'\\<>"); ---- -> it('escaping expressions', () => { -> expectTemplate('{{{awesome}}}').withInput({ awesome: "&'\\<>" }).toCompileTo("&'\\<>"); -225,228c165 -< expectTemplate('{{&awesome}}') -< .withInput({ awesome: "&'\\<>" }) -< .withMessage("expressions with {{& handlebars aren't escaped") -< .toCompileTo("&'\\<>"); ---- -> expectTemplate('{{&awesome}}').withInput({ awesome: "&'\\<>" }).toCompileTo("&'\\<>"); -232d168 -< .withMessage('by default expressions should be escaped') -237d172 -< .withMessage('escaping should properly handle amperstands') -241c176 -< it("functions returning safestrings shouldn't be escaped", function() { ---- -> it("functions returning safestrings shouldn't be escaped", () => { -244c179 -< awesome: function() { ---- -> awesome() { -246c181 -< } ---- -> }, -248d182 -< .withMessage("functions returning safestrings aren't escaped") -252c186 -< it('functions', function() { ---- -> it('functions', () => { -255c189 -< awesome: function() { ---- -> awesome() { -257c191 -< } ---- -> }, -259d192 -< .withMessage('functions are called and render their output') -264c197 -< awesome: function() { ---- -> awesome() { -267c200 -< more: 'More awesome' ---- -> more: 'More awesome', -269d201 -< .withMessage('functions are bound to the context') -273c205 -< it('functions with context argument', function() { ---- -> it('functions with context argument', () => { -276c208 -< awesome: function(context) { ---- -> awesome(context: any) { -279c211 -< frank: 'Frank' ---- -> frank: 'Frank', -281d212 -< .withMessage('functions are called with context arguments') -285c216 -< it('pathed functions with context argument', function() { ---- -> it('pathed functions with context argument', () => { -289c220 -< awesome: function(context) { ---- -> awesome(context: any) { -291c222 -< } ---- -> }, -293c224 -< frank: 'Frank' ---- -> frank: 'Frank', -295d225 -< .withMessage('functions are called with context arguments') -299c229 -< it('depthed functions with context argument', function() { ---- -> it('depthed functions with context argument', () => { -302c232 -< awesome: function(context) { ---- -> awesome(context: any) { -305c235 -< frank: 'Frank' ---- -> frank: 'Frank', -307d236 -< .withMessage('functions are called with context arguments') -311c240 -< it('block functions with context argument', function() { ---- -> it('block functions with context argument', () => { -314c243 -< awesome: function(context, options) { ---- -> awesome(context: any, options: any) { -316c245 -< } ---- -> }, -318d246 -< .withMessage('block functions are called with context and options') -322,325c250,251 -< it('depthed block functions with context argument', function() { -< expectTemplate( -< '{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}' -< ) ---- -> it('depthed block functions with context argument', () => { -> expectTemplate('{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}') -328c254 -< awesome: function(context, options) { ---- -> awesome(context: any, options: any) { -330c256 -< } ---- -> }, -332d257 -< .withMessage('block functions are called with context and options') -336c261 -< it('block functions without context argument', function() { ---- -> it('block functions without context argument', () => { -339c264 -< awesome: function(options) { ---- -> awesome(options: any) { -341c266 -< } ---- -> }, -343d267 -< .withMessage('block functions are called with options') -347c271 -< it('pathed block functions without context argument', function() { ---- -> it('pathed block functions without context argument', () => { -351c275 -< awesome: function() { ---- -> awesome() { -353,354c277,278 -< } -< } ---- -> }, -> }, -356d279 -< .withMessage('block functions are called with options') -360,363c283,284 -< it('depthed block functions without context argument', function() { -< expectTemplate( -< '{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}' -< ) ---- -> it('depthed block functions without context argument', () => { -> expectTemplate('{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}') -366c287 -< awesome: function() { ---- -> awesome() { -368c289 -< } ---- -> }, -370d290 -< .withMessage('block functions are called with options') -374,378c294,295 -< it('paths with hyphens', function() { -< expectTemplate('{{foo-bar}}') -< .withInput({ 'foo-bar': 'baz' }) -< .withMessage('Paths can contain hyphens (-)') -< .toCompileTo('baz'); ---- -> it('paths with hyphens', () => { -> expectTemplate('{{foo-bar}}').withInput({ 'foo-bar': 'baz' }).toCompileTo('baz'); -382d298 -< .withMessage('Paths can contain hyphens (-)') -387d302 -< .withMessage('Paths can contain hyphens (-)') -391c306 -< it('nested paths', function() { ---- -> it('nested paths', () => { -394d308 -< .withMessage('Nested paths access nested objects') -398c312 -< it('nested paths with empty string value', function() { ---- -> it('nested paths with empty string value', () => { -401d314 -< .withMessage('Nested paths access nested objects with empty string') -405c318 -< it('literal paths', function() { ---- -> it('literal paths', () => { -408d320 -< .withMessage('Literal paths can be used') -413d324 -< .withMessage('Literal paths can be used') -417c328 -< it('literal references', function() { ---- -> it('literal references', () => { -443c354 -< it("that current context path ({{.}}) doesn't hit helpers", function() { ---- -> it("that current context path ({{.}}) doesn't hit helpers", () => { -445a357 -> // @ts-expect-error Setting the helper to a string instead of a function doesn't make sense normally, but here it doesn't matter -450c362 -< it('complex but empty paths', function() { ---- -> it('complex but empty paths', () => { -455,457c367 -< expectTemplate('{{person/name}}') -< .withInput({ person: {} }) -< .toCompileTo(''); ---- -> expectTemplate('{{person/name}}').withInput({ person: {} }).toCompileTo(''); -460c370 -< it('this keyword in paths', function() { ---- -> it('this keyword in paths', () => { -463d372 -< .withMessage('This keyword in paths evaluates to current context') -468c377 -< hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }] ---- -> hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }], -470d378 -< .withMessage('This keyword evaluates in more complex paths') -474c382 -< it('this keyword nested inside path', function() { ---- -> it('this keyword nested inside path', () => { -476d383 -< Error, -480,482c387 -< expectTemplate('{{[this]}}') -< .withInput({ this: 'bar' }) -< .toCompileTo('bar'); ---- -> expectTemplate('{{[this]}}').withInput({ this: 'bar' }).toCompileTo('bar'); -489,491c394,396 -< it('this keyword in helpers', function() { -< var helpers = { -< foo: function(value) { ---- -> it('this keyword in helpers', () => { -> const helpers = { -> foo(value: any) { -493c398 -< } ---- -> }, -499d403 -< .withMessage('This keyword in paths evaluates to current context') -504c408 -< hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }] ---- -> hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }], -507d410 -< .withMessage('This keyword evaluates in more complex paths') -511c414 -< it('this keyword nested inside helpers param', function() { ---- -> it('this keyword nested inside helpers param', () => { -513d415 -< Error, -519c421 -< foo: function(value) { ---- -> foo(value: any) { -522c424 -< this: 'bar' ---- -> this: 'bar', -528c430 -< foo: function(value) { ---- -> foo(value: any) { -531c433 -< text: { this: 'bar' } ---- -> text: { this: 'bar' }, -536c438 -< it('pass string literals', function() { ---- -> it('pass string literals', () => { -538,541c440 -< -< expectTemplate('{{"foo"}}') -< .withInput({ foo: 'bar' }) -< .toCompileTo('bar'); ---- -> expectTemplate('{{"foo"}}').withInput({ foo: 'bar' }).toCompileTo('bar'); -545c444 -< foo: ['bar', 'baz'] ---- -> foo: ['bar', 'baz'], -550c449 -< it('pass number literals', function() { ---- -> it('pass number literals', () => { -552,556c451 -< -< expectTemplate('{{12}}') -< .withInput({ '12': 'bar' }) -< .toCompileTo('bar'); -< ---- -> expectTemplate('{{12}}').withInput({ '12': 'bar' }).toCompileTo('bar'); -558,562c453 -< -< expectTemplate('{{12.34}}') -< .withInput({ '12.34': 'bar' }) -< .toCompileTo('bar'); -< ---- -> expectTemplate('{{12.34}}').withInput({ '12.34': 'bar' }).toCompileTo('bar'); -565c456 -< '12.34': function(arg) { ---- -> '12.34'(arg: any) { -567c458 -< } ---- -> }, -572c463 -< it('pass boolean literals', function() { ---- -> it('pass boolean literals', () => { -574,581c465,466 -< -< expectTemplate('{{true}}') -< .withInput({ '': 'foo' }) -< .toCompileTo(''); -< -< expectTemplate('{{false}}') -< .withInput({ false: 'foo' }) -< .toCompileTo('foo'); ---- -> expectTemplate('{{true}}').withInput({ '': 'foo' }).toCompileTo(''); -> expectTemplate('{{false}}').withInput({ false: 'foo' }).toCompileTo('foo'); -584c469 -< it('should handle literals in subexpression', function() { ---- -> it('should handle literals in subexpression', () => { -587c472 -< false: function() { ---- -> false() { -589c474 -< } ---- -> }, -591c476 -< .withHelper('foo', function(arg) { ---- -> .withHelper('foo', function (arg) { diff --git a/packages/kbn-handlebars/.patches/blocks.patch b/packages/kbn-handlebars/.patches/blocks.patch deleted file mode 100644 index 11196151a164d..0000000000000 --- a/packages/kbn-handlebars/.patches/blocks.patch +++ /dev/null @@ -1,534 +0,0 @@ -1,3c1,13 -< describe('blocks', function() { -< it('array', function() { -< var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!'; ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -> -> import Handlebars from '../..'; -> import { expectTemplate } from '../__jest__/test_bench'; -> -> describe('blocks', () => { -> it('array', () => { -> const string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!'; -7,12c17,18 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -14d19 -< .withMessage('Arrays iterate over the contents when not empty') -20c25 -< world: 'world' ---- -> world: 'world', -22d26 -< .withMessage('Arrays ignore the contents when empty') -26,29c30,31 -< it('array without data', function() { -< expectTemplate( -< '{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}' -< ) ---- -> it('array without data', () => { -> expectTemplate('{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}') -31,36c33,34 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -38d35 -< .withCompileOptions({ compat: false }) -42,45c39,40 -< it('array with @index', function() { -< expectTemplate( -< '{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!' -< ) ---- -> it('array with @index', () => { -> expectTemplate('{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!') -47,52c42,43 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -54d44 -< .withMessage('The @index variable is used') -58,59c48,49 -< it('empty block', function() { -< var string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!'; ---- -> it('empty block', () => { -> const string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!'; -63,68c53,54 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -70d55 -< .withMessage('Arrays iterate over the contents when not empty') -76c61 -< world: 'world' ---- -> world: 'world', -78d62 -< .withMessage('Arrays ignore the contents when empty') -82c66 -< it('block with complex lookup', function() { ---- -> it('block with complex lookup', () => { -86,90c70 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ] ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -92,97c72 -< .withMessage( -< 'Templates can access variables in contexts up the stack with relative path syntax' -< ) -< .toCompileTo( -< 'goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ' -< ); ---- -> .toCompileTo('goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! '); -100c75 -< it('multiple blocks with complex lookup', function() { ---- -> it('multiple blocks with complex lookup', () => { -104,108c79 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ] ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -113,116c84,85 -< it('block with complex lookup using nested context', function() { -< expectTemplate( -< '{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}' -< ).toThrow(Error); ---- -> it('block with complex lookup using nested context', () => { -> expectTemplate('{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}').toThrow(Error); -119c88 -< it('block with deep nested complex lookup', function() { ---- -> it('block with deep nested complex lookup', () => { -125c94 -< outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }] ---- -> outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }], -130,133c99,100 -< it('works with cached blocks', function() { -< expectTemplate( -< '{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}' -< ) ---- -> it('works with cached blocks', () => { -> expectTemplate('{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}') -138,139c105,106 -< { first: 'Alan', last: 'Johnson' } -< ] ---- -> { first: 'Alan', last: 'Johnson' }, -> ], -144,145c111,112 -< describe('inverted sections', function() { -< it('inverted sections with unset value', function() { ---- -> describe('inverted sections', () => { -> it('inverted sections with unset value', () => { -148,150c115 -< ) -< .withMessage("Inverted section rendered when value isn't set.") -< .toCompileTo('Right On!'); ---- -> ).toCompileTo('Right On!'); -153,156c118,119 -< it('inverted section with false value', function() { -< expectTemplate( -< '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}' -< ) ---- -> it('inverted section with false value', () => { -> expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}') -158d120 -< .withMessage('Inverted section rendered when value is false.') -162,165c124,125 -< it('inverted section with empty set', function() { -< expectTemplate( -< '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}' -< ) ---- -> it('inverted section with empty set', () => { -> expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}') -167d126 -< .withMessage('Inverted section rendered when value is empty set.') -171c130 -< it('block inverted sections', function() { ---- -> it('block inverted sections', () => { -177c136 -< it('chained inverted sections', function() { ---- -> it('chained inverted sections', () => { -188,190c147 -< expectTemplate( -< '{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}' -< ) ---- -> expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}') -195,198c152,153 -< it('chained inverted sections with mismatch', function() { -< expectTemplate( -< '{{#people}}{{name}}{{else if none}}{{none}}{{/if}}' -< ).toThrow(Error); ---- -> it('chained inverted sections with mismatch', () => { -> expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{/if}}').toThrow(Error); -201c156 -< it('block inverted sections with empty arrays', function() { ---- -> it('block inverted sections with empty arrays', () => { -205c160 -< people: [] ---- -> people: [], -211,212c166,167 -< describe('standalone sections', function() { -< it('block standalone else sections', function() { ---- -> describe('standalone sections', () => { -> it('block standalone else sections', () => { -226,241c181,182 -< it('block standalone else sections can be disabled', function() { -< expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n') -< .withInput({ none: 'No people' }) -< .withCompileOptions({ ignoreStandalone: true }) -< .toCompileTo('\nNo people\n\n'); -< -< expectTemplate('{{#none}}\n{{.}}\n{{^}}\nFail\n{{/none}}\n') -< .withInput({ none: 'No people' }) -< .withCompileOptions({ ignoreStandalone: true }) -< .toCompileTo('\nNo people\n\n'); -< }); -< -< it('block standalone chained else sections', function() { -< expectTemplate( -< '{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n' -< ) ---- -> it('block standalone chained else sections', () => { -> expectTemplate('{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n') -245,247c186 -< expectTemplate( -< '{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n' -< ) ---- -> expectTemplate('{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n') -252c191 -< it('should handle nesting', function() { ---- -> it('should handle nesting', () => { -255c194 -< data: [1, 3, 5] ---- -> data: [1, 3, 5], -261,297c200,201 -< describe('compat mode', function() { -< it('block with deep recursive lookup lookup', function() { -< expectTemplate( -< '{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}' -< ) -< .withInput({ omg: 'OMG!', outer: [{ inner: [{ text: 'goodbye' }] }] }) -< .withCompileOptions({ compat: true }) -< .toCompileTo('Goodbye cruel OMG!'); -< }); -< -< it('block with deep recursive pathed lookup', function() { -< expectTemplate( -< '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}' -< ) -< .withInput({ -< omg: { yes: 'OMG!' }, -< outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] -< }) -< .withCompileOptions({ compat: true }) -< .toCompileTo('Goodbye cruel OMG!'); -< }); -< -< it('block with missed recursive lookup', function() { -< expectTemplate( -< '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}' -< ) -< .withInput({ -< omg: { no: 'OMG!' }, -< outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] -< }) -< .withCompileOptions({ compat: true }) -< .toCompileTo('Goodbye cruel '); -< }); -< }); -< -< describe('decorators', function() { -< it('should apply mustache decorators', function() { ---- -> describe('decorators', () => { -> it('should apply mustache decorators', () => { -299,300c203,204 -< .withHelper('helper', function(options) { -< return options.fn.run; ---- -> .withHelper('helper', function (options: Handlebars.HelperOptions) { -> return (options.fn as any).run; -302,303c206,207 -< .withDecorator('decorator', function(fn) { -< fn.run = 'success'; ---- -> .withDecorator('decorator', function (fn) { -> (fn as any).run = 'success'; -309c213 -< it('should apply allow undefined return', function() { ---- -> it('should apply allow undefined return', () => { -311,312c215,216 -< .withHelper('helper', function(options) { -< return options.fn() + options.fn.run; ---- -> .withHelper('helper', function (options: Handlebars.HelperOptions) { -> return options.fn() + (options.fn as any).run; -314,315c218,219 -< .withDecorator('decorator', function(fn) { -< fn.run = 'cess'; ---- -> .withDecorator('decorator', function (fn) { -> (fn as any).run = 'cess'; -320,325c224,227 -< it('should apply block decorators', function() { -< expectTemplate( -< '{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}' -< ) -< .withHelper('helper', function(options) { -< return options.fn.run; ---- -> it('should apply block decorators', () => { -> expectTemplate('{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}') -> .withHelper('helper', function (options: Handlebars.HelperOptions) { -> return (options.fn as any).run; -327,328c229,230 -< .withDecorator('decorator', function(fn, props, container, options) { -< fn.run = options.fn(); ---- -> .withDecorator('decorator', function (fn, props, container, options) { -> (fn as any).run = options.fn(); -334c236 -< it('should support nested decorators', function() { ---- -> it('should support nested decorators', () => { -338,339c240,241 -< .withHelper('helper', function(options) { -< return options.fn.run; ---- -> .withHelper('helper', function (options: Handlebars.HelperOptions) { -> return (options.fn as any).run; -342,343c244,245 -< decorator: function(fn, props, container, options) { -< fn.run = options.fn.nested + options.fn(); ---- -> decorator(fn, props, container, options) { -> (fn as any).run = options.fn.nested + options.fn(); -346c248 -< nested: function(fn, props, container, options) { ---- -> nested(fn, props, container, options) { -348c250 -< } ---- -> }, -353c255 -< it('should apply multiple decorators', function() { ---- -> it('should apply multiple decorators', () => { -357,358c259,260 -< .withHelper('helper', function(options) { -< return options.fn.run; ---- -> .withHelper('helper', function (options: Handlebars.HelperOptions) { -> return (options.fn as any).run; -360,361c262,263 -< .withDecorator('decorator', function(fn, props, container, options) { -< fn.run = (fn.run || '') + options.fn(); ---- -> .withDecorator('decorator', function (fn, props, container, options) { -> (fn as any).run = ((fn as any).run || '') + options.fn(); -367c269 -< it('should access parent variables', function() { ---- -> it('should access parent variables', () => { -369,370c271,272 -< .withHelper('helper', function(options) { -< return options.fn.run; ---- -> .withHelper('helper', function (options: Handlebars.HelperOptions) { -> return (options.fn as any).run; -372,373c274,275 -< .withDecorator('decorator', function(fn, props, container, options) { -< fn.run = options.args; ---- -> .withDecorator('decorator', function (fn, props, container, options) { -> (fn as any).run = options.args; -380,381c282,283 -< it('should work with root program', function() { -< var run; ---- -> it('should work with root program', () => { -> let run; -383,384c285,286 -< .withDecorator('decorator', function(fn, props, container, options) { -< equals(options.args[0], 'success'); ---- -> .withDecorator('decorator', function (fn, props, container, options) { -> expect(options.args[0]).toEqual('success'); -390c292 -< equals(run, true); ---- -> expect(run).toEqual(true); -393,394c295,296 -< it('should fail when accessing variables from root', function() { -< var run; ---- -> it('should fail when accessing variables from root', () => { -> let run; -396,397c298,299 -< .withDecorator('decorator', function(fn, props, container, options) { -< equals(options.args[0], undefined); ---- -> .withDecorator('decorator', function (fn, props, container, options) { -> expect(options.args[0]).toBeUndefined(); -403c305 -< equals(run, true); ---- -> expect(run).toEqual(true); -406,408c308,311 -< describe('registration', function() { -< it('unregisters', function() { -< handlebarsEnv.decorators = {}; ---- -> describe('registration', () => { -> beforeEach(() => { -> global.kbnHandlebarsEnv = Handlebars.create(); -> }); -410c313,321 -< handlebarsEnv.registerDecorator('foo', function() { ---- -> afterEach(() => { -> global.kbnHandlebarsEnv = null; -> }); -> -> it('unregisters', () => { -> // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property. -> kbnHandlebarsEnv!.decorators = {}; -> -> kbnHandlebarsEnv!.registerDecorator('foo', function () { -414,416c325,327 -< equals(!!handlebarsEnv.decorators.foo, true); -< handlebarsEnv.unregisterDecorator('foo'); -< equals(handlebarsEnv.decorators.foo, undefined); ---- -> expect(!!kbnHandlebarsEnv!.decorators.foo).toEqual(true); -> kbnHandlebarsEnv!.unregisterDecorator('foo'); -> expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined(); -419,420c330,332 -< it('allows multiple globals', function() { -< handlebarsEnv.decorators = {}; ---- -> it('allows multiple globals', () => { -> // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property. -> kbnHandlebarsEnv!.decorators = {}; -422,424c334,337 -< handlebarsEnv.registerDecorator({ -< foo: function() {}, -< bar: function() {} ---- -> // @ts-expect-error: Expected 2 arguments, but got 1. -> kbnHandlebarsEnv!.registerDecorator({ -> foo() {}, -> bar() {}, -427,432c340,345 -< equals(!!handlebarsEnv.decorators.foo, true); -< equals(!!handlebarsEnv.decorators.bar, true); -< handlebarsEnv.unregisterDecorator('foo'); -< handlebarsEnv.unregisterDecorator('bar'); -< equals(handlebarsEnv.decorators.foo, undefined); -< equals(handlebarsEnv.decorators.bar, undefined); ---- -> expect(!!kbnHandlebarsEnv!.decorators.foo).toEqual(true); -> expect(!!kbnHandlebarsEnv!.decorators.bar).toEqual(true); -> kbnHandlebarsEnv!.unregisterDecorator('foo'); -> kbnHandlebarsEnv!.unregisterDecorator('bar'); -> expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined(); -> expect(kbnHandlebarsEnv!.decorators.bar).toBeUndefined(); -435,445c348,354 -< it('fails with multiple and args', function() { -< shouldThrow( -< function() { -< handlebarsEnv.registerDecorator( -< { -< world: function() { -< return 'world!'; -< }, -< testHelper: function() { -< return 'found it!'; -< } ---- -> it('fails with multiple and args', () => { -> expect(() => { -> kbnHandlebarsEnv!.registerDecorator( -> // @ts-expect-error: Argument of type '{ world(): string; testHelper(): string; }' is not assignable to parameter of type 'string'. -> { -> world() { -> return 'world!'; -447,452c356,362 -< {} -< ); -< }, -< Error, -< 'Arg not supported with multiple decorators' -< ); ---- -> testHelper() { -> return 'found it!'; -> }, -> }, -> {} -> ); -> }).toThrow('Arg not supported with multiple decorators'); diff --git a/packages/kbn-handlebars/.patches/builtins.patch b/packages/kbn-handlebars/.patches/builtins.patch deleted file mode 100644 index 30b40456c2142..0000000000000 --- a/packages/kbn-handlebars/.patches/builtins.patch +++ /dev/null @@ -1,937 +0,0 @@ -1,4c1,16 -< describe('builtin helpers', function() { -< describe('#if', function() { -< it('if', function() { -< var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -> -> /* eslint-disable max-classes-per-file */ -> -> import Handlebars from '../..'; -> import { expectTemplate } from '../__jest__/test_bench'; -> -> describe('builtin helpers', () => { -> describe('#if', () => { -> it('if', () => { -> const string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; -9c21 -< world: 'world' ---- -> world: 'world', -11d22 -< .withMessage('if with boolean argument shows the contents when true') -17c28 -< world: 'world' ---- -> world: 'world', -19d29 -< .withMessage('if with string argument shows the contents') -25c35 -< world: 'world' ---- -> world: 'world', -27,29d36 -< .withMessage( -< 'if with boolean argument does not show the contents when false' -< ) -32,35c39 -< expectTemplate(string) -< .withInput({ world: 'world' }) -< .withMessage('if with undefined does not show the contents') -< .toCompileTo('cruel world!'); ---- -> expectTemplate(string).withInput({ world: 'world' }).toCompileTo('cruel world!'); -40c44 -< world: 'world' ---- -> world: 'world', -42d45 -< .withMessage('if with non-empty array shows the contents') -48c51 -< world: 'world' ---- -> world: 'world', -50d52 -< .withMessage('if with empty array does not show the contents') -56c58 -< world: 'world' ---- -> world: 'world', -58d59 -< .withMessage('if with zero does not show the contents') -61,63c62 -< expectTemplate( -< '{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!' -< ) ---- -> expectTemplate('{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!') -66c65 -< world: 'world' ---- -> world: 'world', -68d66 -< .withMessage('if with zero does not show the contents') -72,73c70,71 -< it('if with function argument', function() { -< var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; ---- -> it('if with function argument', () => { -> const string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!'; -77c75 -< goodbye: function() { ---- -> goodbye() { -80c78 -< world: 'world' ---- -> world: 'world', -82,84d79 -< .withMessage( -< 'if with function shows the contents when function returns true' -< ) -89c84 -< goodbye: function() { ---- -> goodbye() { -92c87 -< world: 'world' ---- -> world: 'world', -94,96d88 -< .withMessage( -< 'if with function shows the contents when function returns string' -< ) -101c93 -< goodbye: function() { ---- -> goodbye() { -104c96 -< world: 'world' ---- -> world: 'world', -106,108d97 -< .withMessage( -< 'if with function does not show the contents when returns false' -< ) -113c102 -< goodbye: function() { ---- -> goodbye() { -116c105 -< world: 'world' ---- -> world: 'world', -118,120d106 -< .withMessage( -< 'if with function does not show the contents when returns undefined' -< ) -124,127c110,111 -< it('should not change the depth list', function() { -< expectTemplate( -< '{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}' -< ) ---- -> it('should not change the depth list', () => { -> expectTemplate('{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}') -130c114 -< world: 'world' ---- -> world: 'world', -136,137c120,121 -< describe('#with', function() { -< it('with', function() { ---- -> describe('#with', () => { -> it('with', () => { -142,143c126,127 -< last: 'Johnson' -< } ---- -> last: 'Johnson', -> }, -148c132 -< it('with with function argument', function() { ---- -> it('with with function argument', () => { -151c135 -< person: function() { ---- -> person() { -154c138 -< last: 'Johnson' ---- -> last: 'Johnson', -156c140 -< } ---- -> }, -161c145 -< it('with with else', function() { ---- -> it('with with else', () => { -167c151 -< it('with provides block parameter', function() { ---- -> it('with provides block parameter', () => { -172,173c156,157 -< last: 'Johnson' -< } ---- -> last: 'Johnson', -> }, -178c162 -< it('works when data is disabled', function() { ---- -> it('works when data is disabled', () => { -186,194c170,172 -< describe('#each', function() { -< beforeEach(function() { -< handlebarsEnv.registerHelper('detectDataInsideEach', function(options) { -< return options.data && options.data.exclaim; -< }); -< }); -< -< it('each', function() { -< var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; ---- -> describe('#each', () => { -> it('each', () => { -> const string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; -198,203c176,177 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -205,207d178 -< .withMessage( -< 'each with array argument iterates over the contents when not empty' -< ) -213c184 -< world: 'world' ---- -> world: 'world', -215d185 -< .withMessage('each with array argument ignores the contents when empty') -219c189 -< it('each without data', function() { ---- -> it('each without data', () => { -222,227c192,193 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -240c206 -< it('each without context', function() { ---- -> it('each without context', () => { -246,248c212,213 -< it('each with an object and @key', function() { -< var string = -< '{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!'; ---- -> it('each with an object and @key', () => { -> const string = '{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!'; -250c215 -< function Clazz() { ---- -> function Clazz(this: any) { -255c220 -< var hash = { goodbyes: new Clazz(), world: 'world' }; ---- -> const hash = { goodbyes: new (Clazz as any)(), world: 'world' }; -260,270c225,233 -< var actual = compileWithPartials(string, hash); -< var expected1 = -< '<b>#1</b>. goodbye! 2. GOODBYE! cruel world!'; -< var expected2 = -< '2. GOODBYE! <b>#1</b>. goodbye! cruel world!'; -< -< equals( -< actual === expected1 || actual === expected2, -< true, -< 'each with object argument iterates over the contents when not empty' -< ); ---- -> try { -> expectTemplate(string) -> .withInput(hash) -> .toCompileTo('<b>#1</b>. goodbye! 2. GOODBYE! cruel world!'); -> } catch (e) { -> expectTemplate(string) -> .withInput(hash) -> .toCompileTo('2. GOODBYE! <b>#1</b>. goodbye! cruel world!'); -> } -275c238 -< world: 'world' ---- -> world: 'world', -280,283c243,244 -< it('each with @index', function() { -< expectTemplate( -< '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!' -< ) ---- -> it('each with @index', () => { -> expectTemplate('{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!') -285,290c246,247 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -292d248 -< .withMessage('The @index variable is used') -296c252 -< it('each with nested @index', function() { ---- -> it('each with nested @index', () => { -301,306c257,258 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -308d259 -< .withMessage('The @index variable is used') -314c265 -< it('each with block params', function() { ---- -> it('each with block params', () => { -320c271 -< world: 'world' ---- -> world: 'world', -322,324c273 -< .toCompileTo( -< '0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!' -< ); ---- -> .toCompileTo('0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!'); -327,330c276,284 -< it('each with block params and strict compilation', function() { -< expectTemplate( -< '{{#each goodbyes as |value index|}}{{index}}. {{value.text}}!{{/each}}' -< ) ---- -> // TODO: This test has been added to the `4.x` branch of the handlebars.js repo along with a code-fix, -> // but a new version of the handlebars package containing this fix has not yet been published to npm. -> // -> // Before enabling this code, a new version of handlebars needs to be released and the corresponding -> // updates needs to be applied to this implementation. -> // -> // See: https://github.com/handlebars-lang/handlebars.js/commit/30dbf0478109ded8f12bb29832135d480c17e367 -> it.skip('each with block params and strict compilation', () => { -> expectTemplate('{{#each goodbyes as |value index|}}{{index}}. {{value.text}}!{{/each}}') -336,339c290,291 -< it('each object with @index', function() { -< expectTemplate( -< '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!' -< ) ---- -> it('each object with @index', () => { -> expectTemplate('{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!') -344c296 -< c: { text: 'GOODBYE' } ---- -> c: { text: 'GOODBYE' }, -346c298 -< world: 'world' ---- -> world: 'world', -348d299 -< .withMessage('The @index variable is used') -352,355c303,304 -< it('each with @first', function() { -< expectTemplate( -< '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!' -< ) ---- -> it('each with @first', () => { -> expectTemplate('{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!') -357,362c306,307 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -364d308 -< .withMessage('The @first variable is used') -368c312 -< it('each with nested @first', function() { ---- -> it('each with nested @first', () => { -373,378c317,318 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -380,383c320 -< .withMessage('The @first variable is used') -< .toCompileTo( -< '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!' -< ); ---- -> .toCompileTo('(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!'); -386,389c323,324 -< it('each object with @first', function() { -< expectTemplate( -< '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!' -< ) ---- -> it('each object with @first', () => { -> expectTemplate('{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!') -392c327 -< world: 'world' ---- -> world: 'world', -394d328 -< .withMessage('The @first variable is used') -398,401c332,333 -< it('each with @last', function() { -< expectTemplate( -< '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!' -< ) ---- -> it('each with @last', () => { -> expectTemplate('{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!') -403,408c335,336 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -410d337 -< .withMessage('The @last variable is used') -414,417c341,342 -< it('each object with @last', function() { -< expectTemplate( -< '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!' -< ) ---- -> it('each object with @last', () => { -> expectTemplate('{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!') -420c345 -< world: 'world' ---- -> world: 'world', -422d346 -< .withMessage('The @last variable is used') -426c350 -< it('each with nested @last', function() { ---- -> it('each with nested @last', () => { -431,436c355,356 -< goodbyes: [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ], -< world: 'world' ---- -> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }], -> world: 'world', -438,441c358 -< .withMessage('The @last variable is used') -< .toCompileTo( -< '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!' -< ); ---- -> .toCompileTo('(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!'); -444,445c361,362 -< it('each with function argument', function() { -< var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; ---- -> it('each with function argument', () => { -> const string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; -449,454c366,367 -< goodbyes: function() { -< return [ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ]; ---- -> goodbyes() { -> return [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }]; -456c369 -< world: 'world' ---- -> world: 'world', -458,460d370 -< .withMessage( -< 'each with array function argument iterates over the contents when not empty' -< ) -466c376 -< world: 'world' ---- -> world: 'world', -468,470d377 -< .withMessage( -< 'each with array function argument ignores the contents when empty' -< ) -474,477c381,382 -< it('each object when last key is an empty string', function() { -< expectTemplate( -< '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!' -< ) ---- -> it('each object when last key is an empty string', () => { -> expectTemplate('{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!') -482c387 -< '': { text: 'GOODBYE' } ---- -> '': { text: 'GOODBYE' }, -484c389 -< world: 'world' ---- -> world: 'world', -486d390 -< .withMessage('Empty string key is not skipped') -490,493c394,395 -< it('data passed to helpers', function() { -< expectTemplate( -< '{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}' -< ) ---- -> it('data passed to helpers', () => { -> expectTemplate('{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}') -495c397,399 -< .withMessage('should output data') ---- -> .withHelper('detectDataInsideEach', function (options) { -> return options.data && options.data.exclaim; -> }) -498,499c402,403 -< exclaim: '!' -< } ---- -> exclaim: '!', -> }, -504,508c408,409 -< it('each on implicit context', function() { -< expectTemplate('{{#each}}{{text}}! {{/each}}cruel world!').toThrow( -< handlebarsEnv.Exception, -< 'Must pass iterator to #each' -< ); ---- -> it('each on implicit context', () => { -> expectTemplate('{{#each}}{{text}}! {{/each}}cruel world!').toThrow(Handlebars.Exception); -511,513c412,417 -< if (global.Symbol && global.Symbol.iterator) { -< it('each on iterable', function() { -< function Iterator(arr) { ---- -> it('each on iterable', () => { -> class Iterator { -> private arr: any[]; -> private index: number = 0; -> -> constructor(arr: any[]) { -515d418 -< this.index = 0; -517,519c420,423 -< Iterator.prototype.next = function() { -< var value = this.arr[this.index]; -< var done = this.index === this.arr.length; ---- -> -> next() { -> const value = this.arr[this.index]; -> const done = this.index === this.arr.length; -523,525c427,434 -< return { value: value, done: done }; -< }; -< function Iterable(arr) { ---- -> return { value, done }; -> } -> } -> -> class Iterable { -> private arr: any[]; -> -> constructor(arr: any[]) { -528c437,438 -< Iterable.prototype[global.Symbol.iterator] = function() { ---- -> -> [Symbol.iterator]() { -530,531c440,441 -< }; -< var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; ---- -> } -> } -533,545c443 -< expectTemplate(string) -< .withInput({ -< goodbyes: new Iterable([ -< { text: 'goodbye' }, -< { text: 'Goodbye' }, -< { text: 'GOODBYE' } -< ]), -< world: 'world' -< }) -< .withMessage( -< 'each with array argument iterates over the contents when not empty' -< ) -< .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!'); ---- -> const string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!'; -547,557c445,458 -< expectTemplate(string) -< .withInput({ -< goodbyes: new Iterable([]), -< world: 'world' -< }) -< .withMessage( -< 'each with array argument ignores the contents when empty' -< ) -< .toCompileTo('cruel world!'); -< }); -< } ---- -> expectTemplate(string) -> .withInput({ -> goodbyes: new Iterable([{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }]), -> world: 'world', -> }) -> .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!'); -> -> expectTemplate(string) -> .withInput({ -> goodbyes: new Iterable([]), -> world: 'world', -> }) -> .toCompileTo('cruel world!'); -> }); -560c461 -< describe('#log', function() { ---- -> describe('#log', function () { -562,564c463,465 -< if (typeof console === 'undefined') { -< return; -< } ---- -> let $log: typeof console.log; -> let $info: typeof console.info; -> let $error: typeof console.error; -566,567c467 -< var $log, $info, $error; -< beforeEach(function() { ---- -> beforeEach(function () { -570a471,472 -> -> global.kbnHandlebarsEnv = Handlebars.create(); -572c474,475 -< afterEach(function() { ---- -> -> afterEach(function () { -575a479,480 -> -> global.kbnHandlebarsEnv = null; -578,580c483,486 -< it('should call logger at default level', function() { -< var levelArg, logArg; -< handlebarsEnv.log = function(level, arg) { ---- -> it('should call logger at default level', function () { -> let levelArg; -> let logArg; -> kbnHandlebarsEnv!.log = function (level, arg) { -585,590c491,493 -< expectTemplate('{{log blah}}') -< .withInput({ blah: 'whee' }) -< .withMessage('log should not display') -< .toCompileTo(''); -< equals(1, levelArg, 'should call log with 1'); -< equals('whee', logArg, "should call log with 'whee'"); ---- -> expectTemplate('{{log blah}}').withInput({ blah: 'whee' }).toCompileTo(''); -> expect(1).toEqual(levelArg); -> expect('whee').toEqual(logArg); -593,595c496,499 -< it('should call logger at data level', function() { -< var levelArg, logArg; -< handlebarsEnv.log = function(level, arg) { ---- -> it('should call logger at data level', function () { -> let levelArg; -> let logArg; -> kbnHandlebarsEnv!.log = function (level, arg) { -605,606c509,510 -< equals('03', levelArg); -< equals('whee', logArg); ---- -> expect('03').toEqual(levelArg); -> expect('whee').toEqual(logArg); -609,610c513,515 -< it('should output to info', function() { -< var called; ---- -> it('should output to info', function () { -> let calls = 0; -> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; -612,616c517,523 -< console.info = function(info) { -< equals('whee', info); -< called = true; -< console.info = $info; -< console.log = $log; ---- -> console.info = function (info) { -> expect('whee').toEqual(info); -> calls++; -> if (calls === callsExpected) { -> console.info = $info; -> console.log = $log; -> } -618,622c525,531 -< console.log = function(log) { -< equals('whee', log); -< called = true; -< console.info = $info; -< console.log = $log; ---- -> console.log = function (log) { -> expect('whee').toEqual(log); -> calls++; -> if (calls === callsExpected) { -> console.info = $info; -> console.log = $log; -> } -625,628c534,535 -< expectTemplate('{{log blah}}') -< .withInput({ blah: 'whee' }) -< .toCompileTo(''); -< equals(true, called); ---- -> expectTemplate('{{log blah}}').withInput({ blah: 'whee' }).toCompileTo(''); -> expect(calls).toEqual(callsExpected); -631,632c538,540 -< it('should log at data level', function() { -< var called; ---- -> it('should log at data level', function () { -> let calls = 0; -> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; -634,637c542,545 -< console.error = function(log) { -< equals('whee', log); -< called = true; -< console.error = $error; ---- -> console.error = function (log) { -> expect('whee').toEqual(log); -> calls++; -> if (calls === callsExpected) console.error = $error; -645c553 -< equals(true, called); ---- -> expect(calls).toEqual(callsExpected); -648,649c556,558 -< it('should handle missing logger', function() { -< var called = false; ---- -> it('should handle missing logger', function () { -> let calls = 0; -> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; -650a560 -> // @ts-expect-error -652,655c562,565 -< console.log = function(log) { -< equals('whee', log); -< called = true; -< console.log = $log; ---- -> console.log = function (log) { -> expect('whee').toEqual(log); -> calls++; -> if (calls === callsExpected) console.log = $log; -663c573 -< equals(true, called); ---- -> expect(calls).toEqual(callsExpected); -666,667c576,578 -< it('should handle string log levels', function() { -< var called; ---- -> it('should handle string log levels', function () { -> let calls = 0; -> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; -669,671c580,582 -< console.error = function(log) { -< equals('whee', log); -< called = true; ---- -> console.error = function (log) { -> expect('whee').toEqual(log); -> calls++; -679c590 -< equals(true, called); ---- -> expect(calls).toEqual(callsExpected); -681c592 -< called = false; ---- -> calls = 0; -688c599 -< equals(true, called); ---- -> expect(calls).toEqual(callsExpected); -691,692c602,604 -< it('should handle hash log levels', function() { -< var called; ---- -> it('should handle hash log levels [1]', function () { -> let calls = 0; -> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; -694,696c606,608 -< console.error = function(log) { -< equals('whee', log); -< called = true; ---- -> console.error = function (log) { -> expect('whee').toEqual(log); -> calls++; -699,702c611,612 -< expectTemplate('{{log blah level="error"}}') -< .withInput({ blah: 'whee' }) -< .toCompileTo(''); -< equals(true, called); ---- -> expectTemplate('{{log blah level="error"}}').withInput({ blah: 'whee' }).toCompileTo(''); -> expect(calls).toEqual(callsExpected); -705,706c615,616 -< it('should handle hash log levels', function() { -< var called = false; ---- -> it('should handle hash log levels [2]', function () { -> let called = false; -708,711c618,625 -< console.info = console.log = console.error = console.debug = function() { -< called = true; -< console.info = console.log = console.error = console.debug = $log; -< }; ---- -> console.info = -> console.log = -> console.error = -> console.debug = -> function () { -> called = true; -> console.info = console.log = console.error = console.debug = $log; -> }; -713,716c627,628 -< expectTemplate('{{log blah level="debug"}}') -< .withInput({ blah: 'whee' }) -< .toCompileTo(''); -< equals(false, called); ---- -> expectTemplate('{{log blah level="debug"}}').withInput({ blah: 'whee' }).toCompileTo(''); -> expect(false).toEqual(called); -719,720c631,633 -< it('should pass multiple log arguments', function() { -< var called; ---- -> it('should pass multiple log arguments', function () { -> let calls = 0; -> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; -722,727c635,640 -< console.info = console.log = function(log1, log2, log3) { -< equals('whee', log1); -< equals('foo', log2); -< equals(1, log3); -< called = true; -< console.log = $log; ---- -> console.info = console.log = function (log1, log2, log3) { -> expect('whee').toEqual(log1); -> expect('foo').toEqual(log2); -> expect(1).toEqual(log3); -> calls++; -> if (calls === callsExpected) console.log = $log; -730,733c643,644 -< expectTemplate('{{log blah "foo" 1}}') -< .withInput({ blah: 'whee' }) -< .toCompileTo(''); -< equals(true, called); ---- -> expectTemplate('{{log blah "foo" 1}}').withInput({ blah: 'whee' }).toCompileTo(''); -> expect(calls).toEqual(callsExpected); -736,737c647,649 -< it('should pass zero log arguments', function() { -< var called; ---- -> it('should pass zero log arguments', function () { -> let calls = 0; -> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; -739,742c651,654 -< console.info = console.log = function() { -< expect(arguments.length).to.equal(0); -< called = true; -< console.log = $log; ---- -> console.info = console.log = function () { -> expect(arguments.length).toEqual(0); -> calls++; -> if (calls === callsExpected) console.log = $log; -745,748c657,658 -< expectTemplate('{{log}}') -< .withInput({ blah: 'whee' }) -< .toCompileTo(''); -< expect(called).to.be.true(); ---- -> expectTemplate('{{log}}').withInput({ blah: 'whee' }).toCompileTo(''); -> expect(calls).toEqual(callsExpected); -753,754c663,664 -< describe('#lookup', function() { -< it('should lookup arbitrary content', function() { ---- -> describe('#lookup', () => { -> it('should lookup arbitrary content', () => { -760c670 -< it('should not fail on undefined value', function() { ---- -> it('should not fail on undefined value', () => { diff --git a/packages/kbn-handlebars/.patches/compiler.patch b/packages/kbn-handlebars/.patches/compiler.patch deleted file mode 100644 index 571519b259c3b..0000000000000 --- a/packages/kbn-handlebars/.patches/compiler.patch +++ /dev/null @@ -1,269 +0,0 @@ -1,10c1,6 -< describe('compiler', function() { -< if (!Handlebars.compile) { -< return; -< } -< -< describe('#equals', function() { -< function compile(string) { -< var ast = Handlebars.parse(string); -< return new Handlebars.Compiler().compile(ast, {}); -< } ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -12,60c8,9 -< it('should treat as equal', function() { -< equal(compile('foo').equals(compile('foo')), true); -< equal(compile('{{foo}}').equals(compile('{{foo}}')), true); -< equal(compile('{{foo.bar}}').equals(compile('{{foo.bar}}')), true); -< equal( -< compile('{{foo.bar baz "foo" true false bat=1}}').equals( -< compile('{{foo.bar baz "foo" true false bat=1}}') -< ), -< true -< ); -< equal( -< compile('{{foo.bar (baz bat=1)}}').equals( -< compile('{{foo.bar (baz bat=1)}}') -< ), -< true -< ); -< equal( -< compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{/foo}}')), -< true -< ); -< }); -< it('should treat as not equal', function() { -< equal(compile('foo').equals(compile('bar')), false); -< equal(compile('{{foo}}').equals(compile('{{bar}}')), false); -< equal(compile('{{foo.bar}}').equals(compile('{{bar.bar}}')), false); -< equal( -< compile('{{foo.bar baz bat=1}}').equals( -< compile('{{foo.bar bar bat=1}}') -< ), -< false -< ); -< equal( -< compile('{{foo.bar (baz bat=1)}}').equals( -< compile('{{foo.bar (bar bat=1)}}') -< ), -< false -< ); -< equal( -< compile('{{#foo}} {{/foo}}').equals(compile('{{#bar}} {{/bar}}')), -< false -< ); -< equal( -< compile('{{#foo}} {{/foo}}').equals( -< compile('{{#foo}} {{foo}}{{/foo}}') -< ), -< false -< ); -< }); -< }); ---- -> import Handlebars from '../..'; -> import { forEachCompileFunctionName } from '../__jest__/test_bench'; -62,78c11,13 -< describe('#compile', function() { -< it('should fail with invalid input', function() { -< shouldThrow( -< function() { -< Handlebars.compile(null); -< }, -< Error, -< 'You must pass a string or Handlebars AST to Handlebars.compile. You passed null' -< ); -< shouldThrow( -< function() { -< Handlebars.compile({}); -< }, -< Error, -< 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]' -< ); -< }); ---- -> describe('compiler', () => { -> forEachCompileFunctionName((compileName) => { -> const compile = Handlebars[compileName].bind(Handlebars); -80,92c15,20 -< it('should include the location in the error (row and column)', function() { -< try { -< Handlebars.compile(' \n {{#if}}\n{{/def}}')(); -< equal( -< true, -< false, -< 'Statement must throw exception. This line should not be executed.' -< ); -< } catch (err) { -< equal( -< err.message, -< "if doesn't match def - 2:5", -< 'Checking error message' ---- -> describe(`#${compileName}`, () => { -> it('should fail with invalid input', () => { -> expect(function () { -> compile(null); -> }).toThrow( -> `You must pass a string or Handlebars AST to Handlebars.${compileName}. You passed null` -94,102d21 -< if (Object.getOwnPropertyDescriptor(err, 'column').writable) { -< // In Safari 8, the column-property is read-only. This means that even if it is set with defineProperty, -< // its value won't change (https://github.com/jquery/esprima/issues/1290#issuecomment-132455482) -< // Since this was neither working in Handlebars 3 nor in 4.0.5, we only check the column for other browsers. -< equal(err.column, 5, 'Checking error column'); -< } -< equal(err.lineNumber, 2, 'Checking error row'); -< } -< }); -104,116c23,26 -< it('should include the location as enumerable property', function() { -< try { -< Handlebars.compile(' \n {{#if}}\n{{/def}}')(); -< equal( -< true, -< false, -< 'Statement must throw exception. This line should not be executed.' -< ); -< } catch (err) { -< equal( -< Object.prototype.propertyIsEnumerable.call(err, 'column'), -< true, -< 'Checking error column' ---- -> expect(function () { -> compile({}); -> }).toThrow( -> `You must pass a string or Handlebars AST to Handlebars.${compileName}. You passed [object Object]` -118,129c28 -< } -< }); -< -< it('can utilize AST instance', function() { -< equal( -< Handlebars.compile({ -< type: 'Program', -< body: [{ type: 'ContentStatement', value: 'Hello' }] -< })(), -< 'Hello' -< ); -< }); ---- -> }); -131,133c30,44 -< it('can pass through an empty string', function() { -< equal(Handlebars.compile('')(), ''); -< }); ---- -> it('should include the location in the error (row and column)', () => { -> try { -> compile(' \n {{#if}}\n{{/def}}')(); -> expect(true).toEqual(false); -> } catch (err) { -> expect(err.message).toEqual("if doesn't match def - 2:5"); -> if (Object.getOwnPropertyDescriptor(err, 'column')!.writable) { -> // In Safari 8, the column-property is read-only. This means that even if it is set with defineProperty, -> // its value won't change (https://github.com/jquery/esprima/issues/1290#issuecomment-132455482) -> // Since this was neither working in Handlebars 3 nor in 4.0.5, we only check the column for other browsers. -> expect(err.column).toEqual(5); -> } -> expect(err.lineNumber).toEqual(2); -> } -> }); -135,142c46,53 -< it('should not modify the options.data property(GH-1327)', function() { -< var options = { data: [{ a: 'foo' }, { a: 'bar' }] }; -< Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)(); -< equal( -< JSON.stringify(options, 0, 2), -< JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, 0, 2) -< ); -< }); ---- -> it('should include the location as enumerable property', () => { -> try { -> compile(' \n {{#if}}\n{{/def}}')(); -> expect(true).toEqual(false); -> } catch (err) { -> expect(Object.prototype.propertyIsEnumerable.call(err, 'column')).toEqual(true); -> } -> }); -144,152c55,62 -< it('should not modify the options.knownHelpers property(GH-1327)', function() { -< var options = { knownHelpers: {} }; -< Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)(); -< equal( -< JSON.stringify(options, 0, 2), -< JSON.stringify({ knownHelpers: {} }, 0, 2) -< ); -< }); -< }); ---- -> it('can utilize AST instance', () => { -> expect( -> compile({ -> type: 'Program', -> body: [{ type: 'ContentStatement', value: 'Hello' }], -> })() -> ).toEqual('Hello'); -> }); -154,170c64,66 -< describe('#precompile', function() { -< it('should fail with invalid input', function() { -< shouldThrow( -< function() { -< Handlebars.precompile(null); -< }, -< Error, -< 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed null' -< ); -< shouldThrow( -< function() { -< Handlebars.precompile({}); -< }, -< Error, -< 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed [object Object]' -< ); -< }); ---- -> it('can pass through an empty string', () => { -> expect(compile('')()).toEqual(''); -> }); -172,182c68,75 -< it('can utilize AST instance', function() { -< equal( -< /return "Hello"/.test( -< Handlebars.precompile({ -< type: 'Program', -< body: [{ type: 'ContentStatement', value: 'Hello' }] -< }) -< ), -< true -< ); -< }); ---- -> it('should not modify the options.data property(GH-1327)', () => { -> // The `data` property is supposed to be a boolean, but in this test we want to ignore that -> const options = { data: [{ a: 'foo' }, { a: 'bar' }] as unknown as boolean }; -> compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)(); -> expect(JSON.stringify(options, null, 2)).toEqual( -> JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, null, 2) -> ); -> }); -184,185c77,83 -< it('can pass through an empty string', function() { -< equal(/return ""/.test(Handlebars.precompile('')), true); ---- -> it('should not modify the options.knownHelpers property(GH-1327)', () => { -> const options = { knownHelpers: {} }; -> compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)(); -> expect(JSON.stringify(options, null, 2)).toEqual( -> JSON.stringify({ knownHelpers: {} }, null, 2) -> ); -> }); diff --git a/packages/kbn-handlebars/.patches/data.patch b/packages/kbn-handlebars/.patches/data.patch deleted file mode 100644 index 037214dddc3a1..0000000000000 --- a/packages/kbn-handlebars/.patches/data.patch +++ /dev/null @@ -1,276 +0,0 @@ -1,2c1,12 -< describe('data', function() { -< it('passing in data to a compiled function that expects data - works with helpers', function() { ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -> -> import Handlebars from '../..'; -> import { expectTemplate } from '../__jest__/test_bench'; -> -> describe('data', () => { -> it('passing in data to a compiled function that expects data - works with helpers', () => { -5c15 -< .withHelper('hello', function(options) { ---- -> .withHelper('hello', function (this: any, options) { -10d19 -< .withMessage('Data output by helper') -14c23 -< it('data can be looked up via @foo', function() { ---- -> it('data can be looked up via @foo', () => { -17d25 -< .withMessage('@foo retrieves template data') -21,22c29,31 -< it('deep @foo triggers automatic top-level data', function() { -< var helpers = Handlebars.createFrame(handlebarsEnv.helpers); ---- -> it('deep @foo triggers automatic top-level data', () => { -> global.kbnHandlebarsEnv = Handlebars.create(); -> const helpers = Handlebars.createFrame(kbnHandlebarsEnv!.helpers); -24,25c33,34 -< helpers.let = function(options) { -< var frame = Handlebars.createFrame(options.data); ---- -> helpers.let = function (options: Handlebars.HelperOptions) { -> const frame = Handlebars.createFrame(options.data); -27c36 -< for (var prop in options.hash) { ---- -> for (const prop in options.hash) { -40d48 -< .withMessage('Automatic data was triggered') -41a50,51 -> -> global.kbnHandlebarsEnv = null; -44c54 -< it('parameter data can be looked up via @foo', function() { ---- -> it('parameter data can be looked up via @foo', () => { -47c57 -< .withHelper('hello', function(noun) { ---- -> .withHelper('hello', function (noun) { -50d59 -< .withMessage('@foo as a parameter retrieves template data') -54c63 -< it('hash values can be looked up via @foo', function() { ---- -> it('hash values can be looked up via @foo', () => { -57c66 -< .withHelper('hello', function(options) { ---- -> .withHelper('hello', function (options) { -60d68 -< .withMessage('@foo as a parameter retrieves template data') -64c72 -< it('nested parameter data can be looked up via @foo.bar', function() { ---- -> it('nested parameter data can be looked up via @foo.bar', () => { -67c75 -< .withHelper('hello', function(noun) { ---- -> .withHelper('hello', function (noun) { -70d77 -< .withMessage('@foo as a parameter retrieves template data') -74c81 -< it('nested parameter data does not fail with @world.bar', function() { ---- -> it('nested parameter data does not fail with @world.bar', () => { -77c84 -< .withHelper('hello', function(noun) { ---- -> .withHelper('hello', function (noun) { -80d86 -< .withMessage('@foo as a parameter retrieves template data') -84,87c90,91 -< it('parameter data throws when using complex scope references', function() { -< expectTemplate( -< '{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}' -< ).toThrow(Error); ---- -> it('parameter data throws when using complex scope references', () => { -> expectTemplate('{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}').toThrow(Error); -90c94 -< it('data can be functions', function() { ---- -> it('data can be functions', () => { -94c98 -< hello: function() { ---- -> hello() { -96,97c100,101 -< } -< } ---- -> }, -> }, -102c106 -< it('data can be functions with params', function() { ---- -> it('data can be functions with params', () => { -106c110 -< hello: function(arg) { ---- -> hello(arg: any) { -108,109c112,113 -< } -< } ---- -> }, -> }, -114c118 -< it('data is inherited downstream', function() { ---- -> it('data is inherited downstream', () => { -120,122c124,126 -< .withHelper('let', function(options) { -< var frame = Handlebars.createFrame(options.data); -< for (var prop in options.hash) { ---- -> .withHelper('let', function (this: any, options) { -> const frame = Handlebars.createFrame(options.data); -> for (const prop in options.hash) { -130d133 -< .withMessage('data variables are inherited downstream') -134,147c137 -< it('passing in data to a compiled function that expects data - works with helpers in partials', function() { -< expectTemplate('{{>myPartial}}') -< .withCompileOptions({ data: true }) -< .withPartial('myPartial', '{{hello}}') -< .withHelper('hello', function(options) { -< return options.data.adjective + ' ' + this.noun; -< }) -< .withInput({ noun: 'cat' }) -< .withRuntimeOptions({ data: { adjective: 'happy' } }) -< .withMessage('Data output by helper inside partial') -< .toCompileTo('happy cat'); -< }); -< -< it('passing in data to a compiled function that expects data - works with helpers and parameters', function() { ---- -> it('passing in data to a compiled function that expects data - works with helpers and parameters', () => { -150c140 -< .withHelper('hello', function(noun, options) { ---- -> .withHelper('hello', function (this: any, noun, options) { -155d144 -< .withMessage('Data output by helper') -159c148 -< it('passing in data to a compiled function that expects data - works with block helpers', function() { ---- -> it('passing in data to a compiled function that expects data - works with block helpers', () => { -162c151 -< data: true ---- -> data: true, -164c153 -< .withHelper('hello', function(options) { ---- -> .withHelper('hello', function (this: any, options) { -167c156 -< .withHelper('world', function(options) { ---- -> .withHelper('world', function (this: any, options) { -172d160 -< .withMessage('Data output by helper') -176c164 -< it('passing in data to a compiled function that expects data - works with block helpers that use ..', function() { ---- -> it('passing in data to a compiled function that expects data - works with block helpers that use ..', () => { -179c167 -< .withHelper('hello', function(options) { ---- -> .withHelper('hello', function (options) { -182c170 -< .withHelper('world', function(thing, options) { ---- -> .withHelper('world', function (this: any, thing, options) { -187d174 -< .withMessage('Data output by helper') -191c178 -< it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', function() { ---- -> it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', () => { -194c181 -< .withHelper('hello', function(options) { ---- -> .withHelper('hello', function (options) { -197c184 -< .withHelper('world', function(thing, options) { ---- -> .withHelper('world', function (this: any, thing, options) { -202d188 -< .withMessage('Data output by helper') -206c192 -< it('you can override inherited data when invoking a helper', function() { ---- -> it('you can override inherited data when invoking a helper', () => { -209,213c195,196 -< .withHelper('hello', function(options) { -< return options.fn( -< { exclaim: '?', zomg: 'world' }, -< { data: { adjective: 'sad' } } -< ); ---- -> .withHelper('hello', function (options) { -> return options.fn({ exclaim: '?', zomg: 'world' }, { data: { adjective: 'sad' } }); -215c198 -< .withHelper('world', function(thing, options) { ---- -> .withHelper('world', function (this: any, thing, options) { -220d202 -< .withMessage('Overriden data output by helper') -224c206 -< it('you can override inherited data when invoking a helper with depth', function() { ---- -> it('you can override inherited data when invoking a helper with depth', () => { -227c209 -< .withHelper('hello', function(options) { ---- -> .withHelper('hello', function (options) { -230c212 -< .withHelper('world', function(thing, options) { ---- -> .withHelper('world', function (this: any, thing, options) { -235d216 -< .withMessage('Overriden data output by helper') -239,240c220,221 -< describe('@root', function() { -< it('the root context can be looked up via @root', function() { ---- -> describe('@root', () => { -> it('the root context can be looked up via @root', () => { -246,248c227 -< expectTemplate('{{@root.foo}}') -< .withInput({ foo: 'hello' }) -< .toCompileTo('hello'); ---- -> expectTemplate('{{@root.foo}}').withInput({ foo: 'hello' }).toCompileTo('hello'); -251c230 -< it('passed root values take priority', function() { ---- -> it('passed root values take priority', () => { -259,260c238,239 -< describe('nesting', function() { -< it('the root context can be looked up via @root', function() { ---- -> describe('nesting', () => { -> it('the root context can be looked up via @root', () => { -265,266c244,245 -< .withHelper('helper', function(options) { -< var frame = Handlebars.createFrame(options.data); ---- -> .withHelper('helper', function (this: any, options) { -> const frame = Handlebars.createFrame(options.data); -272,273c251,252 -< depth: 0 -< } ---- -> depth: 0, -> }, diff --git a/packages/kbn-handlebars/.patches/helpers.patch b/packages/kbn-handlebars/.patches/helpers.patch deleted file mode 100644 index 09774103fb587..0000000000000 --- a/packages/kbn-handlebars/.patches/helpers.patch +++ /dev/null @@ -1,1108 +0,0 @@ -1,2c1,20 -< describe('helpers', function() { -< it('helper with complex lookup$', function() { ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -> -> import Handlebars from '../..'; -> import { expectTemplate } from '../__jest__/test_bench'; -> -> beforeEach(() => { -> global.kbnHandlebarsEnv = Handlebars.create(); -> }); -> -> afterEach(() => { -> global.kbnHandlebarsEnv = null; -> }); -> -> describe('helpers', () => { -> it('helper with complex lookup$', () => { -6c24 -< goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] ---- -> goodbyes: [{ text: 'Goodbye', url: 'goodbye' }], -8,11c26,27 -< .withHelper('link', function(prefix) { -< return ( -< '
' + this.text + '' -< ); ---- -> .withHelper('link', function (this: any, prefix) { -> return '' + this.text + ''; -16c32 -< it('helper for raw block gets raw content', function() { ---- -> it('helper for raw block gets raw content', () => { -19c35 -< .withHelper('raw', function(options) { ---- -> .withHelper('raw', function (options: Handlebars.HelperOptions) { -22d37 -< .withMessage('raw block helper gets raw content') -26c41 -< it('helper for raw block gets parameters', function() { ---- -> it('helper for raw block gets parameters', () => { -29,30c44,46 -< .withHelper('raw', function(a, b, c, options) { -< return options.fn() + a + b + c; ---- -> .withHelper('raw', function (a, b, c, options: Handlebars.HelperOptions) { -> const ret = options.fn() + a + b + c; -> return ret; -32d47 -< .withMessage('raw block helper gets raw content') -36,37c51,52 -< describe('raw block parsing (with identity helper-function)', function() { -< function runWithIdentityHelper(template, expected) { ---- -> describe('raw block parsing (with identity helper-function)', () => { -> function runWithIdentityHelper(template: string, expected: string) { -39c54 -< .withHelper('identity', function(options) { ---- -> .withHelper('identity', function (options: Handlebars.HelperOptions) { -45c60 -< it('helper for nested raw block gets raw content', function() { ---- -> it('helper for nested raw block gets raw content', () => { -52c67 -< it('helper for nested raw block works with empty content', function() { ---- -> it('helper for nested raw block works with empty content', () => { -56c71 -< xit('helper for nested raw block works if nested raw blocks are broken', function() { ---- -> it.skip('helper for nested raw block works if nested raw blocks are broken', () => { -67c82 -< it('helper for nested raw block closes after first matching close', function() { ---- -> it('helper for nested raw block closes after first matching close', () => { -74,75c89,90 -< it('helper for nested raw block throw exception when with missing closing braces', function() { -< var string = '{{{{a}}}} {{{{/a'; ---- -> it('helper for nested raw block throw exception when with missing closing braces', () => { -> const string = '{{{{a}}}} {{{{/a'; -80c95 -< it('helper block with identical context', function() { ---- -> it('helper block with identical context', () => { -83,86c98,101 -< .withHelper('goodbyes', function(options) { -< var out = ''; -< var byes = ['Goodbye', 'goodbye', 'GOODBYE']; -< for (var i = 0, j = byes.length; i < j; i++) { ---- -> .withHelper('goodbyes', function (this: any, options: Handlebars.HelperOptions) { -> let out = ''; -> const byes = ['Goodbye', 'goodbye', 'GOODBYE']; -> for (let i = 0, j = byes.length; i < j; i++) { -94c109 -< it('helper block with complex lookup expression', function() { ---- -> it('helper block with complex lookup expression', () => { -97,100c112,115 -< .withHelper('goodbyes', function(options) { -< var out = ''; -< var byes = ['Goodbye', 'goodbye', 'GOODBYE']; -< for (var i = 0, j = byes.length; i < j; i++) { ---- -> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) { -> let out = ''; -> const byes = ['Goodbye', 'goodbye', 'GOODBYE']; -> for (let i = 0, j = byes.length; i < j; i++) { -108,111c123,124 -< it('helper with complex lookup and nested template', function() { -< expectTemplate( -< '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}' -< ) ---- -> it('helper with complex lookup and nested template', () => { -> expectTemplate('{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}') -114c127 -< goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] ---- -> goodbyes: [{ text: 'Goodbye', url: 'goodbye' }], -116,125c129,130 -< .withHelper('link', function(prefix, options) { -< return ( -< '' + -< options.fn(this) + -< '' -< ); ---- -> .withHelper('link', function (this: any, prefix, options: Handlebars.HelperOptions) { -> return '' + options.fn(this) + ''; -130,133c135,136 -< it('helper with complex lookup and nested template in VM+Compiler', function() { -< expectTemplate( -< '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}' -< ) ---- -> it('helper with complex lookup and nested template in VM+Compiler', () => { -> expectTemplate('{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}') -136c139 -< goodbyes: [{ text: 'Goodbye', url: 'goodbye' }] ---- -> goodbyes: [{ text: 'Goodbye', url: 'goodbye' }], -138,147c141,142 -< .withHelper('link', function(prefix, options) { -< return ( -< '' + -< options.fn(this) + -< '' -< ); ---- -> .withHelper('link', function (this: any, prefix, options: Handlebars.HelperOptions) { -> return '' + options.fn(this) + ''; -152c147 -< it('helper returning undefined value', function() { ---- -> it('helper returning undefined value', () => { -155c150 -< nothere: function() {} ---- -> nothere() {}, -161c156 -< nothere: function() {} ---- -> nothere() {}, -166c161 -< it('block helper', function() { ---- -> it('block helper', () => { -169c164 -< .withHelper('goodbyes', function(options) { ---- -> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) { -172d166 -< .withMessage('Block helper executed') -176c170 -< it('block helper staying in the same context', function() { ---- -> it('block helper staying in the same context', () => { -179c173 -< .withHelper('form', function(options) { ---- -> .withHelper('form', function (this: any, options: Handlebars.HelperOptions) { -182d175 -< .withMessage('Block helper executed with current context') -186,187c179,180 -< it('block helper should have context in this', function() { -< function link(options) { ---- -> it('block helper should have context in this', () => { -> function link(this: any, options: Handlebars.HelperOptions) { -191,193c184 -< expectTemplate( -< '
    {{#people}}
  • {{#link}}{{name}}{{/link}}
  • {{/people}}
' -< ) ---- -> expectTemplate('
    {{#people}}
  • {{#link}}{{name}}{{/link}}
  • {{/people}}
') -197,198c188,189 -< { name: 'Yehuda', id: 2 } -< ] ---- -> { name: 'Yehuda', id: 2 }, -> ], -206c197 -< it('block helper for undefined value', function() { ---- -> it('block helper for undefined value', () => { -210c201 -< it('block helper passing a new context', function() { ---- -> it('block helper passing a new context', () => { -213c204 -< .withHelper('form', function(context, options) { ---- -> .withHelper('form', function (context, options: Handlebars.HelperOptions) { -216d206 -< .withMessage('Context variable resolved') -220c210 -< it('block helper passing a complex path context', function() { ---- -> it('block helper passing a complex path context', () => { -223c213 -< .withHelper('form', function(context, options) { ---- -> .withHelper('form', function (context, options: Handlebars.HelperOptions) { -226d215 -< .withMessage('Complex path variable resolved') -230,233c219,220 -< it('nested block helpers', function() { -< expectTemplate( -< '{{#form yehuda}}

{{name}}

{{#link}}Hello{{/link}}{{/form}}' -< ) ---- -> it('nested block helpers', () => { -> expectTemplate('{{#form yehuda}}

{{name}}

{{#link}}Hello{{/link}}{{/form}}') -235c222 -< yehuda: { name: 'Yehuda' } ---- -> yehuda: { name: 'Yehuda' }, -237c224 -< .withHelper('link', function(options) { ---- -> .withHelper('link', function (this: any, options: Handlebars.HelperOptions) { -240c227 -< .withHelper('form', function(context, options) { ---- -> .withHelper('form', function (context, options: Handlebars.HelperOptions) { -243d229 -< .withMessage('Both blocks executed') -247,249c233,235 -< it('block helper inverted sections', function() { -< var string = "{{#list people}}{{name}}{{^}}Nobody's here{{/list}}"; -< function list(context, options) { ---- -> it('block helper inverted sections', () => { -> const string = "{{#list people}}{{name}}{{^}}Nobody's here{{/list}}"; -> function list(this: any, context: any, options: Handlebars.HelperOptions) { -251,252c237,238 -< var out = '
    '; -< for (var i = 0, j = context.length; i < j; i++) { ---- -> let out = '
      '; -> for (let i = 0, j = context.length; i < j; i++) { -268,269c254 -< .withHelpers({ list: list }) -< .withMessage('an inverse wrapper is passed in as a new context') ---- -> .withHelpers({ list }) -274,275c259 -< .withHelpers({ list: list }) -< .withMessage('an inverse wrapper can be optionally called') ---- -> .withHelpers({ list }) -281c265 -< message: "Nobody's here" ---- -> message: "Nobody's here", -283,284c267 -< .withHelpers({ list: list }) -< .withMessage('the context of an inverse is the parent of the block') ---- -> .withHelpers({ list }) -288,292c271,273 -< it('pathed lambas with parameters', function() { -< var hash = { -< helper: function() { -< return 'winning'; -< } ---- -> it('pathed lambas with parameters', () => { -> const hash = { -> helper: () => 'winning', -293a275 -> // @ts-expect-error -295,299d276 -< var helpers = { -< './helper': function() { -< return 'fail'; -< } -< }; -301,304c278,280 -< expectTemplate('{{./helper 1}}') -< .withInput(hash) -< .withHelpers(helpers) -< .toCompileTo('winning'); ---- -> const helpers = { -> './helper': () => 'fail', -> }; -306,309c282,283 -< expectTemplate('{{hash/helper 1}}') -< .withInput(hash) -< .withHelpers(helpers) -< .toCompileTo('winning'); ---- -> expectTemplate('{{./helper 1}}').withInput(hash).withHelpers(helpers).toCompileTo('winning'); -> expectTemplate('{{hash/helper 1}}').withInput(hash).withHelpers(helpers).toCompileTo('winning'); -312,313c286,287 -< describe('helpers hash', function() { -< it('providing a helpers hash', function() { ---- -> describe('helpers hash', () => { -> it('providing a helpers hash', () => { -317c291 -< world: function() { ---- -> world() { -319c293 -< } ---- -> }, -321d294 -< .withMessage('helpers hash is available') -327c300 -< world: function() { ---- -> world() { -329c302 -< } ---- -> }, -331d303 -< .withMessage('helpers hash is available inside other blocks') -335c307 -< it('in cases of conflict, helpers win', function() { ---- -> it('in cases of conflict, helpers win', () => { -339c311 -< lookup: function() { ---- -> lookup() { -341c313 -< } ---- -> }, -343d314 -< .withMessage('helpers hash has precedence escaped expansion') -349c320 -< lookup: function() { ---- -> lookup() { -351c322 -< } ---- -> }, -353d323 -< .withMessage('helpers hash has precedence simple expansion') -357c327 -< it('the helpers hash is available is nested contexts', function() { ---- -> it('the helpers hash is available is nested contexts', () => { -361c331 -< helper: function() { ---- -> helper() { -363c333 -< } ---- -> }, -365d334 -< .withMessage('helpers hash is available in nested contexts.') -369,370c338,339 -< it('the helper hash should augment the global hash', function() { -< handlebarsEnv.registerHelper('test_helper', function() { ---- -> it('the helper hash should augment the global hash', () => { -> kbnHandlebarsEnv!.registerHelper('test_helper', function () { -374,376c343 -< expectTemplate( -< '{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}' -< ) ---- -> expectTemplate('{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}') -379c346 -< world: function() { ---- -> world() { -381c348 -< } ---- -> }, -387,389c354,356 -< describe('registration', function() { -< it('unregisters', function() { -< handlebarsEnv.helpers = {}; ---- -> describe('registration', () => { -> it('unregisters', () => { -> deleteAllKeys(kbnHandlebarsEnv!.helpers); -391c358 -< handlebarsEnv.registerHelper('foo', function() { ---- -> kbnHandlebarsEnv!.registerHelper('foo', function () { -394,395c361,363 -< handlebarsEnv.unregisterHelper('foo'); -< equals(handlebarsEnv.helpers.foo, undefined); ---- -> expect(kbnHandlebarsEnv!.helpers.foo).toBeDefined(); -> kbnHandlebarsEnv!.unregisterHelper('foo'); -> expect(kbnHandlebarsEnv!.helpers.foo).toBeUndefined(); -398,400c366,368 -< it('allows multiple globals', function() { -< var helpers = handlebarsEnv.helpers; -< handlebarsEnv.helpers = {}; ---- -> it('allows multiple globals', () => { -> const ifHelper = kbnHandlebarsEnv!.helpers.if; -> deleteAllKeys(kbnHandlebarsEnv!.helpers); -402,404c370,372 -< handlebarsEnv.registerHelper({ -< if: helpers['if'], -< world: function() { ---- -> kbnHandlebarsEnv!.registerHelper({ -> if: ifHelper, -> world() { -407c375 -< testHelper: function() { ---- -> testHelper() { -409c377 -< } ---- -> }, -412,414c380 -< expectTemplate( -< '{{testHelper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}' -< ) ---- -> expectTemplate('{{testHelper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}') -419,429c385,391 -< it('fails with multiple and args', function() { -< shouldThrow( -< function() { -< handlebarsEnv.registerHelper( -< { -< world: function() { -< return 'world!'; -< }, -< testHelper: function() { -< return 'found it!'; -< } ---- -> it('fails with multiple and args', () => { -> expect(() => { -> kbnHandlebarsEnv!.registerHelper( -> // @ts-expect-error TypeScript is complaining about the invalid input just as the thrown error -> { -> world() { -> return 'world!'; -431,436c393,399 -< {} -< ); -< }, -< Error, -< 'Arg not supported with multiple helpers' -< ); ---- -> testHelper() { -> return 'found it!'; -> }, -> }, -> {} -> ); -> }).toThrow('Arg not supported with multiple helpers'); -440c403 -< it('decimal number literals work', function() { ---- -> it('decimal number literals work', () => { -442c405 -< .withHelper('hello', function(times, times2) { ---- -> .withHelper('hello', function (times, times2) { -451d413 -< .withMessage('template with a negative integer literal') -455c417 -< it('negative number literals work', function() { ---- -> it('negative number literals work', () => { -457c419 -< .withHelper('hello', function(times) { ---- -> .withHelper('hello', function (times) { -463d424 -< .withMessage('template with a negative integer literal') -467,468c428,429 -< describe('String literal parameters', function() { -< it('simple literals work', function() { ---- -> describe('String literal parameters', () => { -> it('simple literals work', () => { -470c431 -< .withHelper('hello', function(param, times, bool1, bool2) { ---- -> .withHelper('hello', function (param, times, bool1, bool2) { -480,482c441 -< return ( -< 'Hello ' + param + ' ' + times + ' times: ' + bool1 + ' ' + bool2 -< ); ---- -> return 'Hello ' + param + ' ' + times + ' times: ' + bool1 + ' ' + bool2; -484d442 -< .withMessage('template with a simple String literal') -488c446 -< it('using a quote in the middle of a parameter raises an error', function() { ---- -> it('using a quote in the middle of a parameter raises an error', () => { -492c450 -< it('escaping a String is possible', function() { ---- -> it('escaping a String is possible', () => { -494c452 -< .withHelper('hello', function(param) { ---- -> .withHelper('hello', function (param) { -497d454 -< .withMessage('template with an escaped String literal') -501c458 -< it("it works with ' marks", function() { ---- -> it("it works with ' marks", () => { -503c460 -< .withHelper('hello', function(param) { ---- -> .withHelper('hello', function (param) { -506d462 -< .withMessage("template with a ' mark") -511,524c467,468 -< it('negative number literals work', function() { -< expectTemplate('Message: {{hello -12}}') -< .withHelper('hello', function(times) { -< if (typeof times !== 'number') { -< times = 'NaN'; -< } -< return 'Hello ' + times + ' times'; -< }) -< .withMessage('template with a negative integer literal') -< .toCompileTo('Message: Hello -12 times'); -< }); -< -< describe('multiple parameters', function() { -< it('simple multi-params work', function() { ---- -> describe('multiple parameters', () => { -> it('simple multi-params work', () => { -527c471 -< .withHelper('goodbye', function(cruel, world) { ---- -> .withHelper('goodbye', function (cruel, world) { -530d473 -< .withMessage('regular helpers with multiple params') -534,537c477,478 -< it('block multi-params work', function() { -< expectTemplate( -< 'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}' -< ) ---- -> it('block multi-params work', () => { -> expectTemplate('Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}') -539c480 -< .withHelper('goodbye', function(cruel, world, options) { ---- -> .withHelper('goodbye', function (cruel, world, options: Handlebars.HelperOptions) { -542d482 -< .withMessage('block helpers with multiple params') -547,548c487,488 -< describe('hash', function() { -< it('helpers can take an optional hash', function() { ---- -> describe('hash', () => { -> it('helpers can take an optional hash', () => { -550c490 -< .withHelper('goodbye', function(options) { ---- -> .withHelper('goodbye', function (options: Handlebars.HelperOptions) { -561d500 -< .withMessage('Helper output hash') -565,566c504,505 -< it('helpers can take an optional hash with booleans', function() { -< function goodbye(options) { ---- -> it('helpers can take an optional hash with booleans', () => { -> function goodbye(options: Handlebars.HelperOptions) { -578d516 -< .withMessage('Helper output hash') -583d520 -< .withMessage('Boolean helper parameter honored') -587c524 -< it('block helpers can take an optional hash', function() { ---- -> it('block helpers can take an optional hash', () => { -589c526 -< .withHelper('goodbye', function(options) { ---- -> .withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) { -600d536 -< .withMessage('Hash parameters output') -604c540 -< it('block helpers can take an optional hash with single quoted stings', function() { ---- -> it('block helpers can take an optional hash with single quoted stings', () => { -606c542 -< .withHelper('goodbye', function(options) { ---- -> .withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) { -617d552 -< .withMessage('Hash parameters output') -621,622c556,557 -< it('block helpers can take an optional hash with booleans', function() { -< function goodbye(options) { ---- -> it('block helpers can take an optional hash with booleans', () => { -> function goodbye(this: any, options: Handlebars.HelperOptions) { -634d568 -< .withMessage('Boolean hash parameter honored') -639d572 -< .withMessage('Boolean hash parameter honored') -644,648c577,579 -< describe('helperMissing', function() { -< it('if a context is not found, helperMissing is used', function() { -< expectTemplate('{{hello}} {{link_to world}}').toThrow( -< /Missing helper: "link_to"/ -< ); ---- -> describe('helperMissing', () => { -> it('if a context is not found, helperMissing is used', () => { -> expectTemplate('{{hello}} {{link_to world}}').toThrow(/Missing helper: "link_to"/); -651c582 -< it('if a context is not found, custom helperMissing is used', function() { ---- -> it('if a context is not found, custom helperMissing is used', () => { -654c585 -< .withHelper('helperMissing', function(mesg, options) { ---- -> .withHelper('helperMissing', function (mesg, options: Handlebars.HelperOptions) { -662c593 -< it('if a value is not found, custom helperMissing is used', function() { ---- -> it('if a value is not found, custom helperMissing is used', () => { -665c596 -< .withHelper('helperMissing', function(options) { ---- -> .withHelper('helperMissing', function (options: Handlebars.HelperOptions) { -674,675c605,606 -< describe('knownHelpers', function() { -< it('Known helper should render helper', function() { ---- -> describe('knownHelpers', () => { -> it('Known helper should render helper', () => { -678c609 -< knownHelpers: { hello: true } ---- -> knownHelpers: { hello: true }, -680c611 -< .withHelper('hello', function() { ---- -> .withHelper('hello', function () { -686c617 -< it('Unknown helper in knownHelpers only mode should be passed as undefined', function() { ---- -> it('Unknown helper in knownHelpers only mode should be passed as undefined', () => { -690c621 -< knownHelpersOnly: true ---- -> knownHelpersOnly: true, -692c623 -< .withHelper('typeof', function(arg) { ---- -> .withHelper('typeof', function (arg) { -695c626 -< .withHelper('hello', function() { ---- -> .withHelper('hello', function () { -701c632 -< it('Builtin helpers available in knownHelpers only mode', function() { ---- -> it('Builtin helpers available in knownHelpers only mode', () => { -704c635 -< knownHelpersOnly: true ---- -> knownHelpersOnly: true, -709c640 -< it('Field lookup works in knownHelpers only mode', function() { ---- -> it('Field lookup works in knownHelpers only mode', () => { -712c643 -< knownHelpersOnly: true ---- -> knownHelpersOnly: true, -718c649 -< it('Conditional blocks work in knownHelpers only mode', function() { ---- -> it('Conditional blocks work in knownHelpers only mode', () => { -721c652 -< knownHelpersOnly: true ---- -> knownHelpersOnly: true, -727c658 -< it('Invert blocks work in knownHelpers only mode', function() { ---- -> it('Invert blocks work in knownHelpers only mode', () => { -730c661 -< knownHelpersOnly: true ---- -> knownHelpersOnly: true, -736c667 -< it('Functions are bound to the context in knownHelpers only mode', function() { ---- -> it('Functions are bound to the context in knownHelpers only mode', () => { -739c670 -< knownHelpersOnly: true ---- -> knownHelpersOnly: true, -742c673 -< foo: function() { ---- -> foo() { -745c676 -< bar: 'bar' ---- -> bar: 'bar', -750c681 -< it('Unknown helper call in knownHelpers only mode should throw', function() { ---- -> it('Unknown helper call in knownHelpers only mode should throw', () => { -757,758c688,689 -< describe('blockHelperMissing', function() { -< it('lambdas are resolved by blockHelperMissing, not handlebars proper', function() { ---- -> describe('blockHelperMissing', () => { -> it('lambdas are resolved by blockHelperMissing, not handlebars proper', () => { -761c692 -< truthy: function() { ---- -> truthy() { -763c694 -< } ---- -> }, -768c699 -< it('lambdas resolved by blockHelperMissing are bound to the context', function() { ---- -> it('lambdas resolved by blockHelperMissing are bound to the context', () => { -771c702 -< truthy: function() { ---- -> truthy() { -774c705 -< truthiness: function() { ---- -> truthiness() { -776c707 -< } ---- -> }, -782,785c713,716 -< describe('name field', function() { -< var helpers = { -< blockHelperMissing: function() { -< return 'missing: ' + arguments[arguments.length - 1].name; ---- -> describe('name field', () => { -> const helpers = { -> blockHelperMissing(...args: any[]) { -> return 'missing: ' + args[args.length - 1].name; -787,788c718,722 -< helperMissing: function() { -< return 'helper missing: ' + arguments[arguments.length - 1].name; ---- -> helperMissing(...args: any[]) { -> return 'helper missing: ' + args[args.length - 1].name; -> }, -> helper(...args: any[]) { -> return 'ran: ' + args[args.length - 1].name; -790,792d723 -< helper: function() { -< return 'ran: ' + arguments[arguments.length - 1].name; -< } -795,798c726,727 -< it('should include in ambiguous mustache calls', function() { -< expectTemplate('{{helper}}') -< .withHelpers(helpers) -< .toCompileTo('ran: helper'); ---- -> it('should include in ambiguous mustache calls', () => { -> expectTemplate('{{helper}}').withHelpers(helpers).toCompileTo('ran: helper'); -801,804c730,731 -< it('should include in helper mustache calls', function() { -< expectTemplate('{{helper 1}}') -< .withHelpers(helpers) -< .toCompileTo('ran: helper'); ---- -> it('should include in helper mustache calls', () => { -> expectTemplate('{{helper 1}}').withHelpers(helpers).toCompileTo('ran: helper'); -807,810c734,735 -< it('should include in ambiguous block calls', function() { -< expectTemplate('{{#helper}}{{/helper}}') -< .withHelpers(helpers) -< .toCompileTo('ran: helper'); ---- -> it('should include in ambiguous block calls', () => { -> expectTemplate('{{#helper}}{{/helper}}').withHelpers(helpers).toCompileTo('ran: helper'); -813c738 -< it('should include in simple block calls', function() { ---- -> it('should include in simple block calls', () => { -819,822c744,745 -< it('should include in helper block calls', function() { -< expectTemplate('{{#helper 1}}{{/helper}}') -< .withHelpers(helpers) -< .toCompileTo('ran: helper'); ---- -> it('should include in helper block calls', () => { -> expectTemplate('{{#helper 1}}{{/helper}}').withHelpers(helpers).toCompileTo('ran: helper'); -825c748 -< it('should include in known helper calls', function() { ---- -> it('should include in known helper calls', () => { -829c752 -< knownHelpersOnly: true ---- -> knownHelpersOnly: true, -835c758 -< it('should include full id', function() { ---- -> it('should include full id', () => { -842c765 -< it('should include full id if a hash is passed', function() { ---- -> it('should include full id if a hash is passed', () => { -850,851c773,774 -< describe('name conflicts', function() { -< it('helpers take precedence over same-named context properties', function() { ---- -> describe('name conflicts', () => { -> it('helpers take precedence over same-named context properties', () => { -853c776 -< .withHelper('goodbye', function() { ---- -> .withHelper('goodbye', function (this: any) { -856c779 -< .withHelper('cruel', function(world) { ---- -> .withHelper('cruel', function (world) { -861c784 -< world: 'world' ---- -> world: 'world', -863d785 -< .withMessage('Helper executed') -867c789 -< it('helpers take precedence over same-named context properties$', function() { ---- -> it('helpers take precedence over same-named context properties$', () => { -869c791 -< .withHelper('goodbye', function(options) { ---- -> .withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) { -872c794 -< .withHelper('cruel', function(world) { ---- -> .withHelper('cruel', function (world) { -877c799 -< world: 'world' ---- -> world: 'world', -879d800 -< .withMessage('Helper executed') -883c804 -< it('Scoped names take precedence over helpers', function() { ---- -> it('Scoped names take precedence over helpers', () => { -885c806 -< .withHelper('goodbye', function() { ---- -> .withHelper('goodbye', function (this: any) { -888c809 -< .withHelper('cruel', function(world) { ---- -> .withHelper('cruel', function (world) { -893c814 -< world: 'world' ---- -> world: 'world', -895d815 -< .withMessage('Helper not executed') -899,903c819,821 -< it('Scoped names take precedence over block helpers', function() { -< expectTemplate( -< '{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}' -< ) -< .withHelper('goodbye', function(options) { ---- -> it('Scoped names take precedence over block helpers', () => { -> expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}') -> .withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) { -906c824 -< .withHelper('cruel', function(world) { ---- -> .withHelper('cruel', function (world) { -911c829 -< world: 'world' ---- -> world: 'world', -913d830 -< .withMessage('Helper executed') -918,919c835,836 -< describe('block params', function() { -< it('should take presedence over context values', function() { ---- -> describe('block params', () => { -> it('should take presedence over context values', () => { -922,923c839,840 -< .withHelper('goodbyes', function(options) { -< equals(options.fn.blockParams, 1); ---- -> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) { -> expect(options.fn.blockParams).toEqual(1); -929c846 -< it('should take presedence over helper values', function() { ---- -> it('should take presedence over helper values', () => { -931c848 -< .withHelper('value', function() { ---- -> .withHelper('value', function () { -934,935c851,852 -< .withHelper('goodbyes', function(options) { -< equals(options.fn.blockParams, 1); ---- -> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) { -> expect(options.fn.blockParams).toEqual(1); -941,944c858,859 -< it('should not take presedence over pathed values', function() { -< expectTemplate( -< '{{#goodbyes as |value|}}{{./value}}{{/goodbyes}}{{value}}' -< ) ---- -> it('should not take presedence over pathed values', () => { -> expectTemplate('{{#goodbyes as |value|}}{{./value}}{{/goodbyes}}{{value}}') -946c861 -< .withHelper('value', function() { ---- -> .withHelper('value', function () { -949,950c864,865 -< .withHelper('goodbyes', function(options) { -< equals(options.fn.blockParams, 1); ---- -> .withHelper('goodbyes', function (this: any, options: Handlebars.HelperOptions) { -> expect(options.fn.blockParams).toEqual(1); -956,957c871,872 -< it('should take presednece over parent block params', function() { -< var value = 1; ---- -> it('should take presednece over parent block params', () => { -> let value: number; -959c874,879 -< '{{#goodbyes as |value|}}{{#goodbyes}}{{value}}{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{/goodbyes}}{{/goodbyes}}{{value}}' ---- -> '{{#goodbyes as |value|}}{{#goodbyes}}{{value}}{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{/goodbyes}}{{/goodbyes}}{{value}}', -> { -> beforeEach() { -> value = 1; -> }, -> } -962c882 -< .withHelper('goodbyes', function(options) { ---- -> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) { -966,967c886 -< blockParams: -< options.fn.blockParams === 1 ? [value++, value++] : undefined ---- -> blockParams: options.fn.blockParams === 1 ? [value++, value++] : undefined, -974,977c893,894 -< it('should allow block params on chained helpers', function() { -< expectTemplate( -< '{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}' -< ) ---- -> it('should allow block params on chained helpers', () => { -> expectTemplate('{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}') -979,980c896,897 -< .withHelper('goodbyes', function(options) { -< equals(options.fn.blockParams, 1); ---- -> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) { -> expect(options.fn.blockParams).toEqual(1); -987,991c904,906 -< describe('built-in helpers malformed arguments ', function() { -< it('if helper - too few arguments', function() { -< expectTemplate('{{#if}}{{/if}}').toThrow( -< /#if requires exactly one argument/ -< ); ---- -> describe('built-in helpers malformed arguments ', () => { -> it('if helper - too few arguments', () => { -> expectTemplate('{{#if}}{{/if}}').toThrow(/#if requires exactly one argument/); -994,997c909,910 -< it('if helper - too many arguments, string', function() { -< expectTemplate('{{#if test "string"}}{{/if}}').toThrow( -< /#if requires exactly one argument/ -< ); ---- -> it('if helper - too many arguments, string', () => { -> expectTemplate('{{#if test "string"}}{{/if}}').toThrow(/#if requires exactly one argument/); -1000,1003c913,914 -< it('if helper - too many arguments, undefined', function() { -< expectTemplate('{{#if test undefined}}{{/if}}').toThrow( -< /#if requires exactly one argument/ -< ); ---- -> it('if helper - too many arguments, undefined', () => { -> expectTemplate('{{#if test undefined}}{{/if}}').toThrow(/#if requires exactly one argument/); -1006,1009c917,918 -< it('if helper - too many arguments, null', function() { -< expectTemplate('{{#if test null}}{{/if}}').toThrow( -< /#if requires exactly one argument/ -< ); ---- -> it('if helper - too many arguments, null', () => { -> expectTemplate('{{#if test null}}{{/if}}').toThrow(/#if requires exactly one argument/); -1012,1015c921,922 -< it('unless helper - too few arguments', function() { -< expectTemplate('{{#unless}}{{/unless}}').toThrow( -< /#unless requires exactly one argument/ -< ); ---- -> it('unless helper - too few arguments', () => { -> expectTemplate('{{#unless}}{{/unless}}').toThrow(/#unless requires exactly one argument/); -1018c925 -< it('unless helper - too many arguments', function() { ---- -> it('unless helper - too many arguments', () => { -1024,1027c931,932 -< it('with helper - too few arguments', function() { -< expectTemplate('{{#with}}{{/with}}').toThrow( -< /#with requires exactly one argument/ -< ); ---- -> it('with helper - too few arguments', () => { -> expectTemplate('{{#with}}{{/with}}').toThrow(/#with requires exactly one argument/); -1030c935 -< it('with helper - too many arguments', function() { ---- -> it('with helper - too many arguments', () => { -1037,1038c942,943 -< describe('the lookupProperty-option', function() { -< it('should be passed to custom helpers', function() { ---- -> describe('the lookupProperty-option', () => { -> it('should be passed to custom helpers', () => { -1040,1042c945,950 -< .withHelper('testHelper', function testHelper(options) { -< return options.lookupProperty(this, 'testProperty'); -< }) ---- -> .withHelper( -> 'testHelper', -> function testHelper(this: any, options: Handlebars.HelperOptions) { -> return options.lookupProperty(this, 'testProperty'); -> } -> ) -1047a956,961 -> -> function deleteAllKeys(obj: { [key: string]: any }) { -> for (const key of Object.keys(obj)) { -> delete obj[key]; -> } -> } diff --git a/packages/kbn-handlebars/.patches/regressions.patch b/packages/kbn-handlebars/.patches/regressions.patch deleted file mode 100644 index 89eb0d927403e..0000000000000 --- a/packages/kbn-handlebars/.patches/regressions.patch +++ /dev/null @@ -1,519 +0,0 @@ -1,2c1,12 -< describe('Regressions', function() { -< it('GH-94: Cannot read property of undefined', function() { ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -> -> import Handlebars from '../..'; -> import { expectTemplate } from '../__jest__/test_bench'; -> -> describe('Regressions', () => { -> it('GH-94: Cannot read property of undefined', () => { -9,10c19,20 -< name: 'Charles Darwin' -< } ---- -> name: 'Charles Darwin', -> }, -13,15c23,25 -< title: 'Lazarillo de Tormes' -< } -< ] ---- -> title: 'Lazarillo de Tormes', -> }, -> ], -17d26 -< .withMessage('Renders without an undefined property error') -21,43c30,35 -< it("GH-150: Inverted sections print when they shouldn't", function() { -< var string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}'; -< -< expectTemplate(string) -< .withMessage( -< "inverted sections run when property isn't present in context" -< ) -< .toCompileTo('not set :: '); -< -< expectTemplate(string) -< .withInput({ set: undefined }) -< .withMessage('inverted sections run when property is undefined') -< .toCompileTo('not set :: '); -< -< expectTemplate(string) -< .withInput({ set: false }) -< .withMessage('inverted sections run when property is false') -< .toCompileTo('not set :: '); -< -< expectTemplate(string) -< .withInput({ set: true }) -< .withMessage("inverted sections don't run when property is true") -< .toCompileTo(' :: set'); ---- -> it("GH-150: Inverted sections print when they shouldn't", () => { -> const string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}'; -> expectTemplate(string).toCompileTo('not set :: '); -> expectTemplate(string).withInput({ set: undefined }).toCompileTo('not set :: '); -> expectTemplate(string).withInput({ set: false }).toCompileTo('not set :: '); -> expectTemplate(string).withInput({ set: true }).toCompileTo(' :: set'); -46c38 -< it('GH-158: Using array index twice, breaks the template', function() { ---- -> it('GH-158: Using array index twice, breaks the template', () => { -49d40 -< .withMessage('it works as expected') -53,54c44,45 -< it("bug reported by @fat where lambdas weren't being properly resolved", function() { -< var string = ---- -> it("bug reported by @fat where lambdas weren't being properly resolved", () => { -> const string = -69,70c60,61 -< var data = { -< thing: function() { ---- -> const data = { -> thing() { -76c67 -< { className: 'three', word: '@sayrer' } ---- -> { className: 'three', word: '@sayrer' }, -78c69 -< hasThings: function() { ---- -> hasThings() { -80c71 -< } ---- -> }, -83c74 -< var output = ---- -> const output = -92,94c83 -< expectTemplate(string) -< .withInput(data) -< .toCompileTo(output); ---- -> expectTemplate(string).withInput(data).toCompileTo(output); -97,100c86,87 -< it('GH-408: Multiple loops fail', function() { -< expectTemplate( -< '{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}' -< ) ---- -> it('GH-408: Multiple loops fail', () => { -> expectTemplate('{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}') -103c90 -< { name: 'Jane Doe', location: { city: 'New York' } } ---- -> { name: 'Jane Doe', location: { city: 'New York' } }, -105d91 -< .withMessage('It should output multiple times') -109,110c95,96 -< it('GS-428: Nested if else rendering', function() { -< var succeedingTemplate = ---- -> it('GS-428: Nested if else rendering', () => { -> const succeedingTemplate = -112c98 -< var failingTemplate = ---- -> const failingTemplate = -115,116c101,102 -< var helpers = { -< blk: function(block) { ---- -> const helpers = { -> blk(block: Handlebars.HelperOptions) { -119c105 -< inverse: function(block) { ---- -> inverse(block: Handlebars.HelperOptions) { -121c107 -< } ---- -> }, -124,130c110,111 -< expectTemplate(succeedingTemplate) -< .withHelpers(helpers) -< .toCompileTo(' Expected '); -< -< expectTemplate(failingTemplate) -< .withHelpers(helpers) -< .toCompileTo(' Expected '); ---- -> expectTemplate(succeedingTemplate).withHelpers(helpers).toCompileTo(' Expected '); -> expectTemplate(failingTemplate).withHelpers(helpers).toCompileTo(' Expected '); -133,136c114,115 -< it('GH-458: Scoped this identifier', function() { -< expectTemplate('{{./foo}}') -< .withInput({ foo: 'bar' }) -< .toCompileTo('bar'); ---- -> it('GH-458: Scoped this identifier', () => { -> expectTemplate('{{./foo}}').withInput({ foo: 'bar' }).toCompileTo('bar'); -139c118 -< it('GH-375: Unicode line terminators', function() { ---- -> it('GH-375: Unicode line terminators', () => { -143c122 -< it('GH-534: Object prototype aliases', function() { ---- -> it('GH-534: Object prototype aliases', () => { -144a124 -> // @ts-expect-error -147,149c127 -< expectTemplate('{{foo}}') -< .withInput({ foo: 'bar' }) -< .toCompileTo('bar'); ---- -> expectTemplate('{{foo}}').withInput({ foo: 'bar' }).toCompileTo('bar'); -150a129 -> // @ts-expect-error -155,157c134,136 -< it('GH-437: Matching escaping', function() { -< expectTemplate('{{{a}}').toThrow(Error, /Parse error on/); -< expectTemplate('{{a}}}').toThrow(Error, /Parse error on/); ---- -> it('GH-437: Matching escaping', () => { -> expectTemplate('{{{a}}').toThrow(/Parse error on/); -> expectTemplate('{{a}}}').toThrow(/Parse error on/); -160,166c139,141 -< it('GH-676: Using array in escaping mustache fails', function() { -< var data = { arr: [1, 2] }; -< -< expectTemplate('{{arr}}') -< .withInput(data) -< .withMessage('it works as expected') -< .toCompileTo(data.arr.toString()); ---- -> it('GH-676: Using array in escaping mustache fails', () => { -> const data = { arr: [1, 2] }; -> expectTemplate('{{arr}}').withInput(data).toCompileTo(data.arr.toString()); -169c144 -< it('Mustache man page', function() { ---- -> it('Mustache man page', () => { -177c152 -< in_ca: true ---- -> in_ca: true, -179,182c154 -< .withMessage('the hello world mustache example works') -< .toCompileTo( -< 'Hello Chris. You have just won $10000! Well, $6000, after taxes.' -< ); ---- -> .toCompileTo('Hello Chris. You have just won $10000! Well, $6000, after taxes.'); -185c157 -< it('GH-731: zero context rendering', function() { ---- -> it('GH-731: zero context rendering', () => { -189c161 -< bar: 'OK' ---- -> bar: 'OK', -194,197c166,167 -< it('GH-820: zero pathed rendering', function() { -< expectTemplate('{{foo.bar}}') -< .withInput({ foo: 0 }) -< .toCompileTo(''); ---- -> it('GH-820: zero pathed rendering', () => { -> expectTemplate('{{foo.bar}}').withInput({ foo: 0 }).toCompileTo(''); -200c170 -< it('GH-837: undefined values for helpers', function() { ---- -> it('GH-837: undefined values for helpers', () => { -203c173 -< str: function(value) { ---- -> str(value) { -205c175 -< } ---- -> }, -210c180 -< it('GH-926: Depths and de-dupe', function() { ---- -> it('GH-926: Depths and de-dupe', () => { -217c187 -< notData: [1] ---- -> notData: [1], -222c192 -< it('GH-1021: Each empty string key', function() { ---- -> it('GH-1021: Each empty string key', () => { -228,229c198,199 -< value: 10000 -< } ---- -> value: 10000, -> }, -234,248c204,205 -< it('GH-1054: Should handle simple safe string responses', function() { -< expectTemplate('{{#wrap}}{{>partial}}{{/wrap}}') -< .withHelpers({ -< wrap: function(options) { -< return new Handlebars.SafeString(options.fn()); -< } -< }) -< .withPartials({ -< partial: '{{#wrap}}{{/wrap}}' -< }) -< .toCompileTo(''); -< }); -< -< it('GH-1065: Sparse arrays', function() { -< var array = []; ---- -> it('GH-1065: Sparse arrays', () => { -> const array = []; -252c209 -< .withInput({ array: array }) ---- -> .withInput({ array }) -256c213 -< it('GH-1093: Undefined helper context', function() { ---- -> it('GH-1093: Undefined helper context', () => { -260c217 -< helper: function() { ---- -> helper(this: any) { -263c220 -< for (var name in this) { ---- -> for (const name in this) { -270c227 -< } ---- -> }, -275,306c232 -< it('should support multiple levels of inline partials', function() { -< expectTemplate( -< '{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}' -< ) -< .withPartials({ -< doctype: 'doctype{{> content}}', -< layout: -< '{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}' -< }) -< .toCompileTo('doctypelayoutsubcontent'); -< }); -< -< it('GH-1089: should support failover content in multiple levels of inline partials', function() { -< expectTemplate('{{#> layout}}{{/layout}}') -< .withPartials({ -< doctype: 'doctype{{> content}}', -< layout: -< '{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}' -< }) -< .toCompileTo('doctypelayoutsubcontent'); -< }); -< -< it('GH-1099: should support greater than 3 nested levels of inline partials', function() { -< expectTemplate('{{#> layout}}Outer{{/layout}}') -< .withPartials({ -< layout: '{{#> inner}}Inner{{/inner}}{{> @partial-block }}', -< inner: '' -< }) -< .toCompileTo('Outer'); -< }); -< -< it('GH-1135 : Context handling within each iteration', function() { ---- -> it('GH-1135 : Context handling within each iteration', () => { -315c241 -< myif: function(conditional, options) { ---- -> myif(conditional, options: Handlebars.HelperOptions) { -321c247 -< } ---- -> }, -326,343c252,253 -< it('GH-1186: Support block params for existing programs', function() { -< expectTemplate( -< '{{#*inline "test"}}{{> @partial-block }}{{/inline}}' + -< '{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' + -< '{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}' -< ) -< .withInput({ -< listOne: ['a'], -< listTwo: ['b'] -< }) -< .withMessage('') -< .toCompileTo('ab'); -< }); -< -< it('GH-1319: "unless" breaks when "each" value equals "null"', function() { -< expectTemplate( -< '{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}' -< ) ---- -> it('GH-1319: "unless" breaks when "each" value equals "null"', () => { -> expectTemplate('{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}') -346c256 -< list: [null, 'a'] ---- -> list: [null, 'a'], -348d257 -< .withMessage('') -352,457c261 -< it('GH-1341: 4.0.7 release breaks {{#if @partial-block}} usage', function() { -< expectTemplate('template {{>partial}} template') -< .withPartials({ -< partialWithBlock: -< '{{#if @partial-block}} block {{> @partial-block}} block {{/if}}', -< partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}' -< }) -< .toCompileTo('template block partial block template'); -< }); -< -< describe('GH-1561: 4.3.x should still work with precompiled templates from 4.0.0 <= x < 4.3.0', function() { -< it('should compile and execute templates', function() { -< var newHandlebarsInstance = Handlebars.create(); -< -< registerTemplate(newHandlebarsInstance, compiledTemplateVersion7()); -< newHandlebarsInstance.registerHelper('loud', function(value) { -< return value.toUpperCase(); -< }); -< var result = newHandlebarsInstance.templates['test.hbs']({ -< name: 'yehuda' -< }); -< equals(result.trim(), 'YEHUDA'); -< }); -< -< it('should call "helperMissing" if a helper is missing', function() { -< var newHandlebarsInstance = Handlebars.create(); -< -< shouldThrow( -< function() { -< registerTemplate(newHandlebarsInstance, compiledTemplateVersion7()); -< newHandlebarsInstance.templates['test.hbs']({}); -< }, -< Handlebars.Exception, -< 'Missing helper: "loud"' -< ); -< }); -< -< it('should pass "options.lookupProperty" to "lookup"-helper, even with old templates', function() { -< var newHandlebarsInstance = Handlebars.create(); -< registerTemplate( -< newHandlebarsInstance, -< compiledTemplateVersion7_usingLookupHelper() -< ); -< -< newHandlebarsInstance.templates['test.hbs']({}); -< -< expect( -< newHandlebarsInstance.templates['test.hbs']({ -< property: 'a', -< test: { a: 'b' } -< }) -< ).to.equal('b'); -< }); -< -< function registerTemplate(Handlebars, compileTemplate) { -< var template = Handlebars.template, -< templates = (Handlebars.templates = Handlebars.templates || {}); -< templates['test.hbs'] = template(compileTemplate); -< } -< -< function compiledTemplateVersion7() { -< return { -< compiler: [7, '>= 4.0.0'], -< main: function(container, depth0, helpers, partials, data) { -< return ( -< container.escapeExpression( -< ( -< helpers.loud || -< (depth0 && depth0.loud) || -< helpers.helperMissing -< ).call( -< depth0 != null ? depth0 : container.nullContext || {}, -< depth0 != null ? depth0.name : depth0, -< { name: 'loud', hash: {}, data: data } -< ) -< ) + '\n\n' -< ); -< }, -< useData: true -< }; -< } -< -< function compiledTemplateVersion7_usingLookupHelper() { -< // This is the compiled version of "{{lookup test property}}" -< return { -< compiler: [7, '>= 4.0.0'], -< main: function(container, depth0, helpers, partials, data) { -< return container.escapeExpression( -< helpers.lookup.call( -< depth0 != null ? depth0 : container.nullContext || {}, -< depth0 != null ? depth0.test : depth0, -< depth0 != null ? depth0.property : depth0, -< { -< name: 'lookup', -< hash: {}, -< data: data -< } -< ) -< ); -< }, -< useData: true -< }; -< } -< }); -< -< it('should allow hash with protected array names', function() { ---- -> it('should allow hash with protected array names', () => { -461c265 -< helpa: function(options) { ---- -> helpa(options: Handlebars.HelperOptions) { -463c267 -< } ---- -> }, -468,496c272,273 -< describe('GH-1598: Performance degradation for partials since v4.3.0', function() { -< // Do not run test for runs without compiler -< if (!Handlebars.compile) { -< return; -< } -< -< var newHandlebarsInstance; -< beforeEach(function() { -< newHandlebarsInstance = Handlebars.create(); -< }); -< afterEach(function() { -< sinon.restore(); -< }); -< -< it('should only compile global partials once', function() { -< var templateSpy = sinon.spy(newHandlebarsInstance, 'template'); -< newHandlebarsInstance.registerPartial({ -< dude: 'I am a partial' -< }); -< var string = 'Dudes: {{> dude}} {{> dude}}'; -< newHandlebarsInstance.compile(string)(); // This should compile template + partial once -< newHandlebarsInstance.compile(string)(); // This should only compile template -< equal(templateSpy.callCount, 3); -< sinon.restore(); -< }); -< }); -< -< describe("GH-1639: TypeError: Cannot read property 'apply' of undefined\" when handlebars version > 4.6.0 (undocumented, deprecated usage)", function() { -< it('should treat undefined helpers like non-existing helpers', function() { ---- -> describe("GH-1639: TypeError: Cannot read property 'apply' of undefined\" when handlebars version > 4.6.0 (undocumented, deprecated usage)", () => { -> it('should treat undefined helpers like non-existing helpers', () => { diff --git a/packages/kbn-handlebars/.patches/security.patch b/packages/kbn-handlebars/.patches/security.patch deleted file mode 100644 index 3eb55711c3f9c..0000000000000 --- a/packages/kbn-handlebars/.patches/security.patch +++ /dev/null @@ -1,443 +0,0 @@ -1,6c1,6 -< describe('security issues', function() { -< describe('GH-1495: Prevent Remote Code Execution via constructor', function() { -< it('should not allow constructors to be accessed', function() { -< expectTemplate('{{lookup (lookup this "constructor") "name"}}') -< .withInput({}) -< .toCompileTo(''); ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -8,10c8,15 -< expectTemplate('{{constructor.name}}') -< .withInput({}) -< .toCompileTo(''); ---- -> import Handlebars from '../..'; -> import { expectTemplate } from '../__jest__/test_bench'; -> -> describe('security issues', () => { -> describe('GH-1495: Prevent Remote Code Execution via constructor', () => { -> it('should not allow constructors to be accessed', () => { -> expectTemplate('{{lookup (lookup this "constructor") "name"}}').withInput({}).toCompileTo(''); -> expectTemplate('{{constructor.name}}').withInput({}).toCompileTo(''); -13c18 -< it('GH-1603: should not allow constructors to be accessed (lookup via toString)', function() { ---- -> it('GH-1603: should not allow constructors to be accessed (lookup via toString)', () => { -16c21 -< .withHelper('list', function(element) { ---- -> .withHelper('list', function (element) { -22c27 -< it('should allow the "constructor" property to be accessed if it is an "ownProperty"', function() { ---- -> it('should allow the "constructor" property to be accessed if it is an "ownProperty"', () => { -32c37 -< it('should allow the "constructor" property to be accessed if it is an "own property"', function() { ---- -> it('should allow the "constructor" property to be accessed if it is an "own property"', () => { -39,45c44,46 -< describe('GH-1558: Prevent explicit call of helperMissing-helpers', function() { -< if (!Handlebars.compile) { -< return; -< } -< -< describe('without the option "allowExplicitCallOfHelperMissing"', function() { -< it('should throw an exception when calling "{{helperMissing}}" ', function() { ---- -> describe('GH-1558: Prevent explicit call of helperMissing-helpers', () => { -> describe('without the option "allowExplicitCallOfHelperMissing"', () => { -> it('should throw an exception when calling "{{helperMissing}}" ', () => { -49c50 -< it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { ---- -> it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', () => { -53,56c54,57 -< it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { -< var functionCalls = []; -< expect(function() { -< var template = Handlebars.compile('{{blockHelperMissing "abc" .}}'); ---- -> it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', () => { -> const functionCalls = []; -> expect(() => { -> const template = Handlebars.compile('{{blockHelperMissing "abc" .}}'); -58c59 -< fn: function() { ---- -> fn() { -60c61 -< } ---- -> }, -62,63c63,64 -< }).to.throw(Error); -< expect(functionCalls.length).to.equal(0); ---- -> }).toThrow(Error); -> expect(functionCalls.length).toEqual(0); -66c67 -< it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { ---- -> it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', () => { -69c70 -< fn: function() { ---- -> fn() { -71c72 -< } ---- -> }, -76,110d76 -< -< describe('with the option "allowCallsToHelperMissing" set to true', function() { -< it('should not throw an exception when calling "{{helperMissing}}" ', function() { -< var template = Handlebars.compile('{{helperMissing}}'); -< template({}, { allowCallsToHelperMissing: true }); -< }); -< -< it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { -< var template = Handlebars.compile( -< '{{#helperMissing}}{{/helperMissing}}' -< ); -< template({}, { allowCallsToHelperMissing: true }); -< }); -< -< it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { -< var functionCalls = []; -< var template = Handlebars.compile('{{blockHelperMissing "abc" .}}'); -< template( -< { -< fn: function() { -< functionCalls.push('called'); -< } -< }, -< { allowCallsToHelperMissing: true } -< ); -< equals(functionCalls.length, 1); -< }); -< -< it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { -< var template = Handlebars.compile( -< '{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}' -< ); -< template({}, { allowCallsToHelperMissing: true }); -< }); -< }); -113,114c79,81 -< describe('GH-1563', function() { -< it('should not allow to access constructor after overriding via __defineGetter__', function() { ---- -> describe('GH-1563', () => { -> it('should not allow to access constructor after overriding via __defineGetter__', () => { -> // @ts-expect-error -116c83 -< return this.skip(); // Browser does not support this exploit anyway ---- -> return; // Browser does not support this exploit anyway -130,131c97,98 -< describe('GH-1595: dangerous properties', function() { -< var templates = [ ---- -> describe('GH-1595: dangerous properties', () => { -> const templates = [ -141c108 -< '{{lookup this "__proto__"}}' ---- -> '{{lookup this "__proto__"}}', -144,382c111,114 -< templates.forEach(function(template) { -< describe('access should be denied to ' + template, function() { -< it('by default', function() { -< expectTemplate(template) -< .withInput({}) -< .toCompileTo(''); -< }); -< it(' with proto-access enabled', function() { -< expectTemplate(template) -< .withInput({}) -< .withRuntimeOptions({ -< allowProtoPropertiesByDefault: true, -< allowProtoMethodsByDefault: true -< }) -< .toCompileTo(''); -< }); -< }); -< }); -< }); -< describe('GH-1631: disallow access to prototype functions', function() { -< function TestClass() {} -< -< TestClass.prototype.aProperty = 'propertyValue'; -< TestClass.prototype.aMethod = function() { -< return 'returnValue'; -< }; -< -< beforeEach(function() { -< handlebarsEnv.resetLoggedPropertyAccesses(); -< }); -< -< afterEach(function() { -< sinon.restore(); -< }); -< -< describe('control access to prototype methods via "allowedProtoMethods"', function() { -< checkProtoMethodAccess({}); -< -< describe('in compat mode', function() { -< checkProtoMethodAccess({ compat: true }); -< }); -< -< function checkProtoMethodAccess(compileOptions) { -< it('should be prohibited by default and log a warning', function() { -< var spy = sinon.spy(console, 'error'); -< -< expectTemplate('{{aMethod}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .toCompileTo(''); -< -< expect(spy.calledOnce).to.be.true(); -< expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/); -< }); -< -< it('should only log the warning once', function() { -< var spy = sinon.spy(console, 'error'); -< -< expectTemplate('{{aMethod}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .toCompileTo(''); -< -< expectTemplate('{{aMethod}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .toCompileTo(''); -< -< expect(spy.calledOnce).to.be.true(); -< expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/); -< }); -< -< it('can be allowed, which disables the warning', function() { -< var spy = sinon.spy(console, 'error'); -< -< expectTemplate('{{aMethod}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .withRuntimeOptions({ -< allowedProtoMethods: { -< aMethod: true -< } -< }) -< .toCompileTo('returnValue'); -< -< expect(spy.callCount).to.equal(0); -< }); -< -< it('can be turned on by default, which disables the warning', function() { -< var spy = sinon.spy(console, 'error'); -< -< expectTemplate('{{aMethod}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .withRuntimeOptions({ -< allowProtoMethodsByDefault: true -< }) -< .toCompileTo('returnValue'); -< -< expect(spy.callCount).to.equal(0); -< }); -< -< it('can be turned off by default, which disables the warning', function() { -< var spy = sinon.spy(console, 'error'); -< -< expectTemplate('{{aMethod}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .withRuntimeOptions({ -< allowProtoMethodsByDefault: false -< }) -< .toCompileTo(''); -< -< expect(spy.callCount).to.equal(0); -< }); -< -< it('can be turned off, if turned on by default', function() { -< expectTemplate('{{aMethod}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .withRuntimeOptions({ -< allowProtoMethodsByDefault: true, -< allowedProtoMethods: { -< aMethod: false -< } -< }) -< .toCompileTo(''); -< }); -< } -< -< it('should cause the recursive lookup by default (in "compat" mode)', function() { -< expectTemplate('{{#aString}}{{trim}}{{/aString}}') -< .withInput({ aString: ' abc ', trim: 'trim' }) -< .withCompileOptions({ compat: true }) -< .toCompileTo('trim'); -< }); -< -< it('should not cause the recursive lookup if allowed through options(in "compat" mode)', function() { -< expectTemplate('{{#aString}}{{trim}}{{/aString}}') -< .withInput({ aString: ' abc ', trim: 'trim' }) -< .withCompileOptions({ compat: true }) -< .withRuntimeOptions({ -< allowedProtoMethods: { -< trim: true -< } -< }) -< .toCompileTo('abc'); -< }); -< }); -< -< describe('control access to prototype non-methods via "allowedProtoProperties" and "allowProtoPropertiesByDefault', function() { -< checkProtoPropertyAccess({}); -< -< describe('in compat-mode', function() { -< checkProtoPropertyAccess({ compat: true }); -< }); -< -< describe('in strict-mode', function() { -< checkProtoPropertyAccess({ strict: true }); -< }); -< -< function checkProtoPropertyAccess(compileOptions) { -< it('should be prohibited by default and log a warning', function() { -< var spy = sinon.spy(console, 'error'); -< -< expectTemplate('{{aProperty}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .toCompileTo(''); -< -< expect(spy.calledOnce).to.be.true(); -< expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/); -< }); -< -< it('can be explicitly prohibited by default, which disables the warning', function() { -< var spy = sinon.spy(console, 'error'); -< -< expectTemplate('{{aProperty}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .withRuntimeOptions({ -< allowProtoPropertiesByDefault: false -< }) -< .toCompileTo(''); -< -< expect(spy.callCount).to.equal(0); -< }); -< -< it('can be turned on, which disables the warning', function() { -< var spy = sinon.spy(console, 'error'); -< -< expectTemplate('{{aProperty}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .withRuntimeOptions({ -< allowedProtoProperties: { -< aProperty: true -< } -< }) -< .toCompileTo('propertyValue'); -< -< expect(spy.callCount).to.equal(0); -< }); -< -< it('can be turned on by default, which disables the warning', function() { -< var spy = sinon.spy(console, 'error'); -< -< expectTemplate('{{aProperty}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .withRuntimeOptions({ -< allowProtoPropertiesByDefault: true -< }) -< .toCompileTo('propertyValue'); -< -< expect(spy.callCount).to.equal(0); -< }); -< -< it('can be turned off, if turned on by default', function() { -< expectTemplate('{{aProperty}}') -< .withInput(new TestClass()) -< .withCompileOptions(compileOptions) -< .withRuntimeOptions({ -< allowProtoPropertiesByDefault: true, -< allowedProtoProperties: { -< aProperty: false -< } -< }) -< .toCompileTo(''); -< }); -< } -< }); -< -< describe('compatibility with old runtimes, that do not provide the function "container.lookupProperty"', function() { -< beforeEach(function simulateRuntimeWithoutLookupProperty() { -< var oldTemplateMethod = handlebarsEnv.template; -< sinon.replace(handlebarsEnv, 'template', function(templateSpec) { -< templateSpec.main = wrapToAdjustContainer(templateSpec.main); -< return oldTemplateMethod.call(this, templateSpec); ---- -> templates.forEach((template) => { -> describe('access should be denied to ' + template, () => { -> it('by default', () => { -> expectTemplate(template).withInput({}).toCompileTo(''); -385,400d116 -< -< afterEach(function() { -< sinon.restore(); -< }); -< -< it('should work with simple properties', function() { -< expectTemplate('{{aProperty}}') -< .withInput({ aProperty: 'propertyValue' }) -< .toCompileTo('propertyValue'); -< }); -< -< it('should work with Array.prototype.length', function() { -< expectTemplate('{{anArray.length}}') -< .withInput({ anArray: ['a', 'b', 'c'] }) -< .toCompileTo('3'); -< }); -404,409c120,122 -< describe('escapes template variables', function() { -< it('in compat mode', function() { -< expectTemplate("{{'a\\b'}}") -< .withCompileOptions({ compat: true }) -< .withInput({ 'a\\b': 'c' }) -< .toCompileTo('c'); ---- -> describe('escapes template variables', () => { -> it('in default mode', () => { -> expectTemplate("{{'a\\b'}}").withCompileOptions().withInput({ 'a\\b': 'c' }).toCompileTo('c'); -412,418c125 -< it('in default mode', function() { -< expectTemplate("{{'a\\b'}}") -< .withCompileOptions() -< .withInput({ 'a\\b': 'c' }) -< .toCompileTo('c'); -< }); -< it('in default mode', function() { ---- -> it('in strict mode', () => { -426,432d132 -< -< function wrapToAdjustContainer(precompiledTemplateFunction) { -< return function templateFunctionWrapper(container /*, more args */) { -< delete container.lookupProperty; -< return precompiledTemplateFunction.apply(this, arguments); -< }; -< } diff --git a/packages/kbn-handlebars/.patches/strict.patch b/packages/kbn-handlebars/.patches/strict.patch deleted file mode 100644 index 30613348b9852..0000000000000 --- a/packages/kbn-handlebars/.patches/strict.patch +++ /dev/null @@ -1,180 +0,0 @@ -1c1,6 -< var Exception = Handlebars.Exception; ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -3,5c8,12 -< describe('strict', function() { -< describe('strict mode', function() { -< it('should error on missing property lookup', function() { ---- -> import { expectTemplate } from '../__jest__/test_bench'; -> -> describe('strict', () => { -> describe('strict mode', () => { -> it('should error on missing property lookup', () => { -8c15 -< .toThrow(Exception, /"hello" not defined in/); ---- -> .toThrow(/"hello" not defined in/); -11c18 -< it('should error on missing child', function() { ---- -> it('should error on missing child', () => { -20c27 -< .toThrow(Exception, /"bar" not defined in/); ---- -> .toThrow(/"bar" not defined in/); -23c30 -< it('should handle explicit undefined', function() { ---- -> it('should handle explicit undefined', () => { -30c37 -< it('should error on missing property lookup in known helpers mode', function() { ---- -> it('should error on missing property lookup in known helpers mode', () => { -34c41 -< knownHelpersOnly: true ---- -> knownHelpersOnly: true, -36c43 -< .toThrow(Exception, /"hello" not defined in/); ---- -> .toThrow(/"hello" not defined in/); -39,42c46,47 -< it('should error on missing context', function() { -< expectTemplate('{{hello}}') -< .withCompileOptions({ strict: true }) -< .toThrow(Error); ---- -> it('should error on missing context', () => { -> expectTemplate('{{hello}}').withCompileOptions({ strict: true }).toThrow(Error); -45,47c50,52 -< it('should error on missing data lookup', function() { -< var xt = expectTemplate('{{@hello}}').withCompileOptions({ -< strict: true ---- -> it('should error on missing data lookup', () => { -> const xt = expectTemplate('{{@hello}}').withCompileOptions({ -> strict: true, -55c60 -< it('should not run helperMissing for helper calls', function() { ---- -> it('should not run helperMissing for helper calls', () => { -59c64 -< .toThrow(Exception, /"hello" not defined in/); ---- -> .toThrow(/"hello" not defined in/); -64c69 -< .toThrow(Exception, /"hello" not defined in/); ---- -> .toThrow(/"hello" not defined in/); -67c72 -< it('should throw on ambiguous blocks', function() { ---- -> it('should throw on ambiguous blocks', () => { -70c75 -< .toThrow(Exception, /"hello" not defined in/); ---- -> .toThrow(/"hello" not defined in/); -74c79 -< .toThrow(Exception, /"hello" not defined in/); ---- -> .toThrow(/"hello" not defined in/); -79c84 -< .toThrow(Exception, /"bar" not defined in/); ---- -> .toThrow(/"bar" not defined in/); -82c87 -< it('should allow undefined parameters when passed to helpers', function() { ---- -> it('should allow undefined parameters when passed to helpers', () => { -88c93 -< it('should allow undefined hash when passed to helpers', function() { ---- -> it('should allow undefined hash when passed to helpers', () => { -91c96 -< strict: true ---- -> strict: true, -94,96c99,101 -< helper: function(options) { -< equals('value' in options.hash, true); -< equals(options.hash.value, undefined); ---- -> helper(options) { -> expect('value' in options.hash).toEqual(true); -> expect(options.hash.value).toBeUndefined(); -98c103 -< } ---- -> }, -103c108 -< it('should show error location on missing property lookup', function() { ---- -> it('should show error location on missing property lookup', () => { -106c111 -< .toThrow(Exception, '"hello" not defined in [object Object] - 4:5'); ---- -> .toThrow('"hello" not defined in [object Object] - 4:5'); -109c114 -< it('should error contains correct location properties on missing property lookup', function() { ---- -> it('should error contains correct location properties on missing property lookup', () => { -111,114c116,118 -< var template = CompilerContext.compile('\n\n\n {{hello}}', { -< strict: true -< }); -< template({}); ---- -> expectTemplate('\n\n\n {{hello}}') -> .withCompileOptions({ strict: true }) -> .toCompileTo('throw before asserting this'); -116,119c120,123 -< equals(error.lineNumber, 4); -< equals(error.endLineNumber, 4); -< equals(error.column, 5); -< equals(error.endColumn, 10); ---- -> expect(error.lineNumber).toEqual(4); -> expect(error.endLineNumber).toEqual(4); -> expect(error.column).toEqual(5); -> expect(error.endColumn).toEqual(10); -124,128c128,130 -< describe('assume objects', function() { -< it('should ignore missing property', function() { -< expectTemplate('{{hello}}') -< .withCompileOptions({ assumeObjects: true }) -< .toCompileTo(''); ---- -> describe('assume objects', () => { -> it('should ignore missing property', () => { -> expectTemplate('{{hello}}').withCompileOptions({ assumeObjects: true }).toCompileTo(''); -131c133 -< it('should ignore missing child', function() { ---- -> it('should ignore missing child', () => { -138,141c140,141 -< it('should error on missing object', function() { -< expectTemplate('{{hello.bar}}') -< .withCompileOptions({ assumeObjects: true }) -< .toThrow(Error); ---- -> it('should error on missing object', () => { -> expectTemplate('{{hello.bar}}').withCompileOptions({ assumeObjects: true }).toThrow(Error); -144c144 -< it('should error on missing context', function() { ---- -> it('should error on missing context', () => { -151c151 -< it('should error on missing data lookup', function() { ---- -> it('should error on missing data lookup', () => { -158c158 -< it('should execute blockHelperMissing', function() { ---- -> it('should execute blockHelperMissing', () => { diff --git a/packages/kbn-handlebars/.patches/subexpressions.patch b/packages/kbn-handlebars/.patches/subexpressions.patch deleted file mode 100644 index c775ac2035aff..0000000000000 --- a/packages/kbn-handlebars/.patches/subexpressions.patch +++ /dev/null @@ -1,318 +0,0 @@ -1,2c1,12 -< describe('subexpressions', function() { -< it('arg-less helper', function() { ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -> -> import Handlebars from '../..'; -> import { expectTemplate } from '../__jest__/test_bench'; -> -> describe('subexpressions', () => { -> it('arg-less helper', () => { -5c15 -< foo: function(val) { ---- -> foo(val) { -8c18 -< bar: function() { ---- -> bar() { -10c20 -< } ---- -> }, -15c25 -< it('helper w args', function() { ---- -> it('helper w args', () => { -19c29 -< blog: function(val) { ---- -> blog(val) { -22c32 -< equal: function(x, y) { ---- -> equal(x, y) { -24c34 -< } ---- -> }, -29c39 -< it('mixed paths and helpers', function() { ---- -> it('mixed paths and helpers', () => { -33c43 -< blog: function(val, that, theOther) { ---- -> blog(val, that, theOther) { -36c46 -< equal: function(x, y) { ---- -> equal(x, y) { -38c48 -< } ---- -> }, -43c53 -< it('supports much nesting', function() { ---- -> it('supports much nesting', () => { -47c57 -< blog: function(val) { ---- -> blog(val) { -50c60 -< equal: function(x, y) { ---- -> equal(x, y) { -52c62 -< } ---- -> }, -57,60c67,70 -< it('GH-800 : Complex subexpressions', function() { -< var context = { a: 'a', b: 'b', c: { c: 'c' }, d: 'd', e: { e: 'e' } }; -< var helpers = { -< dash: function(a, b) { ---- -> it('GH-800 : Complex subexpressions', () => { -> const context = { a: 'a', b: 'b', c: { c: 'c' }, d: 'd', e: { e: 'e' } }; -> const helpers = { -> dash(a: any, b: any) { -63c73 -< concat: function(a, b) { ---- -> concat(a: any, b: any) { -65c75 -< } ---- -> }, -94,97c104,107 -< it('provides each nested helper invocation its own options hash', function() { -< var lastOptions = null; -< var helpers = { -< equal: function(x, y, options) { ---- -> it('provides each nested helper invocation its own options hash', () => { -> let lastOptions: Handlebars.HelperOptions; -> const helpers = { -> equal(x: any, y: any, options: Handlebars.HelperOptions) { -103c113 -< } ---- -> }, -105,107c115 -< expectTemplate('{{equal (equal true true) true}}') -< .withHelpers(helpers) -< .toCompileTo('true'); ---- -> expectTemplate('{{equal (equal true true) true}}').withHelpers(helpers).toCompileTo('true'); -110c118 -< it('with hashes', function() { ---- -> it('with hashes', () => { -114c122 -< blog: function(val) { ---- -> blog(val) { -117c125 -< equal: function(x, y) { ---- -> equal(x, y) { -119c127 -< } ---- -> }, -124c132 -< it('as hashes', function() { ---- -> it('as hashes', () => { -127c135 -< blog: function(options) { ---- -> blog(options) { -130c138 -< equal: function(x, y) { ---- -> equal(x, y) { -132c140 -< } ---- -> }, -137,140c145,146 -< it('multiple subexpressions in a hash', function() { -< expectTemplate( -< '{{input aria-label=(t "Name") placeholder=(t "Example User")}}' -< ) ---- -> it('multiple subexpressions in a hash', () => { -> expectTemplate('{{input aria-label=(t "Name") placeholder=(t "Example User")}}') -142,145c148,151 -< input: function(options) { -< var hash = options.hash; -< var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']); -< var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder); ---- -> input(options) { -> const hash = options.hash; -> const ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']); -> const placeholder = Handlebars.Utils.escapeExpression(hash.placeholder); -147,151c153 -< '' ---- -> '' -154c156 -< t: function(defaultString) { ---- -> t(defaultString) { -156c158 -< } ---- -> }, -161,164c163,164 -< it('multiple subexpressions in a hash with context', function() { -< expectTemplate( -< '{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}' -< ) ---- -> it('multiple subexpressions in a hash with context', () => { -> expectTemplate('{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}') -168,169c168,169 -< placeholder: 'Example User' -< } ---- -> placeholder: 'Example User', -> }, -172,175c172,175 -< input: function(options) { -< var hash = options.hash; -< var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']); -< var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder); ---- -> input(options) { -> const hash = options.hash; -> const ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']); -> const placeholder = Handlebars.Utils.escapeExpression(hash.placeholder); -177,181c177 -< '' ---- -> '' -184c180 -< t: function(defaultString) { ---- -> t(defaultString) { -186,242d181 -< } -< }) -< .toCompileTo(''); -< }); -< -< it('in string params mode,', function() { -< expectTemplate('{{snog (blorg foo x=y) yeah a=b}}') -< .withCompileOptions({ stringParams: true }) -< .withHelpers({ -< snog: function(a, b, options) { -< equals(a, 'foo'); -< equals( -< options.types.length, -< 2, -< 'string params for outer helper processed correctly' -< ); -< equals( -< options.types[0], -< 'SubExpression', -< 'string params for outer helper processed correctly' -< ); -< equals( -< options.types[1], -< 'PathExpression', -< 'string params for outer helper processed correctly' -< ); -< return a + b; -< }, -< -< blorg: function(a, options) { -< equals( -< options.types.length, -< 1, -< 'string params for inner helper processed correctly' -< ); -< equals( -< options.types[0], -< 'PathExpression', -< 'string params for inner helper processed correctly' -< ); -< return a; -< } -< }) -< .withInput({ -< foo: {}, -< yeah: {} -< }) -< .toCompileTo('fooyeah'); -< }); -< -< it('as hashes in string params mode', function() { -< expectTemplate('{{blog fun=(bork)}}') -< .withCompileOptions({ stringParams: true }) -< .withHelpers({ -< blog: function(options) { -< equals(options.hashTypes.fun, 'SubExpression'); -< return 'val is ' + options.hash.fun; -244,246d182 -< bork: function() { -< return 'BORK'; -< } -248c184 -< .toCompileTo('val is BORK'); ---- -> .toCompileTo(''); -251c187 -< it('subexpression functions on the context', function() { ---- -> it('subexpression functions on the context', () => { -254c190 -< bar: function() { ---- -> bar() { -256c192 -< } ---- -> }, -259c195 -< foo: function(val) { ---- -> foo(val) { -261c197 -< } ---- -> }, -266c202 -< it("subexpressions can't just be property lookups", function() { ---- -> it("subexpressions can't just be property lookups", () => { -269c205 -< bar: 'LOL' ---- -> bar: 'LOL', -272c208 -< foo: function(val) { ---- -> foo(val) { -274c210 -< } ---- -> }, diff --git a/packages/kbn-handlebars/.patches/utils.patch b/packages/kbn-handlebars/.patches/utils.patch deleted file mode 100644 index 8bd09ad0c9927..0000000000000 --- a/packages/kbn-handlebars/.patches/utils.patch +++ /dev/null @@ -1,108 +0,0 @@ -1,55c1,6 -< describe('utils', function() { -< describe('#SafeString', function() { -< it('constructing a safestring from a string and checking its type', function() { -< var safe = new Handlebars.SafeString('testing 1, 2, 3'); -< if (!(safe instanceof Handlebars.SafeString)) { -< throw new Error('Must be instance of SafeString'); -< } -< equals( -< safe.toString(), -< 'testing 1, 2, 3', -< 'SafeString is equivalent to its underlying string' -< ); -< }); -< -< it('it should not escape SafeString properties', function() { -< var name = new Handlebars.SafeString('Sean O'Malley'); -< -< expectTemplate('{{name}}') -< .withInput({ name: name }) -< .toCompileTo('Sean O'Malley'); -< }); -< }); -< -< describe('#escapeExpression', function() { -< it('shouhld escape html', function() { -< equals( -< Handlebars.Utils.escapeExpression('foo<&"\'>'), -< 'foo<&"'>' -< ); -< equals(Handlebars.Utils.escapeExpression('foo='), 'foo='); -< }); -< it('should not escape SafeString', function() { -< var string = new Handlebars.SafeString('foo<&"\'>'); -< equals(Handlebars.Utils.escapeExpression(string), 'foo<&"\'>'); -< -< var obj = { -< toHTML: function() { -< return 'foo<&"\'>'; -< } -< }; -< equals(Handlebars.Utils.escapeExpression(obj), 'foo<&"\'>'); -< }); -< it('should handle falsy', function() { -< equals(Handlebars.Utils.escapeExpression(''), ''); -< equals(Handlebars.Utils.escapeExpression(undefined), ''); -< equals(Handlebars.Utils.escapeExpression(null), ''); -< -< equals(Handlebars.Utils.escapeExpression(false), 'false'); -< equals(Handlebars.Utils.escapeExpression(0), '0'); -< }); -< it('should handle empty objects', function() { -< equals(Handlebars.Utils.escapeExpression({}), {}.toString()); -< equals(Handlebars.Utils.escapeExpression([]), [].toString()); -< }); -< }); ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -57,64c8,9 -< describe('#isEmpty', function() { -< it('should not be empty', function() { -< equals(Handlebars.Utils.isEmpty(undefined), true); -< equals(Handlebars.Utils.isEmpty(null), true); -< equals(Handlebars.Utils.isEmpty(false), true); -< equals(Handlebars.Utils.isEmpty(''), true); -< equals(Handlebars.Utils.isEmpty([]), true); -< }); ---- -> import Handlebars from '../..'; -> import { expectTemplate } from '../__jest__/test_bench'; -66,70c11,16 -< it('should be empty', function() { -< equals(Handlebars.Utils.isEmpty(0), false); -< equals(Handlebars.Utils.isEmpty([1]), false); -< equals(Handlebars.Utils.isEmpty('foo'), false); -< equals(Handlebars.Utils.isEmpty({ bar: 1 }), false); ---- -> describe('utils', function () { -> describe('#SafeString', function () { -> it('constructing a safestring from a string and checking its type', function () { -> const safe = new Handlebars.SafeString('testing 1, 2, 3'); -> expect(safe).toBeInstanceOf(Handlebars.SafeString); -> expect(safe.toString()).toEqual('testing 1, 2, 3'); -72,83d17 -< }); -< -< describe('#extend', function() { -< it('should ignore prototype values', function() { -< function A() { -< this.a = 1; -< } -< A.prototype.b = 4; -< -< var b = { b: 2 }; -< -< Handlebars.Utils.extend(b, new A()); -85,86c19,21 -< equals(b.a, 1); -< equals(b.b, 2); ---- -> it('it should not escape SafeString properties', function () { -> const name = new Handlebars.SafeString('Sean O'Malley'); -> expectTemplate('{{name}}').withInput({ name }).toCompileTo('Sean O'Malley'); diff --git a/packages/kbn-handlebars/.patches/whitespace-control.patch b/packages/kbn-handlebars/.patches/whitespace-control.patch deleted file mode 100644 index f973b0b5ad8d8..0000000000000 --- a/packages/kbn-handlebars/.patches/whitespace-control.patch +++ /dev/null @@ -1,186 +0,0 @@ -1,19c1,6 -< describe('whitespace control', function() { -< it('should strip whitespace around mustache calls', function() { -< var hash = { foo: 'bar<' }; -< -< expectTemplate(' {{~foo~}} ') -< .withInput(hash) -< .toCompileTo('bar<'); -< -< expectTemplate(' {{~foo}} ') -< .withInput(hash) -< .toCompileTo('bar< '); -< -< expectTemplate(' {{foo~}} ') -< .withInput(hash) -< .toCompileTo(' bar<'); -< -< expectTemplate(' {{~&foo~}} ') -< .withInput(hash) -< .toCompileTo('bar<'); ---- -> /* -> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), -> * and may include modifications made by Elasticsearch B.V. -> * Elasticsearch B.V. licenses this file to you under the MIT License. -> * See `packages/kbn-handlebars/LICENSE` for more information. -> */ -21,23c8 -< expectTemplate(' {{~{foo}~}} ') -< .withInput(hash) -< .toCompileTo('bar<'); ---- -> import { expectTemplate } from '../__jest__/test_bench'; -24a10,17 -> describe('whitespace control', () => { -> it('should strip whitespace around mustache calls', () => { -> const hash = { foo: 'bar<' }; -> expectTemplate(' {{~foo~}} ').withInput(hash).toCompileTo('bar<'); -> expectTemplate(' {{~foo}} ').withInput(hash).toCompileTo('bar< '); -> expectTemplate(' {{foo~}} ').withInput(hash).toCompileTo(' bar<'); -> expectTemplate(' {{~&foo~}} ').withInput(hash).toCompileTo('bar<'); -> expectTemplate(' {{~{foo}~}} ').withInput(hash).toCompileTo('bar<'); -28,42c21,23 -< describe('blocks', function() { -< it('should strip whitespace around simple block calls', function() { -< var hash = { foo: 'bar<' }; -< -< expectTemplate(' {{~#if foo~}} bar {{~/if~}} ') -< .withInput(hash) -< .toCompileTo('bar'); -< -< expectTemplate(' {{#if foo~}} bar {{/if~}} ') -< .withInput(hash) -< .toCompileTo(' bar '); -< -< expectTemplate(' {{~#if foo}} bar {{~/if}} ') -< .withInput(hash) -< .toCompileTo(' bar '); ---- -> describe('blocks', () => { -> it('should strip whitespace around simple block calls', () => { -> const hash = { foo: 'bar<' }; -44,46c25,28 -< expectTemplate(' {{#if foo}} bar {{/if}} ') -< .withInput(hash) -< .toCompileTo(' bar '); ---- -> expectTemplate(' {{~#if foo~}} bar {{~/if~}} ').withInput(hash).toCompileTo('bar'); -> expectTemplate(' {{#if foo~}} bar {{/if~}} ').withInput(hash).toCompileTo(' bar '); -> expectTemplate(' {{~#if foo}} bar {{~/if}} ').withInput(hash).toCompileTo(' bar '); -> expectTemplate(' {{#if foo}} bar {{/if}} ').withInput(hash).toCompileTo(' bar '); -57c39 -< it('should strip whitespace around inverse block calls', function() { ---- -> it('should strip whitespace around inverse block calls', () => { -59d40 -< -61d41 -< -63d42 -< -65,68c44 -< -< expectTemplate( -< ' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ' -< ).toCompileTo('bar'); ---- -> expectTemplate(' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ').toCompileTo('bar'); -71,88c47,48 -< it('should strip whitespace around complex block calls', function() { -< var hash = { foo: 'bar<' }; -< -< expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}') -< .withInput(hash) -< .toCompileTo('bar'); -< -< expectTemplate('{{#if foo~}} bar {{^~}} baz {{/if}}') -< .withInput(hash) -< .toCompileTo('bar '); -< -< expectTemplate('{{#if foo}} bar {{~^~}} baz {{~/if}}') -< .withInput(hash) -< .toCompileTo(' bar'); -< -< expectTemplate('{{#if foo}} bar {{^~}} baz {{/if}}') -< .withInput(hash) -< .toCompileTo(' bar '); ---- -> it('should strip whitespace around complex block calls', () => { -> const hash = { foo: 'bar<' }; -90,92c50,54 -< expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}') -< .withInput(hash) -< .toCompileTo('bar'); ---- -> expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}').withInput(hash).toCompileTo('bar'); -> expectTemplate('{{#if foo~}} bar {{^~}} baz {{/if}}').withInput(hash).toCompileTo('bar '); -> expectTemplate('{{#if foo}} bar {{~^~}} baz {{~/if}}').withInput(hash).toCompileTo(' bar'); -> expectTemplate('{{#if foo}} bar {{^~}} baz {{/if}}').withInput(hash).toCompileTo(' bar '); -> expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}').withInput(hash).toCompileTo('bar'); -94,96c56 -< expectTemplate( -< '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n' -< ) ---- -> expectTemplate('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n') -100,102c60 -< expectTemplate( -< '\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n' -< ) ---- -> expectTemplate('\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n') -106,109c64 -< expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}').toCompileTo( -< 'baz' -< ); -< ---- -> expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}').toCompileTo('baz'); -111,120c66,69 -< -< expectTemplate('{{#if foo~}} bar {{~^}} baz {{~/if}}').toCompileTo( -< ' baz' -< ); -< -< expectTemplate('{{#if foo~}} bar {{~^}} baz {{/if}}').toCompileTo( -< ' baz ' -< ); -< -< expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}').toCompileTo( ---- -> expectTemplate('{{#if foo~}} bar {{~^}} baz {{~/if}}').toCompileTo(' baz'); -> expectTemplate('{{#if foo~}} bar {{~^}} baz {{/if}}').toCompileTo(' baz '); -> expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}').toCompileTo('baz'); -> expectTemplate('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n').toCompileTo( -123,126d71 -< -< expectTemplate( -< '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n' -< ).toCompileTo('baz'); -130,152c75 -< it('should strip whitespace around partials', function() { -< expectTemplate('foo {{~> dude~}} ') -< .withPartials({ dude: 'bar' }) -< .toCompileTo('foobar'); -< -< expectTemplate('foo {{> dude~}} ') -< .withPartials({ dude: 'bar' }) -< .toCompileTo('foo bar'); -< -< expectTemplate('foo {{> dude}} ') -< .withPartials({ dude: 'bar' }) -< .toCompileTo('foo bar '); -< -< expectTemplate('foo\n {{~> dude}} ') -< .withPartials({ dude: 'bar' }) -< .toCompileTo('foobar'); -< -< expectTemplate('foo\n {{> dude}} ') -< .withPartials({ dude: 'bar' }) -< .toCompileTo('foo\n bar'); -< }); -< -< it('should only strip whitespace once', function() { ---- -> it('should only strip whitespace once', () => { diff --git a/packages/kbn-handlebars/README.md b/packages/kbn-handlebars/README.md index 69fdfcd83033a..8528d9803b16d 100644 --- a/packages/kbn-handlebars/README.md +++ b/packages/kbn-handlebars/README.md @@ -77,25 +77,21 @@ By default, each test will run both the original `handlebars` code and the modif ## Development -Some of the tests have been copied from the upstream `handlebars` project and modified to fit our use-case, test-suite, and coding conventions. They are all located under the `packages/kbn-handlebars/src/upstream` directory. To check if any of the copied files have received updates upstream that we might want to include in our copies, you can run the following script: +Some of the tests have been copied from the upstream `handlebars` project and modified to fit our use-case, test-suite, and coding conventions. They are all located under the `packages/kbn-handlebars/src/spec` directory. To check if any of the copied files have received updates upstream that we might want to include in our copies, you can run the following script: ```sh -./packages/kbn-handlebars/scripts/check_for_test_changes.sh +./packages/kbn-handlebars/scripts/check_for_upstream_updates.sh ``` -If the script outputs a diff for a given file, it means that this file has been updated. - -_Note: that this will look for changes in the `4.x` branch of the `handlebars.js` repo only. Changes in the `master` branch are ignored._ +_Note: This will look for changes in the `4.x` branch of the `handlebars.js` repo only. Changes in the `master` branch are ignored._ Once all updates have been manually merged with our versions of the files, run the following script to "lock" us into the new updates: ```sh -./packages/kbn-handlebars/scripts/update_test_patches.sh +./packages/kbn-handlebars/scripts/update_upstream_git_hash.sh ``` -This will update the `.patch` files inside the `packages/kbn-handlebars/.patches` directory. Make sure to commit those changes. - -_Note: If we manually make changes to our test files in the `upstream` directory, we need to run the `update_test_patches.sh` script as well._ +This will update file `packages/kbn-handlebars/src/spec/.upstream_git_hash`. Make sure to commit changes to this file as well. ## Debugging diff --git a/packages/kbn-handlebars/index.test.ts b/packages/kbn-handlebars/index.test.ts index 6159a65dbcb8f..7a3a2a5772c14 100644 --- a/packages/kbn-handlebars/index.test.ts +++ b/packages/kbn-handlebars/index.test.ts @@ -307,7 +307,37 @@ describe('blocks', () => { .toCompileTo(''); }); - it('should pass expected options to root decorator', () => { + it('should pass expected options to root decorator with no args', () => { + expectTemplate('{{*decorator}}') + .withDecorator('decorator', function (fn, props, container, options) { + expect(options).toMatchInlineSnapshot(` + Object { + "args": Array [], + "data": Object { + "root": Object { + "foo": "bar", + }, + }, + "hash": Object {}, + "loc": Object { + "end": Object { + "column": 14, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "name": "decorator", + } + `); + }) + .withInput({ foo: 'bar' }) + .toCompileTo(''); + }); + + it('should pass expected options to root decorator with one arg', () => { expectTemplate('{{*decorator foo}}') .withDecorator('decorator', function (fn, props, container, options) { expect(options).toMatchInlineSnapshot(` @@ -339,6 +369,163 @@ describe('blocks', () => { .toCompileTo(''); }); + describe('return values', () => { + for (const [desc, template, result] of [ + ['non-block', '{{*decorator}}cont{{*decorator}}ent', 'content'], + ['block', '{{#*decorator}}con{{/decorator}}tent', 'tent'], + ]) { + describe(desc, () => { + const falsy = [undefined, null, false, 0, '']; + const truthy = [true, 42, 'foo', {}]; + + // Falsy return values from decorators are simply ignored and the + // execution falls back to default behavior which is to render the + // other parts of the template. + for (const value of falsy) { + it(`falsy value (type ${typeof value}): ${JSON.stringify(value)}`, () => { + expectTemplate(template) + .withDecorator('decorator', () => value) + .toCompileTo(result); + }); + } + + // Truthy return values from decorators are expected to be functions + // and the program will attempt to call them. We expect an error to + // be thrown in this case. + for (const value of truthy) { + it(`non-falsy value (type ${typeof value}): ${JSON.stringify(value)}`, () => { + expectTemplate(template) + .withDecorator('decorator', () => value) + .toThrow('is not a function'); + }); + } + + // If the decorator return value is a custom function, its return + // value will be the final content of the template. + for (const value of [...falsy, ...truthy]) { + it(`function returning ${typeof value}: ${JSON.stringify(value)}`, () => { + expectTemplate(template) + .withDecorator('decorator', () => () => value) + .toCompileTo(value as string); + }); + } + }); + } + }); + + describe('custom return function should be called with expected arguments and its return value should be rendered in the template', () => { + it('root decorator', () => { + expectTemplate('{{*decorator}}world') + .withInput({ me: 'my' }) + .withDecorator( + 'decorator', + (fn): Handlebars.TemplateDelegate => + (context, options) => { + expect(context).toMatchInlineSnapshot(` + Object { + "me": "my", + } + `); + expect(options).toMatchInlineSnapshot(` + Object { + "decorators": Object { + "decorator": [Function], + }, + "helpers": Object {}, + } + `); + return `hello ${context.me} ${fn()}!`; + } + ) + .toCompileTo('hello my world!'); + }); + + it('decorator nested inside of array-helper', () => { + expectTemplate('{{#arr}}{{*decorator}}world{{/arr}}') + .withInput({ arr: ['my'] }) + .withDecorator( + 'decorator', + (fn): Handlebars.TemplateDelegate => + (context, options) => { + expect(context).toMatchInlineSnapshot(`"my"`); + expect(options).toMatchInlineSnapshot(` + Object { + "blockParams": Array [ + "my", + 0, + ], + "data": Object { + "_parent": Object { + "root": Object { + "arr": Array [ + "my", + ], + }, + }, + "first": true, + "index": 0, + "key": 0, + "last": true, + "root": Object { + "arr": Array [ + "my", + ], + }, + }, + } + `); + return `hello ${context} ${fn()}!`; + } + ) + .toCompileTo('hello my world!'); + }); + + it('decorator nested inside of custom helper', () => { + expectTemplate('{{#helper}}{{*decorator}}world{{/helper}}') + .withHelper('helper', function (options: Handlebars.HelperOptions) { + return options.fn('my', { foo: 'bar' } as any); + }) + .withDecorator( + 'decorator', + (fn): Handlebars.TemplateDelegate => + (context, options) => { + expect(context).toMatchInlineSnapshot(`"my"`); + expect(options).toMatchInlineSnapshot(` + Object { + "foo": "bar", + } + `); + return `hello ${context} ${fn()}!`; + } + ) + .toCompileTo('hello my world!'); + }); + }); + + it('should call multiple decorators in the same program body in the expected order and get the expected output', () => { + let decoratorCall = 0; + let progCall = 0; + expectTemplate('{{*decorator}}con{{*decorator}}tent') + .beforeRender(() => { + // ensure the counters are reset between EVAL/AST render calls + decoratorCall = 0; + progCall = 0; + }) + .withInput({ + decoratorCall: 0, + progCall: 0, + }) + .withDecorator('decorator', (fn) => { + const decoratorCallOrder = ++decoratorCall; + const ret: Handlebars.TemplateDelegate = () => { + const progCallOrder = ++progCall; + return `(decorator: ${decoratorCallOrder}, prog: ${progCallOrder}, fn: "${fn()}")`; + }; + return ret; + }) + .toCompileTo('(decorator: 2, prog: 1, fn: "(decorator: 1, prog: 2, fn: "content")")'); + }); + describe('registration', () => { beforeEach(() => { global.kbnHandlebarsEnv = Handlebars.create(); diff --git a/packages/kbn-handlebars/index.ts b/packages/kbn-handlebars/index.ts index da48f5e1475f7..2c63e014a3387 100644 --- a/packages/kbn-handlebars/index.ts +++ b/packages/kbn-handlebars/index.ts @@ -64,6 +64,13 @@ type ProcessableNodeWithPathPartsOrLiteral = ProcessableNode & { path: hbs.AST.PathExpression | hbs.AST.Literal; }; +interface Helper { + fn?: Handlebars.HelperDelegate; + context: any[]; + params: any[]; + options: AmbiguousHelperOptions; +} + export type NonBlockHelperOptions = Omit; export type AmbiguousHelperOptions = Handlebars.HelperOptions | NonBlockHelperOptions; @@ -290,7 +297,7 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor { render(context: any, options: ExtendedRuntimeOptions = {}): string { this.contexts = [context]; this.output = []; - this.runtimeOptions = options; + this.runtimeOptions = Object.assign({}, options); this.container.helpers = Object.assign(this.initialHelpers, options.helpers); this.container.decorators = Object.assign( this.initialDecorators, @@ -312,9 +319,46 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor { this.ast = Handlebars.parse(this.template!); } - this.accept(this.ast); + // The `defaultMain` function contains the default behavior: + // + // Generate a "program" function based on the root `Program` in the AST and + // call it. This will start the processing of all the child nodes in the + // AST. + const defaultMain: Handlebars.TemplateDelegate = (_context) => { + const prog = this.generateProgramFunction(this.ast!); + return prog(_context, this.runtimeOptions); + }; - return this.output.join(''); + // Run any decorators that might exist on the root: + // + // The `defaultMain` function is passed in, and if there are no root + // decorators, or if the decorators chooses to do so, the same function is + // returned from `processDecorators` and the default behavior is retained. + // + // Alternatively any of the root decorators might call the `defaultMain` + // function themselves, process its return value, and return a completely + // different `main` function. + const main = this.processDecorators(this.ast, defaultMain); + this.processedRootDecorators = true; + + // Call the `main` function and add the result to the final output. + const result = main(this.context, options); + + if (main === defaultMain) { + this.output.push(result); + return this.output.join(''); + } else { + // We normally expect the return value of `main` to be a string. However, + // if a decorator is used to override the `defaultMain` function, the + // return value can be any type. To match the upstream handlebars project + // behavior, we want the result of rendering the template to be the + // literal value returned by the decorator. + // + // Since the output array in this case always will be empty, we just + // return that single value instead of attempting to join all the array + // elements as strings. + return result; + } } // ********************************************** // @@ -323,11 +367,6 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor { Program(program: hbs.AST.Program) { this.blockParamNames.unshift(program.blockParams); - - // Run any decorators that might exist on the root - this.processDecorators(program, this.generateProgramFunction(program)); - this.processedRootDecorators = true; - super.Program(program); this.blockParamNames.shift(); } @@ -340,14 +379,14 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor { this.processStatementOrExpression(block); } - // This space intentionally left blank: We want to override the Visitor class implementation - // of this method, but since we handle decorators separately before traversing the nodes, we - // just want to make this a no-op. + // This space is intentionally left blank: We want to override the Visitor + // class implementation of this method, but since we handle decorators + // separately before traversing the nodes, we just want to make this a no-op. DecoratorBlock(decorator: hbs.AST.DecoratorBlock) {} - // This space intentionally left blank: We want to override the Visitor class implementation - // of this method, but since we handle decorators separately before traversing the nodes, we - // just want to make this a no-op. + // This space is intentionally left blank: We want to override the Visitor + // class implementation of this method, but since we handle decorators + // separately before traversing the nodes, we just want to make this a no-op. Decorator(decorator: hbs.AST.Decorator) {} SubExpression(sexpr: hbs.AST.SubExpression) { @@ -405,20 +444,23 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor { */ private processDecorators(program: hbs.AST.Program, prog: Handlebars.TemplateDelegate) { if (!this.processedDecoratorsForProgram.has(program)) { + this.processedDecoratorsForProgram.add(program); + const props = {}; for (const node of program.body) { if (isDecorator(node)) { - this.processDecorator(node, prog); + prog = this.processDecorator(node, prog, props); } } - this.processedDecoratorsForProgram.add(program); } + + return prog; } private processDecorator( decorator: hbs.AST.DecoratorBlock | hbs.AST.Decorator, - prog: Handlebars.TemplateDelegate + prog: Handlebars.TemplateDelegate, + props: Record ) { - const props = {}; const options = this.setupDecoratorOptions(decorator); const result = this.container.lookupProperty( @@ -426,7 +468,7 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor { options.name )(prog, props, this.container, options); - Object.assign(result || prog, props); + return Object.assign(result || prog, props); } private processStatementOrExpression(node: ProcessableNodeWithPathPartsOrLiteral) { @@ -590,77 +632,51 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor { } private processAmbiguousNode(node: ProcessableNodeWithPathParts) { - const invokeResult = this.invokeAmbiguous(node); - - if (isBlock(node)) { - const result = this.ambiguousBlockValue(node, invokeResult); - if (result != null) { - this.output.push(result); - } - } else { - if ( - (node as hbs.AST.MustacheStatement).escaped === false || - this.compileOptions.noEscape === true || - typeof invokeResult !== 'string' - ) { - this.output.push(invokeResult); - } else { - this.output.push(Handlebars.escapeExpression(invokeResult)); - } - } - } - - // This operation is used when an expression like `{{foo}}` - // is provided, but we don't know at compile-time whether it - // is a helper or a path. - // - // This operation emits more code than the other options, - // and can be avoided by passing the `knownHelpers` and - // `knownHelpersOnly` flags at compile-time. - private invokeAmbiguous(node: ProcessableNodeWithPathParts) { const name = node.path.parts[0]; const helper = this.setupHelper(node, name); + let { fn: helperFn } = helper; - const loc = helper.fn ? node.loc : node.path.loc; - helper.fn = helper.fn ?? this.resolveNodes(node.path)[0]; + const loc = helperFn ? node.loc : node.path.loc; + helperFn = helperFn ?? this.resolveNodes(node.path)[0]; - if (helper.fn === undefined) { + if (helperFn === undefined) { if (this.compileOptions.strict) { - helper.fn = this.container.strict(helper.context, name, loc); + helperFn = this.container.strict(helper.context, name, loc); } else { - helper.fn = + helperFn = helper.context != null ? this.container.lookupProperty(helper.context, name) : helper.context; - if (helper.fn == null) helper.fn = this.container.hooks.helperMissing; + if (helperFn == null) helperFn = this.container.hooks.helperMissing; } } - return typeof helper.fn === 'function' - ? helper.fn.call(helper.context, ...helper.params, helper.options) - : helper.fn; - } - - private ambiguousBlockValue(block: hbs.AST.BlockStatement, value: any) { - const name = block.path.parts[0]; - const helper = this.setupHelper(block, name); + const helperResult = + typeof helperFn === 'function' + ? helperFn.call(helper.context, ...helper.params, helper.options) + : helperFn; - if (!helper.fn) { - value = this.container.hooks.blockHelperMissing!.call(this.context, value, helper.options); + if (isBlock(node)) { + const result = helper.fn + ? helperResult + : this.container.hooks.blockHelperMissing!.call(this.context, helperResult, helper.options); + if (result != null) { + this.output.push(result); + } + } else { + if ( + (node as hbs.AST.MustacheStatement).escaped === false || + this.compileOptions.noEscape === true || + typeof helperResult !== 'string' + ) { + this.output.push(helperResult); + } else { + this.output.push(Handlebars.escapeExpression(helperResult)); + } } - - return value; } - private setupHelper( - node: ProcessableNode, - helperName: string - ): { - fn?: Handlebars.HelperDelegate; - context: any[]; - params: any[]; - options: AmbiguousHelperOptions; - } { + private setupHelper(node: ProcessableNode, helperName: string): Helper { return { fn: this.container.lookupProperty(this.container.helpers, helperName), context: this.context, @@ -683,6 +699,8 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor { } else { options.args = this.resolveNodes(decorator.params); } + } else { + options.args = []; } return options; @@ -701,13 +719,12 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor { }; if (isBlock(node)) { - // TODO: Is there a way in TypeScript to infer that `options` is `Handlebars.HelperOptions` inside this if-statement. If not, is there a way to just cast once? - (options as Handlebars.HelperOptions).fn = this.generateProgramFunction(node.program); - if (node.program) - this.processDecorators(node.program, (options as Handlebars.HelperOptions).fn); - (options as Handlebars.HelperOptions).inverse = this.generateProgramFunction(node.inverse); - if (node.inverse) - this.processDecorators(node.inverse, (options as Handlebars.HelperOptions).inverse); + (options as Handlebars.HelperOptions).fn = node.program + ? this.processDecorators(node.program, this.generateProgramFunction(node.program)) + : noop; + (options as Handlebars.HelperOptions).inverse = node.inverse + ? this.processDecorators(node.inverse, this.generateProgramFunction(node.inverse)) + : noop; } return options; diff --git a/packages/kbn-handlebars/scripts/check_for_test_changes.sh b/packages/kbn-handlebars/scripts/check_for_test_changes.sh deleted file mode 100755 index a9818505b707f..0000000000000 --- a/packages/kbn-handlebars/scripts/check_for_test_changes.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash - -set -e - -TMP=.tmp-handlebars - -# Try to detect Windows environment (I've not tested this!) -if [[ "$OSTYPE" == "msys" ]]; then - # Windows environment - DEVNULL=NUL -else - # Everything else (including Cygwin on Windows) - DEVNULL=/dev/null -fi - -function cleanup { - rm -fr $TMP -} - -trap cleanup EXIT - -rm -fr $TMP -mkdir $TMP - -echo "Cloning handlebars repo..." -git clone -q --depth 1 https://github.com/handlebars-lang/handlebars.js.git -b 4.x $TMP/handlebars - -files=(packages/kbn-handlebars/src/upstream/index.*.test.ts) - -for file in "${files[@]}" -do - tmp=${file#*.} # remove anything before first period - file=${tmp%.test.ts} # remove trailing .test.ts - - echo "Checking for changes to spec/$file.js..." - - set +e - diff -d --strip-trailing-cr $TMP/handlebars/spec/$file.js packages/kbn-handlebars/src/upstream/index.$file.test.ts > $TMP/$file.patch - error=$? - set -e - if [ $error -gt 1 ] - then - echo "The diff command encountered an unexpected error!" - exit $error - fi - - set +e - diff -d --strip-trailing-cr $TMP/$file.patch packages/kbn-handlebars/.patches/$file.patch > $DEVNULL - error=$? - set -e - if [ $error -gt 1 ] - then - echo "The diff command encountered an unexpected error!" - exit $error - elif [ $error -gt 0 ] - then - echo - echo "The following files contain unexpected differences:" - echo - echo " Upstream: spec/$file.js" - echo " Downstream: packages/kbn-handlebars/src/upstream/index.$file.test.ts" - echo - echo "This can happen if either the upstream or the downstream version has been" - echo "updated without our patch files being kept up to date." - echo - echo "To resolve this issue, do the following:" - echo - echo " 1. Check the '4.x' branch of the upstream git repository to see if the file" - echo " has been updated. If so, please ensure that our copy of the file is kept in" - echo " sync. You can view the recent upstream commits to this file here:" - echo - echo " https://github.com/handlebars-lang/handlebars.js/commits/4.x/spec/$file.js" - echo - echo " 2. Update our patch files by running the following script. This is also" - echo " necessary even if it's only the downstream file that has been updated:" - echo - echo " ./packages/kbn-handlebars/scripts/update_test_patches.sh $file" - echo - echo " 3. Commit the changes to the updated patch file and execute this script again" - echo " until everything passes:" - echo - echo " ./packages/kbn-handlebars/scripts/check_for_test_changes.sh" - echo - exit $error - fi -done - -echo "No changes found :)" diff --git a/packages/kbn-handlebars/scripts/check_for_upstream_updates.sh b/packages/kbn-handlebars/scripts/check_for_upstream_updates.sh new file mode 100755 index 0000000000000..73f7376ab4312 --- /dev/null +++ b/packages/kbn-handlebars/scripts/check_for_upstream_updates.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +set -e + +TMP=.tmp-handlebars +HASH_FILE=packages/kbn-handlebars/src/spec/.upstream_git_hash + +function cleanup { + rm -fr $TMP +} + +trap cleanup EXIT + +rm -fr $TMP +mkdir $TMP + +echo "Cloning handlebars repo..." +git clone -q --depth 1 https://github.com/handlebars-lang/handlebars.js.git -b 4.x $TMP + +echo "Looking for updates..." +hash=$(git -C $TMP rev-parse HEAD) +expected_hash=$(cat $HASH_FILE) + +if [ "$hash" = "$expected_hash" ]; then + echo "You're all up to date :)" +else + echo + echo "New changes has been committed to the '4.x' branch in the upstream git repository" + echo + echo "To resolve this issue, do the following:" + echo + echo " 1. Investigate the commits in the '4.x' branch of the upstream git repository." + echo " If files inside the 'spec' folder has been updated, sync those updates with" + echo " our local versions of these files (located in" + echo " 'packages/kbn-handlebars/src/spec')." + echo + echo " https://github.com/handlebars-lang/handlebars.js/compare/$hash...4.x" + echo + echo " 2. Execute the following script and commit the updated '$HASH_FILE'" + echo " file including any changes you made to our own spec files." + echo + echo " ./packages/kbn-handlebars/scripts/update_upstream_git_hash.sh" + echo + exit 1 +fi diff --git a/packages/kbn-handlebars/scripts/update_test_patches.sh b/packages/kbn-handlebars/scripts/update_test_patches.sh deleted file mode 100755 index 233ca9e0f25ba..0000000000000 --- a/packages/kbn-handlebars/scripts/update_test_patches.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -set -e - -TMP=.tmp-handlebars - -function cleanup { - rm -fr $TMP -} - -trap cleanup EXIT - -rm -fr $TMP -mkdir $TMP - -echo "Cloning handlebars repo..." -git clone -q --depth 1 https://github.com/handlebars-lang/handlebars.js.git -b 4.x $TMP/handlebars - -if [ -z "$1" ] -then - # No argument given: Update all patch files - files=(packages/kbn-handlebars/src/upstream/index.*.test.ts) -else - # Argument detected: Update only the requested patch file - files=(packages/kbn-handlebars/src/upstream/index.$1.test.ts) -fi - -for file in "${files[@]}" -do - tmp=${file#*.} # remove anything before first period - file=${tmp%.test.ts} # remove trailing .test.ts - - echo "Overwriting stored patch file for spec/$file.js..." - set +e - diff -d --strip-trailing-cr $TMP/handlebars/spec/$file.js packages/kbn-handlebars/src/upstream/index.$file.test.ts > packages/kbn-handlebars/.patches/$file.patch - set -e -done - -echo "All patches updated :)" diff --git a/packages/kbn-handlebars/scripts/update_upstream_git_hash.sh b/packages/kbn-handlebars/scripts/update_upstream_git_hash.sh new file mode 100755 index 0000000000000..52cc39e0d7bb3 --- /dev/null +++ b/packages/kbn-handlebars/scripts/update_upstream_git_hash.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +set -e + +TMP=.tmp-handlebars +HASH_FILE=packages/kbn-handlebars/src/spec/.upstream_git_hash + +function cleanup { + rm -fr $TMP +} + +trap cleanup EXIT + +rm -fr $TMP +mkdir $TMP + +echo "Cloning handlebars repo..." +git clone -q --depth 1 https://github.com/handlebars-lang/handlebars.js.git -b 4.x $TMP + +echo "Updating hash file..." +git -C $TMP rev-parse HEAD | tr -d '\n' > $HASH_FILE +git add $HASH_FILE + +echo "Done! - Don't forget to commit any changes to $HASH_FILE" diff --git a/packages/kbn-handlebars/src/__jest__/test_bench.ts b/packages/kbn-handlebars/src/__jest__/test_bench.ts index 4aaac2b52bd73..ffbf8b1fe84f5 100644 --- a/packages/kbn-handlebars/src/__jest__/test_bench.ts +++ b/packages/kbn-handlebars/src/__jest__/test_bench.ts @@ -38,6 +38,7 @@ export function forEachCompileFunctionName( class HandlebarsTestBench { private template: string; private options: TestOptions; + private beforeRenderFn: Function = () => {}; private compileOptions?: ExtendedCompileOptions; private runtimeOptions?: ExtendedRuntimeOptions; private helpers: { [name: string]: Handlebars.HelperDelegate | undefined } = {}; @@ -49,6 +50,11 @@ class HandlebarsTestBench { this.options = options; } + beforeRender(fn: Function) { + this.beforeRenderFn = fn; + return this; + } + withCompileOptions(compileOptions?: ExtendedCompileOptions) { this.compileOptions = compileOptions; return this; @@ -147,6 +153,8 @@ class HandlebarsTestBench { this.runtimeOptions ); + this.beforeRenderFn(); + return renderEval(this.input, runtimeOptions); } @@ -161,6 +169,8 @@ class HandlebarsTestBench { this.runtimeOptions ); + this.beforeRenderFn(); + return renderAST(this.input, runtimeOptions); } diff --git a/packages/kbn-handlebars/src/spec/.upstream_git_hash b/packages/kbn-handlebars/src/spec/.upstream_git_hash new file mode 100644 index 0000000000000..5a6b183166d46 --- /dev/null +++ b/packages/kbn-handlebars/src/spec/.upstream_git_hash @@ -0,0 +1 @@ +c65c6cce3f626e4896a9d59250f0908be695adae \ No newline at end of file diff --git a/packages/kbn-handlebars/src/upstream/index.basic.test.ts b/packages/kbn-handlebars/src/spec/index.basic.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.basic.test.ts rename to packages/kbn-handlebars/src/spec/index.basic.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.blocks.test.ts b/packages/kbn-handlebars/src/spec/index.blocks.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.blocks.test.ts rename to packages/kbn-handlebars/src/spec/index.blocks.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.builtins.test.ts b/packages/kbn-handlebars/src/spec/index.builtins.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.builtins.test.ts rename to packages/kbn-handlebars/src/spec/index.builtins.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.compiler.test.ts b/packages/kbn-handlebars/src/spec/index.compiler.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.compiler.test.ts rename to packages/kbn-handlebars/src/spec/index.compiler.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.data.test.ts b/packages/kbn-handlebars/src/spec/index.data.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.data.test.ts rename to packages/kbn-handlebars/src/spec/index.data.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.helpers.test.ts b/packages/kbn-handlebars/src/spec/index.helpers.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.helpers.test.ts rename to packages/kbn-handlebars/src/spec/index.helpers.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.regressions.test.ts b/packages/kbn-handlebars/src/spec/index.regressions.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.regressions.test.ts rename to packages/kbn-handlebars/src/spec/index.regressions.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.security.test.ts b/packages/kbn-handlebars/src/spec/index.security.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.security.test.ts rename to packages/kbn-handlebars/src/spec/index.security.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.strict.test.ts b/packages/kbn-handlebars/src/spec/index.strict.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.strict.test.ts rename to packages/kbn-handlebars/src/spec/index.strict.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.subexpressions.test.ts b/packages/kbn-handlebars/src/spec/index.subexpressions.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.subexpressions.test.ts rename to packages/kbn-handlebars/src/spec/index.subexpressions.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.utils.test.ts b/packages/kbn-handlebars/src/spec/index.utils.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.utils.test.ts rename to packages/kbn-handlebars/src/spec/index.utils.test.ts diff --git a/packages/kbn-handlebars/src/upstream/index.whitespace-control.test.ts b/packages/kbn-handlebars/src/spec/index.whitespace_control.test.ts similarity index 100% rename from packages/kbn-handlebars/src/upstream/index.whitespace-control.test.ts rename to packages/kbn-handlebars/src/spec/index.whitespace_control.test.ts diff --git a/packages/shared-ux/code_editor/impl/kibana.jsonc b/packages/shared-ux/code_editor/impl/kibana.jsonc index d00f7a078b55e..d66e88d40710e 100644 --- a/packages/shared-ux/code_editor/impl/kibana.jsonc +++ b/packages/shared-ux/code_editor/impl/kibana.jsonc @@ -1,5 +1,5 @@ { "type": "shared-common", "id": "@kbn/code-editor", - "owner": "@elastic/shared-ux" + "owner": "@elastic/appex-sharedux" } diff --git a/packages/shared-ux/code_editor/mocks/kibana.jsonc b/packages/shared-ux/code_editor/mocks/kibana.jsonc index d8d52615dc5be..dd258cfd76853 100644 --- a/packages/shared-ux/code_editor/mocks/kibana.jsonc +++ b/packages/shared-ux/code_editor/mocks/kibana.jsonc @@ -1,5 +1,5 @@ { "type": "shared-common", "id": "@kbn/code-editor-mocks", - "owner": "@elastic/shared-ux" + "owner": "@elastic/appex-sharedux" } diff --git a/packages/shared-ux/code_editor/types/kibana.jsonc b/packages/shared-ux/code_editor/types/kibana.jsonc index c4fa126fd5da1..a4d4fb37dba34 100644 --- a/packages/shared-ux/code_editor/types/kibana.jsonc +++ b/packages/shared-ux/code_editor/types/kibana.jsonc @@ -1,5 +1,5 @@ { "type": "shared-common", "id": "@kbn/code-editor-types", - "owner": "@elastic/shared-ux" + "owner": "@elastic/appex-sharedux" } diff --git a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.test.tsx b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.test.tsx index 4bd3fca6cb902..1f657f642fc47 100644 --- a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.test.tsx +++ b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.test.tsx @@ -27,6 +27,7 @@ describe('AnalyticsNoDataPageComponent', () => { ); @@ -50,6 +51,7 @@ describe('AnalyticsNoDataPageComponent', () => { onDataViewCreated={onDataViewCreated} kibanaGuideDocLink={'http://www.test.com'} allowAdHocDataView={true} + showPlainSpinner={false} /> ); diff --git a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.tsx b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.tsx index fe607b70120df..d67cb082f5539 100644 --- a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.tsx +++ b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.component.tsx @@ -19,6 +19,8 @@ export interface Props { onDataViewCreated: (dataView: unknown) => void; /** if set to true allows creation of an ad-hoc dataview from data view editor */ allowAdHocDataView?: boolean; + /** if the kibana instance is customly branded */ + showPlainSpinner: boolean; } const solution = i18n.translate('sharedUXPackages.noDataConfig.analytics', { @@ -47,6 +49,7 @@ export const AnalyticsNoDataPage = ({ kibanaGuideDocLink, onDataViewCreated, allowAdHocDataView, + showPlainSpinner, }: Props) => { const noDataConfig = { solution, @@ -61,5 +64,10 @@ export const AnalyticsNoDataPage = ({ }, docsLink: kibanaGuideDocLink, }; - return ; + + return ( + + ); }; diff --git a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.test.tsx b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.test.tsx index 996b9d062becf..c73f61e6c0e82 100644 --- a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.test.tsx +++ b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.test.tsx @@ -9,7 +9,10 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { getAnalyticsNoDataPageServicesMock } from '@kbn/shared-ux-page-analytics-no-data-mocks'; +import { + getAnalyticsNoDataPageServicesMock, + getAnalyticsNoDataPageServicesMockWithCustomBranding, +} from '@kbn/shared-ux-page-analytics-no-data-mocks'; import { AnalyticsNoDataPageProvider } from './services'; import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.component'; @@ -19,6 +22,7 @@ describe('AnalyticsNoDataPage', () => { const onDataViewCreated = jest.fn(); const services = getAnalyticsNoDataPageServicesMock(); + const servicesWithCustomBranding = getAnalyticsNoDataPageServicesMockWithCustomBranding(); afterAll(() => { jest.resetAllMocks(); @@ -38,4 +42,15 @@ describe('AnalyticsNoDataPage', () => { expect(component.find(Component).props().onDataViewCreated).toBe(onDataViewCreated); expect(component.find(Component).props().allowAdHocDataView).toBe(true); }); + + it('passes correct boolean value to showPlainSpinner', () => { + const component = mountWithIntl( + + + + ); + + expect(component.find(Component).length).toBe(1); + expect(component.find(Component).props().showPlainSpinner).toBe(true); + }); }); diff --git a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.tsx b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.tsx index df1fc2486c1b3..9b600c374dd02 100644 --- a/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.tsx +++ b/packages/shared-ux/page/analytics_no_data/impl/src/analytics_no_data_page.tsx @@ -8,6 +8,7 @@ import React from 'react'; import type { AnalyticsNoDataPageProps } from '@kbn/shared-ux-page-analytics-no-data-types'; +import useObservable from 'react-use/lib/useObservable'; import { useServices } from './services'; import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.component'; @@ -20,7 +21,9 @@ export const AnalyticsNoDataPage = ({ allowAdHocDataView, }: AnalyticsNoDataPageProps) => { const services = useServices(); - const { kibanaGuideDocLink } = services; + const { kibanaGuideDocLink, customBranding } = services; + const { hasCustomBranding$ } = customBranding; + const showPlainSpinner = useObservable(hasCustomBranding$) ?? false; return ( ); diff --git a/packages/shared-ux/page/analytics_no_data/impl/src/services.tsx b/packages/shared-ux/page/analytics_no_data/impl/src/services.tsx index bd486be8f8976..991893aeca501 100644 --- a/packages/shared-ux/page/analytics_no_data/impl/src/services.tsx +++ b/packages/shared-ux/page/analytics_no_data/impl/src/services.tsx @@ -27,10 +27,10 @@ export const AnalyticsNoDataPageProvider: FC = ({ children, ...services }) => { - const { kibanaGuideDocLink } = services; + const { kibanaGuideDocLink, customBranding } = services; return ( - + {children} ); @@ -45,8 +45,10 @@ export const AnalyticsNoDataPageKibanaProvider: FC { const value: Services = { kibanaGuideDocLink: dependencies.coreStart.docLinks.links.kibana.guide, + customBranding: { + hasCustomBranding$: dependencies.coreStart.customBranding.hasCustomBranding$, + }, }; - return ( {children} diff --git a/packages/shared-ux/page/analytics_no_data/mocks/index.ts b/packages/shared-ux/page/analytics_no_data/mocks/index.ts index cc73dc378452b..1f1ac86e9d247 100644 --- a/packages/shared-ux/page/analytics_no_data/mocks/index.ts +++ b/packages/shared-ux/page/analytics_no_data/mocks/index.ts @@ -7,6 +7,7 @@ */ export { getServicesMock as getAnalyticsNoDataPageServicesMock } from './src/jest'; +export { getServicesMockCustomBranding as getAnalyticsNoDataPageServicesMockWithCustomBranding } from './src/jest'; export { StorybookMock as AnalyticsNoDataPageStorybookMock } from './src/storybook'; export type { Params as AnalyticsNoDataPageStorybookParams } from './src/storybook'; diff --git a/packages/shared-ux/page/analytics_no_data/mocks/src/jest.ts b/packages/shared-ux/page/analytics_no_data/mocks/src/jest.ts index 29a79c40054aa..98885d55ba47d 100644 --- a/packages/shared-ux/page/analytics_no_data/mocks/src/jest.ts +++ b/packages/shared-ux/page/analytics_no_data/mocks/src/jest.ts @@ -8,11 +8,24 @@ import type { AnalyticsNoDataPageServices } from '@kbn/shared-ux-page-analytics-no-data-types'; import { getKibanaNoDataPageServicesMock } from '@kbn/shared-ux-page-kibana-no-data-mocks'; +import { of } from 'rxjs'; export const getServicesMock = () => { const services: AnalyticsNoDataPageServices = { ...getKibanaNoDataPageServicesMock(), kibanaGuideDocLink: 'Kibana guide', + customBranding: { hasCustomBranding$: of(false) }, + }; + + return services; +}; + +export const getServicesMockCustomBranding = () => { + const services: AnalyticsNoDataPageServices = { + ...getKibanaNoDataPageServicesMock(), + // this mock will have custom branding set to true + customBranding: { hasCustomBranding$: of(true) }, + kibanaGuideDocLink: 'Kibana guide', }; return services; diff --git a/packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts b/packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts index c909795647c4e..86bf25dbde9e9 100644 --- a/packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts +++ b/packages/shared-ux/page/analytics_no_data/mocks/src/storybook.ts @@ -15,8 +15,9 @@ import type { AnalyticsNoDataPageServices, AnalyticsNoDataPageProps, } from '@kbn/shared-ux-page-analytics-no-data-types'; +import { of } from 'rxjs'; -type ServiceArguments = Pick; +type ServiceArguments = Pick; export type Params = ArgumentParams<{}, ServiceArguments> & KibanaNoDataPageStorybookParams; @@ -34,6 +35,12 @@ export class StorybookMock extends AbstractStorybookMock< control: 'text', defaultValue: 'Kibana guide', }, + customBranding: { + hasCustomBranding$: { + control: 'boolean', + defaultValue: false, + }, + }, }; dependencies = [kibanaNoDataMock]; @@ -41,6 +48,9 @@ export class StorybookMock extends AbstractStorybookMock< getServices(params: Params): AnalyticsNoDataPageServices { return { kibanaGuideDocLink: 'Kibana guide', + customBranding: { + hasCustomBranding$: of(false), + }, ...kibanaNoDataMock.getServices(params), }; } diff --git a/packages/shared-ux/page/analytics_no_data/types/index.d.ts b/packages/shared-ux/page/analytics_no_data/types/index.d.ts index d4021360bea23..4e54315f071dd 100644 --- a/packages/shared-ux/page/analytics_no_data/types/index.d.ts +++ b/packages/shared-ux/page/analytics_no_data/types/index.d.ts @@ -9,12 +9,14 @@ import { KibanaNoDataPageServices, KibanaNoDataPageKibanaDependencies, } from '@kbn/shared-ux-page-kibana-no-data-types'; +import { Observable } from 'rxjs'; /** * A list of services that are consumed by this component. */ export interface Services { kibanaGuideDocLink: string; + customBranding: { hasCustomBranding$: Observable }; } /** @@ -31,6 +33,9 @@ export interface KibanaDependencies { }; }; }; + customBranding: { + hasCustomBranding$: Observable; + }; }; } diff --git a/packages/shared-ux/page/kibana_no_data/impl/src/kibana_no_data_page.test.tsx b/packages/shared-ux/page/kibana_no_data/impl/src/kibana_no_data_page.test.tsx index c15a5c061dd1b..a3484719a49ed 100644 --- a/packages/shared-ux/page/kibana_no_data/impl/src/kibana_no_data_page.test.tsx +++ b/packages/shared-ux/page/kibana_no_data/impl/src/kibana_no_data_page.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; -import { EuiLoadingElastic } from '@elastic/eui'; +import { EuiLoadingElastic, EuiLoadingSpinner } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { NoDataViewsPrompt } from '@kbn/shared-ux-prompt-no-data-views'; import { NoDataConfigPage } from '@kbn/shared-ux-page-no-data-config'; @@ -46,7 +46,7 @@ describe('Kibana No Data Page', () => { const services = getKibanaNoDataPageServicesMock(config); const component = mountWithIntl( - + ); @@ -61,7 +61,11 @@ describe('Kibana No Data Page', () => { const services = getKibanaNoDataPageServicesMock({ ...config, hasESData: true }); const component = mountWithIntl( - + ); @@ -86,7 +90,11 @@ describe('Kibana No Data Page', () => { const component = mountWithIntl( - + ); @@ -96,4 +104,15 @@ describe('Kibana No Data Page', () => { expect(component.find(NoDataViewsPrompt).length).toBe(0); expect(component.find(NoDataConfigPage).length).toBe(0); }); + + test('shows EuiLoadingSpinner vs EuiLoadingElastic for custom branding', () => { + const services = getKibanaNoDataPageServicesMock(config); + const component = mountWithIntl( + + + + ); + expect(component.find(EuiLoadingSpinner).length).toBe(1); + expect(component.find(EuiLoadingElastic).length).toBe(0); + }); }); diff --git a/packages/shared-ux/page/kibana_no_data/impl/src/kibana_no_data_page.tsx b/packages/shared-ux/page/kibana_no_data/impl/src/kibana_no_data_page.tsx index c3fbccd3a60fb..2773184b087bb 100644 --- a/packages/shared-ux/page/kibana_no_data/impl/src/kibana_no_data_page.tsx +++ b/packages/shared-ux/page/kibana_no_data/impl/src/kibana_no_data_page.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import React, { useEffect, useState } from 'react'; -import { EuiLoadingElastic } from '@elastic/eui'; +import { EuiLoadingElastic, EuiLoadingSpinner } from '@elastic/eui'; import { NoDataConfigPage } from '@kbn/shared-ux-page-no-data-config'; import { NoDataViewsPrompt } from '@kbn/shared-ux-prompt-no-data-views'; import { KibanaNoDataPageProps } from '@kbn/shared-ux-page-kibana-no-data-types'; @@ -20,6 +20,7 @@ export const KibanaNoDataPage = ({ onDataViewCreated, noDataConfig, allowAdHocDataView, + showPlainSpinner, }: KibanaNoDataPageProps) => { // These hooks are temporary, until this component is moved to a package. const services = useServices(); @@ -43,7 +44,11 @@ export const KibanaNoDataPage = ({ }, [hasESData, hasUserDataView]); if (isLoading) { - return ; + return showPlainSpinner ? ( + + ) : ( + + ); } if (!hasUserDataViews && dataExists) { diff --git a/packages/shared-ux/page/kibana_no_data/mocks/src/jest.ts b/packages/shared-ux/page/kibana_no_data/mocks/src/jest.ts index 5f2f6b309e56c..dc46f22286646 100644 --- a/packages/shared-ux/page/kibana_no_data/mocks/src/jest.ts +++ b/packages/shared-ux/page/kibana_no_data/mocks/src/jest.ts @@ -13,11 +13,13 @@ import { getNoDataViewsPromptServicesMock } from '@kbn/shared-ux-prompt-no-data- interface Params { hasESData: boolean; hasUserDataView: boolean; + showPlainSpinner: boolean; } const defaultParams = { hasESData: true, hasUserDataView: true, + showPlainSpinner: false, }; /** diff --git a/packages/shared-ux/page/kibana_no_data/mocks/src/storybook.ts b/packages/shared-ux/page/kibana_no_data/mocks/src/storybook.ts index 1f4a7453e59b6..10cc9a0f40961 100644 --- a/packages/shared-ux/page/kibana_no_data/mocks/src/storybook.ts +++ b/packages/shared-ux/page/kibana_no_data/mocks/src/storybook.ts @@ -79,7 +79,11 @@ export class StorybookMock extends AbstractStorybookMock< docsLink: 'http://docs.elastic.dev', }; - return { noDataConfig, onDataViewCreated: action('onDataViewCreated') }; + return { + showPlainSpinner: false, + noDataConfig, + onDataViewCreated: action('onDataViewCreated'), + }; } getServices(params: Params): KibanaNoDataPageServices { diff --git a/packages/shared-ux/page/kibana_no_data/types/index.d.ts b/packages/shared-ux/page/kibana_no_data/types/index.d.ts index 1cce51f372021..ff9b4d845f597 100644 --- a/packages/shared-ux/page/kibana_no_data/types/index.d.ts +++ b/packages/shared-ux/page/kibana_no_data/types/index.d.ts @@ -57,4 +57,6 @@ export interface KibanaNoDataPageProps { noDataConfig: NoDataPageProps; /** if set to true allows creation of an ad-hoc dataview from data view editor */ allowAdHocDataView?: boolean; + /** Set to true if the kibana is customly branded */ + showPlainSpinner: boolean; } diff --git a/packages/shared-ux/prompt/not_found/src/not_found_prompt.tsx b/packages/shared-ux/prompt/not_found/src/not_found_prompt.tsx index bd607e1beae07..116deb7098c39 100644 --- a/packages/shared-ux/prompt/not_found/src/not_found_prompt.tsx +++ b/packages/shared-ux/prompt/not_found/src/not_found_prompt.tsx @@ -32,12 +32,14 @@ const NOT_FOUND_GO_BACK = i18n.translate('sharedUXPackages.prompt.errors.notFoun interface NotFoundProps { /** Array of buttons, links and other actions to show at the bottom of the `EuiEmptyPrompt`. Defaults to a "Back" button. */ actions?: EuiEmptyPromptProps['actions']; + title?: EuiEmptyPromptProps['title'] | string; + body?: EuiEmptyPromptProps['body']; } /** * Predefined `EuiEmptyPrompt` for 404 pages. */ -export const NotFoundPrompt = ({ actions }: NotFoundProps) => { +export const NotFoundPrompt = ({ actions, title, body }: NotFoundProps) => { const { colorMode } = useEuiTheme(); const [imageSrc, setImageSrc] = useState(); const goBack = useCallback(() => history.back(), []); @@ -71,8 +73,8 @@ export const NotFoundPrompt = ({ actions }: NotFoundProps) => { color="subdued" titleSize="m" icon={icon} - title={

      {NOT_FOUND_TITLE}

      } - body={NOT_FOUND_BODY} + title={typeof title === 'string' || !title ?

      {title ?? NOT_FOUND_TITLE}

      : title} + body={body ?? NOT_FOUND_BODY} actions={actions ?? DEFAULT_ACTIONS} /> ); diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 106a057079dcb..991b0c4bc1b1a 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -209,6 +209,8 @@ export type { export type { ToastsApi } from '@kbn/core-notifications-browser-internal'; +export type { CustomBrandingStart, CustomBrandingSetup } from '@kbn/core-custom-branding-browser'; + export type { ThemeServiceSetup, ThemeServiceStart, CoreTheme } from '@kbn/core-theme-browser'; export type { diff --git a/src/core/public/styles/rendering/_base.scss b/src/core/public/styles/rendering/_base.scss index b64595e69a791..9d4296ca3b4ef 100644 --- a/src/core/public/styles/rendering/_base.scss +++ b/src/core/public/styles/rendering/_base.scss @@ -62,6 +62,8 @@ @include kbnAffordForHeader($kbnHeaderOffset); &.kbnBody--hasHeaderBanner { + padding-top: $kbnHeaderBannerHeight; + @include kbnAffordForHeader($kbnHeaderOffsetWithBanner); // Prevents banners from covering full screen data grids diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 22398843324d2..3ed069403d8f3 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -530,3 +530,5 @@ export type { PublicHttpServiceSetup as HttpServiceSetup, HttpServiceSetup as BaseHttpServiceSetup, }; + +export type { CustomBrandingSetup } from '@kbn/core-custom-branding-server'; diff --git a/src/core/tsconfig.json b/src/core/tsconfig.json index 5ce6fba92e138..1791e14334243 100644 --- a/src/core/tsconfig.json +++ b/src/core/tsconfig.json @@ -146,6 +146,8 @@ "@kbn/core-mount-utils-browser", "@kbn/core-execution-context-browser", "@kbn/core-lifecycle-browser", + "@kbn/core-custom-branding-browser", + "@kbn/core-custom-branding-server", ], "exclude": [ "target/**/*", diff --git a/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx b/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx index 603a36bb3d59b..fb04a3187c72c 100644 --- a/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx +++ b/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx @@ -25,6 +25,7 @@ export const DashboardAppNoDataPage = ({ dataViewEditor, http: { basePath }, documentationLinks: { indexPatternsDocLink, kibanaGuideDocLink }, + customBranding, } = pluginServices.getServices(); const analyticsServices = { @@ -37,6 +38,9 @@ export const DashboardAppNoDataPage = ({ }, application, http: { basePath }, + customBranding: { + hasCustomBranding$: customBranding.hasCustomBranding$, + }, }, dataViews, dataViewEditor, diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index fa457c425440e..1d562877a5dea 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -49,6 +49,7 @@ import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plu import type { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; import { DashboardContainerFactoryDefinition } from './dashboard_container/embeddable/dashboard_container_factory'; import { type DashboardAppLocator, @@ -97,6 +98,7 @@ export interface DashboardStartDependencies { urlForwarding: UrlForwardingStart; usageCollection?: UsageCollectionStart; visualizations: VisualizationsStart; + customBranding: CustomBrandingStart; } export interface DashboardSetup { diff --git a/src/plugins/dashboard/public/services/custom_branding/custom_branding.stub.ts b/src/plugins/dashboard/public/services/custom_branding/custom_branding.stub.ts new file mode 100644 index 0000000000000..5496c29b760f9 --- /dev/null +++ b/src/plugins/dashboard/public/services/custom_branding/custom_branding.stub.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { coreMock } from '@kbn/core/public/mocks'; +import { DashboardCustomBrandingService } from './types'; + +type CustomBrandingServiceFactory = PluginServiceFactory; + +export const customBrandingServiceFactory: CustomBrandingServiceFactory = () => { + const pluginMock = coreMock.createStart(); + return { + hasCustomBranding$: pluginMock.customBranding.hasCustomBranding$, + }; +}; diff --git a/src/plugins/dashboard/public/services/custom_branding/custom_branding_service.ts b/src/plugins/dashboard/public/services/custom_branding/custom_branding_service.ts new file mode 100644 index 0000000000000..659a669a5bda1 --- /dev/null +++ b/src/plugins/dashboard/public/services/custom_branding/custom_branding_service.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { DashboardStartDependencies } from '../../plugin'; +import { DashboardCustomBrandingService } from './types'; + +export type CustomBrandingServiceFactory = KibanaPluginServiceFactory< + DashboardCustomBrandingService, + DashboardStartDependencies +>; + +export const customBrandingServiceFactory: CustomBrandingServiceFactory = ({ coreStart }) => { + const { customBranding } = coreStart; + return { + hasCustomBranding$: customBranding.hasCustomBranding$, + }; +}; diff --git a/src/plugins/dashboard/public/services/custom_branding/types.ts b/src/plugins/dashboard/public/services/custom_branding/types.ts new file mode 100644 index 0000000000000..7e7e88bb15a7a --- /dev/null +++ b/src/plugins/dashboard/public/services/custom_branding/types.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; + +export interface DashboardCustomBrandingService { + hasCustomBranding$: CustomBrandingStart['hasCustomBranding$']; +} diff --git a/src/plugins/dashboard/public/services/plugin_services.stub.ts b/src/plugins/dashboard/public/services/plugin_services.stub.ts index b8c39909dd61a..eabe85288687a 100644 --- a/src/plugins/dashboard/public/services/plugin_services.stub.ts +++ b/src/plugins/dashboard/public/services/plugin_services.stub.ts @@ -38,6 +38,7 @@ import { spacesServiceFactory } from './spaces/spaces.stub'; import { urlForwardingServiceFactory } from './url_forwarding/url_fowarding.stub'; import { visualizationsServiceFactory } from './visualizations/visualizations.stub'; import { dashboardSavedObjectServiceFactory } from './dashboard_saved_object/dashboard_saved_object.stub'; +import { customBrandingServiceFactory } from './custom_branding/custom_branding.stub'; export const providers: PluginServiceProviders = { dashboardSavedObject: new PluginServiceProvider(dashboardSavedObjectServiceFactory), @@ -64,6 +65,7 @@ export const providers: PluginServiceProviders = { urlForwarding: new PluginServiceProvider(urlForwardingServiceFactory), usageCollection: new PluginServiceProvider(usageCollectionServiceFactory), visualizations: new PluginServiceProvider(visualizationsServiceFactory), + customBranding: new PluginServiceProvider(customBrandingServiceFactory), }; export const registry = new PluginServiceRegistry(providers); diff --git a/src/plugins/dashboard/public/services/plugin_services.ts b/src/plugins/dashboard/public/services/plugin_services.ts index b4ee1b566a8ac..4382506a37948 100644 --- a/src/plugins/dashboard/public/services/plugin_services.ts +++ b/src/plugins/dashboard/public/services/plugin_services.ts @@ -39,6 +39,7 @@ import { visualizationsServiceFactory } from './visualizations/visualizations_se import { usageCollectionServiceFactory } from './usage_collection/usage_collection_service'; import { analyticsServiceFactory } from './analytics/analytics_service'; import { dashboardSavedObjectServiceFactory } from './dashboard_saved_object/dashboard_saved_object_service'; +import { customBrandingServiceFactory } from './custom_branding/custom_branding_service'; const providers: PluginServiceProviders = { dashboardSavedObject: new PluginServiceProvider(dashboardSavedObjectServiceFactory, [ @@ -78,6 +79,7 @@ const providers: PluginServiceProviders(); diff --git a/src/plugins/dashboard/public/services/types.ts b/src/plugins/dashboard/public/services/types.ts index 5d14b59e8a125..fc7e0acf1b5c4 100644 --- a/src/plugins/dashboard/public/services/types.ts +++ b/src/plugins/dashboard/public/services/types.ts @@ -14,6 +14,7 @@ import { DashboardAnalyticsService } from './analytics/types'; import { DashboardApplicationService } from './application/types'; import { DashboardChromeService } from './chrome/types'; import { DashboardCoreContextService } from './core_context/types'; +import { DashboardCustomBrandingService } from './custom_branding/types'; import { DashboardCapabilitiesService } from './dashboard_capabilities/types'; import { DashboardSavedObjectService } from './dashboard_saved_object/types'; import { DashboardSessionStorageServiceType } from './dashboard_session_storage/types'; @@ -64,4 +65,5 @@ export interface DashboardServices { urlForwarding: DashboardUrlForwardingService; usageCollection: DashboardUsageCollectionService; // TODO: make this optional in follow up visualizations: DashboardVisualizationsService; + customBranding: DashboardCustomBrandingService; } diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index c79c0bc281042..8ab580ccdfe72 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -51,6 +51,7 @@ "@kbn/core-saved-objects-common", "@kbn/task-manager-plugin", "@kbn/core-execution-context-common", + "@kbn/core-custom-branding-browser", ], "exclude": [ "target/**/*", diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index 2ce045f1b6d66..f06a15edf044a 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -114,7 +114,7 @@ describe('SearchSource', () => { }); describe('#getActiveIndexFilter()', () => { - test('pase _index from query', () => { + test('pass _index from query', () => { searchSource.setField('query', { language: 'kuery', query: `_INDEX : fakebeat and _index : "mybeat-*"`, @@ -122,7 +122,7 @@ describe('SearchSource', () => { expect(searchSource.getActiveIndexFilter()).toMatchObject(['mybeat-*']); }); - test('pase _index from filter', () => { + test('pass _index from filter', () => { const filter = [ { query: { match_phrase: { _index: 'auditbeat-*' } }, @@ -163,7 +163,7 @@ describe('SearchSource', () => { expect(searchSource.getActiveIndexFilter()).toMatchObject(['auditbeat-*']); }); - test('pase _index from query and filter with negate equals to true', () => { + test('pass _index from query and filter with negate equals to true', () => { const filter = [ { query: { @@ -189,7 +189,7 @@ describe('SearchSource', () => { expect(searchSource.getActiveIndexFilter()).toMatchObject([]); }); - test('pase _index from query and filter with negate equals to true and disabled equals to true', () => { + test('pass _index from query and filter with negate equals to true and disabled equals to true', () => { const filter = [ { query: { diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 14b2e364878ef..0d79d469f40c8 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -59,7 +59,17 @@ */ import { setWith } from '@kbn/safer-lodash-set'; -import { difference, isEqual, isFunction, isObject, keyBy, pick, uniqueId, uniqWith } from 'lodash'; +import { + difference, + isEqual, + isFunction, + isObject, + keyBy, + pick, + uniqueId, + uniqWith, + concat, +} from 'lodash'; import { catchError, finalize, @@ -72,7 +82,7 @@ import { } from 'rxjs/operators'; import { defer, EMPTY, from, lastValueFrom, Observable } from 'rxjs'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { buildEsQuery, Filter, isOfQueryType } from '@kbn/es-query'; +import { buildEsQuery, Filter, isOfQueryType, isPhraseFilter } from '@kbn/es-query'; import { fieldWildcardFilter } from '@kbn/kibana-utils-plugin/common'; import { getHighlightRequest } from '@kbn/field-formats-plugin/common'; import type { DataView } from '@kbn/data-views-plugin/common'; @@ -81,7 +91,6 @@ import { buildExpression, buildExpressionFunction, } from '@kbn/expressions-plugin/common'; -import _ from 'lodash'; import { normalizeSortRequest } from './normalize_sort_request'; import { AggConfigSerialized, DataViewField, SerializedSearchSourceFields } from '../..'; @@ -251,7 +260,6 @@ export class SearchSource { getActiveIndexFilter() { const { filter: originalFilters, query } = this.getFields(); - let filters: Filter[] = []; if (originalFilters) { filters = this.getFilters(originalFilters); @@ -270,16 +278,16 @@ export class SearchSource { return acc.concat(this.parseActiveIndexPatternFromQueryString(currStr)); }, []) ?? []; - const activeIndexPattern: string[] = filters?.reduce((acc, f) => { - if (f.meta.key === '_index' && f.meta.disabled === false) { - if (f.meta.negate === false) { - return _.concat(acc, f.meta.params.query ?? f.meta.params); - } else { - if (Array.isArray(f.meta.params)) { - return _.difference(acc, f.meta.params); + const activeIndexPattern = filters?.reduce((acc, f) => { + if (isPhraseFilter(f)) { + if (f.meta.key === '_index' && f.meta.disabled === false) { + if (f.meta.negate === false) { + return concat(acc, f.meta.params?.query ?? f.meta.params); } else { - return _.difference(acc, [f.meta.params.query]); + return difference(acc, [f.meta.params?.query]); } + } else { + return acc; } } else { return acc; diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts index 6058a125c9a54..0471b1e4bee7f 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts @@ -25,7 +25,7 @@ export function getPhraseDisplayValue( formatter?: FieldFormat, fieldType?: string ): string { - const value = filter.meta.value ?? filter.meta.params.query; + const value = filter.meta.value ?? filter.meta.params?.query; const updatedValue = fieldType === 'number' && !value ? 0 : value; if (formatter?.convert) { return formatter.convert(updatedValue); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts index 48ca3852e715d..3a94972a5e69b 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts @@ -11,7 +11,7 @@ import { FieldFormat } from '@kbn/field-formats-plugin/common'; export function getPhrasesDisplayValue(filter: PhrasesFilter, formatter?: FieldFormat) { return filter.meta.params - .map((v: string) => { + .map((v) => { return formatter?.convert(v) ?? v; }) .join(', '); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts index 04eb67e792163..f3fcb3ded54a6 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts @@ -21,8 +21,8 @@ export function getRangeDisplayValue( { meta: { params } }: RangeFilter | ScriptedRangeFilter, formatter?: FieldFormat ) { - const left = params.gte ?? params.gt ?? -Infinity; - const right = params.lte ?? params.lt ?? Infinity; + const left = params?.gte ?? params?.gt ?? -Infinity; + const right = params?.lte ?? params?.lt ?? Infinity; if (!formatter) return `${left} to ${right}`; const convert = formatter.getConverterFor('text'); return `${convert(left)} to ${convert(right)}`; diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index ab8c5da67d04a..2eec55daf742d 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -19,7 +19,6 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; -import { isOfQueryType } from '@kbn/es-query'; import classNames from 'classnames'; import { generateFilters } from '@kbn/data-plugin/public'; import { DataView, DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; @@ -42,7 +41,6 @@ import { DataMainMsg, RecordRawType } from '../../services/discover_data_state_c import { useColumns } from '../../../../hooks/use_data_grid_columns'; import { FetchStatus } from '../../../types'; import { useDataState } from '../../hooks/use_data_state'; -import { hasActiveFilter } from './utils'; import { getRawRecordType } from '../../utils/get_raw_record_type'; import { SavedSearchURLConflictCallout } from '../../../../components/saved_search_url_conflict_callout/saved_search_url_conflict_callout'; import { DiscoverHistogramLayout } from './discover_histogram_layout'; @@ -84,10 +82,9 @@ export function DiscoverLayout({ inspector, } = useDiscoverServices(); const { main$ } = stateContainer.dataState.data$; - const [query, savedQuery, filters, columns, sort] = useAppStateSelector((state) => [ + const [query, savedQuery, columns, sort] = useAppStateSelector((state) => [ state.query, state.savedQuery, - state.filters, state.columns, state.sort, ]); @@ -208,13 +205,16 @@ export function DiscoverLayout({ const mainDisplay = useMemo(() => { if (resultState === 'none') { + const globalQueryState = data.query.getState(); + return ( ); @@ -257,7 +257,6 @@ export function DiscoverLayout({ dataState.error, dataView, expandedDoc, - filters, inspectorAdapters, isPlainRecord, isTimeBased, @@ -265,7 +264,6 @@ export function DiscoverLayout({ onAddFilter, onDisableFilters, onFieldEdited, - query, resetSavedSearch, resultState, savedSearch, diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx index 6ead00f8cce06..98b5d6f46fcc8 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx @@ -7,47 +7,83 @@ */ import React from 'react'; +import { ReactWrapper } from 'enzyme'; +import * as RxApi from 'rxjs'; +import { act } from 'react-dom/test-utils'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; - -import { DiscoverNoResults, DiscoverNoResultsProps } from './no_results'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { + stubDataView, + stubDataViewWithoutTimeField, +} from '@kbn/data-views-plugin/common/data_view.stub'; +import { type Filter } from '@kbn/es-query'; +import { DiscoverNoResults, DiscoverNoResultsProps } from './no_results'; +import { createDiscoverServicesMock } from '../../../../__mocks__/services'; -beforeEach(() => { - jest.clearAllMocks(); -}); - -function mountAndFindSubjects(props: Omit) { - const services = { - docLinks: { - links: { - query: { - luceneQuerySyntax: 'documentation-link', - }, +jest.spyOn(RxApi, 'lastValueFrom').mockImplementation(async () => ({ + rawResponse: { + aggregations: { + earliest_timestamp: { + value_as_string: '2020-09-01T08:30:00.000Z', + }, + latest_timestamp: { + value_as_string: '2022-09-01T08:30:00.000Z', }, }, - }; - const component = mountWithIntl( - - {}} {...props} /> - - ); + }, +})); + +async function mountAndFindSubjects( + props: Omit +) { + const services = createDiscoverServicesMock(); + + let component: ReactWrapper; + + await act(async () => { + component = await mountWithIntl( + + {}} + {...props} + /> + + ); + }); + + await new Promise((resolve) => setTimeout(resolve, 0)); + await act(async () => { + await component!.update(); + }); + return { - mainMsg: findTestSubject(component, 'discoverNoResults').exists(), - errorMsg: findTestSubject(component, 'discoverNoResultsError').exists(), - adjustTimeRange: findTestSubject(component, 'discoverNoResultsTimefilter').exists(), - adjustSearch: findTestSubject(component, 'discoverNoResultsAdjustSearch').exists(), - adjustFilters: findTestSubject(component, 'discoverNoResultsAdjustFilters').exists(), - checkIndices: findTestSubject(component, 'discoverNoResultsCheckIndices').exists(), - disableFiltersButton: findTestSubject(component, 'discoverNoResultsDisableFilters').exists(), + mainMsg: findTestSubject(component!, 'discoverNoResults').exists(), + errorMsg: findTestSubject(component!, 'discoverNoResultsError').exists(), + adjustTimeRange: findTestSubject(component!, 'discoverNoResultsTimefilter').exists(), + adjustSearch: findTestSubject(component!, 'discoverNoResultsAdjustSearch').exists(), + adjustFilters: findTestSubject(component!, 'discoverNoResultsAdjustFilters').exists(), + checkIndices: findTestSubject(component!, 'discoverNoResultsCheckIndices').exists(), + disableFiltersButton: findTestSubject(component!, 'discoverNoResultsDisableFilters').exists(), + viewMatchesButton: findTestSubject(component!, 'discoverNoResultsViewAllMatches').exists(), }; } describe('DiscoverNoResults', () => { + beforeEach(() => { + (RxApi.lastValueFrom as jest.Mock).mockClear(); + }); + describe('props', () => { describe('no props', () => { - test('renders default feedback', () => { - const result = mountAndFindSubjects({}); + test('renders default feedback', async () => { + const result = await mountAndFindSubjects({ + dataView: stubDataViewWithoutTimeField, + query: undefined, + filters: undefined, + }); expect(result).toMatchInlineSnapshot(` Object { "adjustFilters": false, @@ -57,14 +93,17 @@ describe('DiscoverNoResults', () => { "disableFiltersButton": false, "errorMsg": false, "mainMsg": true, + "viewMatchesButton": false, } `); }); }); describe('timeFieldName', () => { - test('renders time range feedback', () => { - const result = mountAndFindSubjects({ - isTimeBased: true, + test('renders time range feedback', async () => { + const result = await mountAndFindSubjects({ + dataView: stubDataView, + query: { language: 'lucene', query: '' }, + filters: [], }); expect(result).toMatchInlineSnapshot(` Object { @@ -75,30 +114,42 @@ describe('DiscoverNoResults', () => { "disableFiltersButton": false, "errorMsg": false, "mainMsg": true, + "viewMatchesButton": true, } `); + expect(RxApi.lastValueFrom).toHaveBeenCalledTimes(1); }); }); describe('filter/query', () => { - test('shows "adjust search" message when having query', () => { - const result = mountAndFindSubjects({ hasQuery: true }); + test('shows "adjust search" message when having query', async () => { + const result = await mountAndFindSubjects({ + dataView: stubDataView, + query: { language: 'lucene', query: '*' }, + filters: undefined, + }); expect(result).toHaveProperty('adjustSearch', true); }); - test('shows "adjust filters" message when having filters', () => { - const result = mountAndFindSubjects({ hasFilters: true }); + test('shows "adjust filters" message when having filters', async () => { + const result = await mountAndFindSubjects({ + dataView: stubDataView, + query: { language: 'lucene', query: '' }, + filters: [{} as Filter], + }); expect(result).toHaveProperty('adjustFilters', true); expect(result).toHaveProperty('disableFiltersButton', true); }); }); describe('error message', () => { - test('renders error message', () => { + test('renders error message', async () => { const error = new Error('Fatal error'); - const result = mountAndFindSubjects({ - isTimeBased: true, + const result = await mountAndFindSubjects({ + dataView: stubDataView, error, + query: { language: 'lucene', query: '' }, + filters: [{} as Filter], }); expect(result).toMatchInlineSnapshot(` Object { @@ -109,6 +160,7 @@ describe('DiscoverNoResults', () => { "disableFiltersButton": false, "errorMsg": true, "mainMsg": false, + "viewMatchesButton": false, } `); }); diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results.tsx index 4e6f4425f56e0..c24423693a622 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results.tsx @@ -8,60 +8,41 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiButton, - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiTitle, -} from '@elastic/eui'; -import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { EuiButton, EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import type { AggregateQuery, Filter, Query } from '@kbn/es-query'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { NoResultsSuggestions } from './no_results_suggestions'; import './_no_results.scss'; -import { NoResultsIllustration } from './assets/no_results_illustration'; export interface DiscoverNoResultsProps { isTimeBased?: boolean; + query: Query | AggregateQuery | undefined; + filters: Filter[] | undefined; error?: Error; - data?: DataPublicPluginStart; - hasQuery?: boolean; - hasFilters?: boolean; + data: DataPublicPluginStart; + dataView: DataView; onDisableFilters: () => void; } export function DiscoverNoResults({ isTimeBased, + query, + filters, error, data, - hasFilters, - hasQuery, + dataView, onDisableFilters, }: DiscoverNoResultsProps) { const callOut = !error ? ( - - -

      - -

      -
      - - - - - - - - - + + ) : ( diff --git a/src/plugins/discover/public/application/main/components/no_results/assets/no_results_illustration.scss b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/assets/no_results_illustration.scss similarity index 100% rename from src/plugins/discover/public/application/main/components/no_results/assets/no_results_illustration.scss rename to src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/assets/no_results_illustration.scss diff --git a/src/plugins/discover/public/application/main/components/no_results/assets/no_results_illustration.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/assets/no_results_illustration.tsx similarity index 99% rename from src/plugins/discover/public/application/main/components/no_results/assets/no_results_illustration.tsx rename to src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/assets/no_results_illustration.tsx index b96fad88f1b44..10fc01537688a 100644 --- a/src/plugins/discover/public/application/main/components/no_results/assets/no_results_illustration.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/assets/no_results_illustration.tsx @@ -12,8 +12,8 @@ import React from 'react'; export const NoResultsIllustration = () => ( diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_default.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_default.tsx index b232b4138ea69..b90ca64c23e64 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_default.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_default.tsx @@ -8,17 +8,15 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiDescriptionList, EuiDescriptionListDescription } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; export function NoResultsSuggestionDefault() { return ( - - - - - + + + ); } diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_filters.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_filters.tsx index b153f6046b104..4112161aa5f29 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_filters.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_filters.tsx @@ -8,12 +8,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiDescriptionList, - EuiDescriptionListTitle, - EuiLink, - EuiDescriptionListDescription, -} from '@elastic/eui'; +import { EuiLink, EuiText } from '@elastic/eui'; export interface NoResultsSuggestionWhenFiltersProps { onDisableFilters: () => void; @@ -23,29 +18,21 @@ export function NoResultsSuggestionWhenFilters({ onDisableFilters, }: NoResultsSuggestionWhenFiltersProps) { return ( - - - - - - - - - ), - }} - /> - - + + + + + ), + }} + /> + ); } diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_query.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_query.tsx index 166b2a7f742cd..d6ecb53a8025f 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_query.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_query.tsx @@ -7,25 +7,199 @@ */ import React from 'react'; +import { css } from '@emotion/react'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiDescriptionList, - EuiDescriptionListTitle, - EuiDescriptionListDescription, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiLink } from '@elastic/eui'; +import { SyntaxExamples, SyntaxSuggestionsPopover } from './syntax_suggestions_popover'; +import { type DiscoverServices } from '../../../../../build_services'; +import { useDiscoverServices } from '../../../../../hooks/use_discover_services'; -export function NoResultsSuggestionWhenQuery() { - return ( - - - - - +const getExamples = ( + querySyntax: string | undefined, + docLinks: DiscoverServices['docLinks'] +): SyntaxExamples | null => { + if (!querySyntax) { + return null; + } + + if (querySyntax === 'lucene') { + return { + title: i18n.translate('discover.noResults.luceneExamples.title', { + defaultMessage: 'Lucene examples', + }), + items: [ + { + label: i18n.translate( + 'discover.noResults.luceneExamples.findRequestsThatContain200Text', + { + defaultMessage: 'Find requests that contain the number 200, in any field', + } + ), + example: '200', + }, + { + label: i18n.translate('discover.noResults.luceneExamples.find200InStatusFieldText', { + defaultMessage: 'Find 200 in the status field', + }), + example: 'status:200', + }, + { + label: i18n.translate('discover.noResults.luceneExamples.findAllStatusCodesText', { + defaultMessage: 'Find all status codes between 400-499', + }), + example: 'status:[400 TO 499]', + }, + { + label: i18n.translate('discover.noResults.luceneExamples.findStatusCodesWithPHPText', { + defaultMessage: 'Find status codes 400-499 with the extension php', + }), + example: 'status:[400 TO 499] AND extension:PHP', + }, + { + label: i18n.translate( + 'discover.noResults.luceneExamples.findStatusCodesWithPhpOrHtmlText', + { + defaultMessage: 'Find status codes 400-499 with the extension php or html', + } + ), + example: 'status:[400 TO 499] AND (extension:php OR extension:html)', + }, + ], + footer: ( + + + ), + }} /> - - - ); + ), + }; + } + + if (querySyntax === 'kuery') { + return { + title: i18n.translate('discover.noResults.kqlExamples.title', { + defaultMessage: 'KQL examples', + }), + items: [ + { + label: i18n.translate('discover.noResults.kqlExamples.filterForExistingFieldsText', { + defaultMessage: 'Filter for documents where a field exists', + }), + example: 'http.request.method: *', + }, + { + label: i18n.translate('discover.noResults.kqlExamples.filterForDocsThatMatchValueText', { + defaultMessage: 'Filter for documents that match a value', + }), + example: 'http.request.method: GET', + }, + { + label: i18n.translate('discover.noResults.kqlExamples.filterForDocsWithinRangeText', { + defaultMessage: 'Filter for documents within a range', + }), + example: 'http.response.bytes > 10000 and http.response.bytes <= 20000', + }, + { + label: i18n.translate('discover.noResults.kqlExamples.filterForDocsWithWildcardsText', { + defaultMessage: 'Filter for documents using wildcards', + }), + example: 'http.response.status_code: 4*', + }, + { + label: i18n.translate('discover.noResults.kqlExamples.negatingQueryText', { + defaultMessage: 'Negating a query', + }), + example: 'NOT http.request.method: GET', + }, + { + label: i18n.translate('discover.noResults.kqlExamples.combineMultipleText', { + defaultMessage: 'Combining multiple queries with AND/OR', + }), + example: 'http.request.method: GET AND http.response.status_code: 400', + }, + { + label: i18n.translate('discover.noResults.kqlExamples.queryMultipleText', { + defaultMessage: 'Querying multiple values for the same field', + }), + example: 'http.request.method: (GET OR POST OR DELETE)', + }, + ], + footer: ( + + + + ), + }} + /> + ), + }; + } + + return null; +}; + +export interface NoResultsSuggestionWhenQueryProps { + querySyntax: string | undefined; } + +export const NoResultsSuggestionWhenQuery: React.FC = ({ + querySyntax, +}) => { + const services = useDiscoverServices(); + const { docLinks } = services; + const examplesMeta = getExamples(querySyntax, docLinks); + + return ( + <> + + + + {examplesMeta ? ( + + ) : ( + + )} + + + {!!examplesMeta && ( + + + + )} + + + ); +}; diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_time_range.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_time_range.tsx index 434d6025b950e..41f36b446778e 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_time_range.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestion_when_time_range.tsx @@ -8,27 +8,15 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiDescriptionList, - EuiDescriptionListTitle, - EuiDescriptionListDescription, -} from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; -export function NoResultsSuggestionWhenTimeRange() { +export const NoResultsSuggestionWhenTimeRange: React.FC = () => { return ( - - - - - - - - + + + ); -} +}; diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx index 595ca61225ebb..e9cd75e022db3 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx @@ -6,8 +6,18 @@ * Side Public License, v 1. */ -import React from 'react'; -import { EuiSpacer } from '@elastic/eui'; +import React, { useState } from 'react'; +import { css } from '@emotion/react'; +import { EuiEmptyPrompt, EuiButton, EuiLoadingSpinner, EuiSpacer, useEuiTheme } from '@elastic/eui'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { + isOfQueryType, + isOfAggregateQueryType, + type Query, + type AggregateQuery, + type Filter, +} from '@kbn/es-query'; +import { FormattedMessage } from '@kbn/i18n-react'; import { NoResultsSuggestionDefault } from './no_results_suggestion_default'; import { NoResultsSuggestionWhenFilters, @@ -15,41 +25,133 @@ import { } from './no_results_suggestion_when_filters'; import { NoResultsSuggestionWhenQuery } from './no_results_suggestion_when_query'; import { NoResultsSuggestionWhenTimeRange } from './no_results_suggestion_when_time_range'; +import { hasActiveFilter } from '../../layout/utils'; +import { useDiscoverServices } from '../../../../../hooks/use_discover_services'; +import { useFetchOccurrencesRange } from './use_fetch_occurances_range'; +import { NoResultsIllustration } from './assets/no_results_illustration'; interface NoResultsSuggestionProps { - hasFilters?: boolean; - hasQuery?: boolean; + dataView: DataView; isTimeBased?: boolean; + query: Query | AggregateQuery | undefined; + filters: Filter[] | undefined; onDisableFilters: NoResultsSuggestionWhenFiltersProps['onDisableFilters']; } -export function NoResultsSuggestions({ +export const NoResultsSuggestions: React.FC = ({ + dataView, isTimeBased, - hasFilters, - hasQuery, + query, + filters, onDisableFilters, -}: NoResultsSuggestionProps) { +}) => { + const { euiTheme } = useEuiTheme(); + const services = useDiscoverServices(); + const { data, uiSettings, timefilter } = services; + const hasQuery = + (isOfQueryType(query) && !!query?.query) || (!!query && isOfAggregateQueryType(query)); + const hasFilters = hasActiveFilter(filters); + + const [isExtending, setIsExtending] = useState(false); + const { range: occurrencesRange, refetch } = useFetchOccurrencesRange({ + dataView, + query, + filters, + services: { + data, + uiSettings, + }, + }); + + const extendTimeRange = async () => { + setIsExtending(true); + const range = await refetch(); + if (range?.from && range?.to) { + timefilter.setTime({ + from: range.from, + to: range.to, + }); + } else { + setIsExtending(false); + } + }; + + const canExtendTimeRange = Boolean(occurrencesRange?.from && occurrencesRange.to); const canAdjustSearchCriteria = isTimeBased || hasFilters || hasQuery; - if (canAdjustSearchCriteria) { - return ( - <> - {isTimeBased && } + const body = canAdjustSearchCriteria ? ( + <> + + +
        + {isTimeBased && ( +
      • + +
      • + )} {hasQuery && ( - <> - - - +
      • + +
      • )} {hasFilters && ( - <> - +
      • - +
      • )} - - ); - } +
      + + ) : ( + + ); - return ; -} + return ( + } + title={ +

      + +

      + } + body={body} + actions={ +
      + {typeof occurrencesRange === 'undefined' ? ( + + ) : canExtendTimeRange ? ( + + + + ) : null} +
      + } + /> + ); +}; diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/syntax_suggestions_popover.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/syntax_suggestions_popover.tsx new file mode 100644 index 0000000000000..4e5e3ba3796cd --- /dev/null +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/syntax_suggestions_popover.tsx @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; +import { css } from '@emotion/react'; +import { + EuiBasicTable, + EuiButtonIcon, + EuiPanel, + EuiPopover, + EuiPopoverTitle, + EuiCode, + EuiText, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +interface SyntaxExample { + label: string; + example: string; +} + +export interface SyntaxExamples { + title: string; + footer: React.ReactElement; + items: SyntaxExample[]; +} + +export interface SyntaxSuggestionsPopoverProps { + meta: SyntaxExamples; +} + +export const SyntaxSuggestionsPopover: React.FC = ({ meta }) => { + const [isOpen, setIsOpen] = useState(false); + const { title, items, footer } = meta; + + const helpButton = ( + setIsOpen((prev) => !prev)} + iconType="documentation" + aria-label={title} + /> + ); + + const columns = [ + { + field: 'label', + name: i18n.translate('discover.noResults.suggestion.syntaxPopoverDescriptionHeader', { + defaultMessage: 'Description', + }), + width: '200px', + }, + { + field: 'example', + name: i18n.translate('discover.noResults.suggestion.syntaxPopoverExampleHeader', { + defaultMessage: 'Example', + }), + render: (example: string) => {example}, + }, + ]; + + return ( + setIsOpen(false)} + initialFocus="#querySyntaxBasicTableId" + > + {title} + + + id="querySyntaxBasicTableId" + tableCaption={title} + items={items} + compressed={true} + rowHeader="label" + columns={columns} + responsive + /> + + + {footer} + + + + ); +}; diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/use_fetch_occurances_range.ts b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/use_fetch_occurances_range.ts new file mode 100644 index 0000000000000..8ae801fc93039 --- /dev/null +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/use_fetch_occurances_range.ts @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useCallback, useEffect, useRef, useState } from 'react'; +import { lastValueFrom } from 'rxjs'; +import type { DataView } from '@kbn/data-plugin/common'; +import type { AggregateQuery, Filter, Query } from '@kbn/es-query'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import type { AggregationsSingleMetricAggregateBase } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { buildEsQuery } from '@kbn/es-query'; +import { getEsQueryConfig } from '@kbn/data-plugin/common'; + +export interface Params { + dataView?: DataView; + query?: Query | AggregateQuery; + filters?: Filter[]; + services: { + data: DataPublicPluginStart; + uiSettings: IUiSettingsClient; + }; +} + +export interface OccurrencesRange { + from: string; + to: string; +} + +export interface Result { + range: OccurrencesRange | null | undefined; + refetch: () => Promise; +} + +export const useFetchOccurrencesRange = (params: Params): Result => { + const data = params.services.data; + const uiSettings = params.services.uiSettings; + const [range, setRange] = useState(undefined); + const abortControllerRef = useRef(null); + const mountedRef = useRef(true); + + const fetchOccurrences = useCallback( + async (dataView?: DataView, query?: Query | AggregateQuery, filters?: Filter[]) => { + let occurrencesRange = null; + if (!dataView?.timeFieldName || !query || !mountedRef.current) { + return null; + } + + abortControllerRef.current?.abort(); + abortControllerRef.current = new AbortController(); + + try { + const dslQuery = buildEsQuery( + dataView, + query ?? [], + filters ?? [], + getEsQueryConfig(uiSettings) + ); + occurrencesRange = await fetchDocumentsTimeRange({ + data, + dataView, + dslQuery, + abortSignal: abortControllerRef.current?.signal, + }); + } catch (error) { + // + } + + if (mountedRef.current) { + setRange(occurrencesRange); + } + + return occurrencesRange; + }, + [abortControllerRef, setRange, mountedRef, data, uiSettings] + ); + + useEffect(() => { + return () => { + mountedRef.current = false; + abortControllerRef.current?.abort(); + }; + }, [abortControllerRef, mountedRef]); + + useEffect(() => { + fetchOccurrences(params.dataView, params.query, params.filters); + }, [fetchOccurrences, params.query, params.filters, params.dataView]); + + return { + range, + refetch: () => fetchOccurrences(params.dataView, params.query, params.filters), + }; +}; + +async function fetchDocumentsTimeRange({ + data, + dataView, + dslQuery, + abortSignal, +}: { + data: DataPublicPluginStart; + dataView: DataView; + dslQuery?: object; + abortSignal?: AbortSignal; +}): Promise { + if (!dataView?.timeFieldName) { + return null; + } + + const result = await lastValueFrom( + data.search.search( + { + params: { + index: dataView.title, + size: 0, + body: { + query: dslQuery ?? { match_all: {} }, + aggs: { + earliest_timestamp: { + min: { + field: dataView.timeFieldName, + }, + }, + latest_timestamp: { + max: { + field: dataView.timeFieldName, + }, + }, + }, + }, + }, + }, + { + abortSignal, + } + ) + ); + + const earliestTimestamp = ( + result.rawResponse?.aggregations?.earliest_timestamp as AggregationsSingleMetricAggregateBase + )?.value_as_string; + const latestTimestamp = ( + result.rawResponse?.aggregations?.latest_timestamp as AggregationsSingleMetricAggregateBase + )?.value_as_string; + + return earliestTimestamp && latestTimestamp + ? { from: earliestTimestamp, to: latestTimestamp } + : null; +} diff --git a/src/plugins/discover/public/application/main/discover_main_route.tsx b/src/plugins/discover/public/application/main/discover_main_route.tsx index b317c0e5cffdc..242f2ab95a5cf 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.tsx @@ -21,6 +21,7 @@ import { getSavedSearch, getSavedSearchFullPathUrl, } from '@kbn/saved-search-plugin/public'; +import useObservable from 'react-use/lib/useObservable'; import { MainHistoryLocationState } from '../../../common/locator'; import { getDiscoverStateContainer } from './services/discover_state'; import { loadDataView, resolveDataView } from './utils/resolve_data_view'; @@ -63,6 +64,7 @@ export function DiscoverMainRoute(props: Props) { const [hasESData, setHasESData] = useState(false); const [hasUserDataView, setHasUserDataView] = useState(false); const [showNoDataPage, setShowNoDataPage] = useState(false); + const hasCustomBranding = useObservable(core.customBranding.hasCustomBranding$, false); const { id } = useParams(); /** @@ -278,7 +280,7 @@ export function DiscoverMainRoute(props: Props) { } if (loading || !savedSearch) { - return ; + return ; } return ; diff --git a/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx b/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx index 7b0fd713c09cc..de86ad4048478 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx +++ b/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx @@ -18,7 +18,7 @@ import { testGuideConfig, testGuideId } from '@kbn/guided-onboarding'; import type { PluginState } from '../../common'; import { API_BASE_PATH } from '../../common'; -import { apiService } from '../services/api'; +import { apiService } from '../services/api.service'; import type { GuidedOnboardingApi } from '../types'; import { testGuideStep1ActiveState, diff --git a/src/plugins/guided_onboarding/public/components/quit_guide_modal.tsx b/src/plugins/guided_onboarding/public/components/quit_guide_modal.tsx index 6765e7a4ecbf9..c649ca2a9eae0 100644 --- a/src/plugins/guided_onboarding/public/components/quit_guide_modal.tsx +++ b/src/plugins/guided_onboarding/public/components/quit_guide_modal.tsx @@ -20,7 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import type { GuideState } from '@kbn/guided-onboarding'; import { NotificationsStart } from '@kbn/core/public'; -import { apiService } from '../services/api'; +import { apiService } from '../services/api.service'; interface QuitGuideModalProps { closeModal: () => void; diff --git a/src/plugins/guided_onboarding/public/plugin.tsx b/src/plugins/guided_onboarding/public/plugin.tsx index 4c5e02ddac9c4..97eac765c5ddd 100755 --- a/src/plugins/guided_onboarding/public/plugin.tsx +++ b/src/plugins/guided_onboarding/public/plugin.tsx @@ -27,7 +27,7 @@ import type { GuidedOnboardingPluginStart, } from './types'; import { GuidePanel } from './components'; -import { ApiService, apiService } from './services/api'; +import { ApiService, apiService } from './services/api.service'; export class GuidedOnboardingPlugin implements Plugin diff --git a/src/plugins/guided_onboarding/public/services/api.ts b/src/plugins/guided_onboarding/public/services/api.service.ts similarity index 97% rename from src/plugins/guided_onboarding/public/services/api.ts rename to src/plugins/guided_onboarding/public/services/api.service.ts index 5ab9bed187f6b..398d1cf56a6a3 100644 --- a/src/plugins/guided_onboarding/public/services/api.ts +++ b/src/plugins/guided_onboarding/public/services/api.service.ts @@ -37,7 +37,7 @@ import { getStepConfig, isLastStep, } from './helpers'; -import { ConfigService } from './config_service'; +import { ConfigService } from './config.service'; export class ApiService implements GuidedOnboardingApi { private isCloudEnabled: boolean | undefined; @@ -123,6 +123,10 @@ export class ApiService implements GuidedOnboardingApi { if (!this.client) { throw new Error('ApiService has not be initialized.'); } + // don't send a request if a request is already in flight + if (this.isLoading$.value) { + return undefined; + } try { this.isLoading$.next(true); @@ -152,6 +156,10 @@ export class ApiService implements GuidedOnboardingApi { if (!this.client) { throw new Error('ApiService has not be initialized.'); } + // don't send a request if a request is already in flight + if (this.isLoading$.value) { + return undefined; + } try { this.isLoading$.next(true); @@ -474,6 +482,10 @@ export class ApiService implements GuidedOnboardingApi { if (!this.client) { throw new Error('ApiService has not be initialized.'); } + // don't send a request if a request is already in flight + if (this.isLoading$.value) { + return undefined; + } this.isLoading$.next(true); const config = await this.configService.getGuideConfig(guideId); this.isLoading$.next(false); diff --git a/src/plugins/guided_onboarding/public/services/api.test.ts b/src/plugins/guided_onboarding/public/services/api.test.ts index 5146d2df72ddb..1b5a7b02d0bb3 100644 --- a/src/plugins/guided_onboarding/public/services/api.test.ts +++ b/src/plugins/guided_onboarding/public/services/api.test.ts @@ -13,7 +13,7 @@ import { testGuideConfig, testGuideId } from '@kbn/guided-onboarding'; import { firstValueFrom, Subscription } from 'rxjs'; import { API_BASE_PATH } from '../../common'; -import { ApiService } from './api'; +import { ApiService } from './api.service'; import { testGuideFirstStep, testGuideLastStep, diff --git a/src/plugins/guided_onboarding/public/services/config_service.test.ts b/src/plugins/guided_onboarding/public/services/config.service.test.ts similarity index 98% rename from src/plugins/guided_onboarding/public/services/config_service.test.ts rename to src/plugins/guided_onboarding/public/services/config.service.test.ts index 98aa0deb35701..42ddcb5e46808 100644 --- a/src/plugins/guided_onboarding/public/services/config_service.test.ts +++ b/src/plugins/guided_onboarding/public/services/config.service.test.ts @@ -18,7 +18,7 @@ import { wrongIntegration, } from './api.mocks'; -import { ConfigService } from './config_service'; +import { ConfigService } from './config.service'; describe('GuidedOnboarding ConfigService', () => { let configService: ConfigService; diff --git a/src/plugins/guided_onboarding/public/services/config_service.ts b/src/plugins/guided_onboarding/public/services/config.service.ts similarity index 100% rename from src/plugins/guided_onboarding/public/services/config_service.ts rename to src/plugins/guided_onboarding/public/services/config.service.ts diff --git a/src/plugins/home/public/application/components/guided_onboarding/__snapshots__/getting_started.test.tsx.snap b/src/plugins/home/public/application/components/guided_onboarding/__snapshots__/getting_started.test.tsx.snap index 4483bc431241e..291d0173a0bbf 100644 --- a/src/plugins/home/public/application/components/guided_onboarding/__snapshots__/getting_started.test.tsx.snap +++ b/src/plugins/home/public/application/components/guided_onboarding/__snapshots__/getting_started.test.tsx.snap @@ -36,7 +36,7 @@ exports[`getting started should render getting started component 1`] = ` textAlign="center" >

      - Select a guide to help you make the most of your data. + Select an option and we'll help you get started.

      - - - - - - - - - - - - - - - - + + +
      - I’d like to do something else (skip) + I’d like to do something else.
      diff --git a/src/plugins/home/public/application/components/guided_onboarding/getting_started.test.tsx b/src/plugins/home/public/application/components/guided_onboarding/getting_started.test.tsx index 9506c713faf90..a575ed1d0d82a 100644 --- a/src/plugins/home/public/application/components/guided_onboarding/getting_started.test.tsx +++ b/src/plugins/home/public/application/components/guided_onboarding/getting_started.test.tsx @@ -12,8 +12,7 @@ import { act } from 'react-dom/test-utils'; import { findTestSubject, registerTestBed, TestBed } from '@kbn/test-jest-helpers'; import { cloudMock } from '@kbn/cloud-plugin/public/mocks'; import { chromeServiceMock, applicationServiceMock, httpServiceMock } from '@kbn/core/public/mocks'; -import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; -import { ApiService } from '@kbn/guided-onboarding-plugin/public/services/api'; +import { ApiService } from '@kbn/guided-onboarding-plugin/public/services/api.service'; import { GettingStarted } from './getting_started'; import { KEY_ENABLE_WELCOME } from '../home'; @@ -21,8 +20,6 @@ import { KEY_ENABLE_WELCOME } from '../home'; const mockCloud = cloudMock.createSetup(); const mockChrome = chromeServiceMock.createStartContract(); const mockApplication = applicationServiceMock.createStartContract(); -const mockSettingsUI = uiSettingsServiceMock.createSetupContract(); -mockSettingsUI.get.mockReturnValue(false); const mockHttp = httpServiceMock.createStartContract(); const mockApiService = new ApiService(); mockApiService.setup(mockHttp, true); @@ -33,8 +30,6 @@ jest.mock('../../kibana_services', () => ({ chrome: mockChrome, application: mockApplication, trackUiMetric: jest.fn(), - uiSettings: mockSettingsUI, - http: mockHttp, guidedOnboardingService: mockApiService, }), })); diff --git a/src/plugins/home/public/application/components/guided_onboarding/getting_started.tsx b/src/plugins/home/public/application/components/guided_onboarding/getting_started.tsx index 2af90e9be5edb..01d39efbcf2b8 100644 --- a/src/plugins/home/public/application/components/guided_onboarding/getting_started.tsx +++ b/src/plugins/home/public/application/components/guided_onboarding/getting_started.tsx @@ -9,9 +9,6 @@ import React, { useCallback, useEffect, useState } from 'react'; import { EuiButton, - EuiFlexGrid, - EuiFlexItem, - EuiHorizontalRule, EuiLink, EuiLoadingSpinner, EuiPageTemplate, @@ -26,9 +23,9 @@ import { useHistory } from 'react-router-dom'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; -import type { GuideState, GuideId, GuideCardUseCase } from '@kbn/guided-onboarding'; -import { GuideCard, InfrastructureLinkCard } from '@kbn/guided-onboarding'; +import type { GuideFilterValues, GuideId, GuideState } from '@kbn/guided-onboarding'; +import { GuideCards, GuideFilters } from '@kbn/guided-onboarding'; import { getServices } from '../../kibana_services'; import { KEY_ENABLE_WELCOME } from '../home'; @@ -40,18 +37,18 @@ const title = i18n.translate('home.guidedOnboarding.gettingStarted.useCaseSelect defaultMessage: 'What would you like to do first?', }); const subtitle = i18n.translate('home.guidedOnboarding.gettingStarted.useCaseSelectionSubtitle', { - defaultMessage: 'Select a guide to help you make the most of your data.', + defaultMessage: `Select an option and we'll help you get started.`, }); const skipText = i18n.translate('home.guidedOnboarding.gettingStarted.skip.buttonLabel', { - defaultMessage: `I’d like to do something else (skip)`, + defaultMessage: `I’d like to do something else.`, }); export const GettingStarted = () => { - const { application, trackUiMetric, chrome, guidedOnboardingService, http, uiSettings, cloud } = - getServices(); + const { application, trackUiMetric, chrome, guidedOnboardingService, cloud } = getServices(); const [guidesState, setGuidesState] = useState([]); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); + const [filter, setFilter] = useState('all'); const history = useHistory(); useEffect(() => { @@ -114,11 +111,10 @@ export const GettingStarted = () => { padding: calc(${euiTheme.size.base}*3) calc(${euiTheme.size.base}*4); `; - const isDarkTheme = uiSettings.get('theme:darkMode'); const activateGuide = useCallback( - async (useCase: GuideCardUseCase, guideState?: GuideState) => { + async (guideId: GuideId, guideState?: GuideState) => { try { - await guidedOnboardingService?.activateGuide(useCase as GuideId, guideState); + await guidedOnboardingService?.activateGuide(guideId, guideState); } catch (err) { getServices().toastNotifications.addDanger({ title: i18n.translate('home.guidedOnboarding.gettingStarted.activateGuide.errorMessage', { @@ -200,34 +196,14 @@ export const GettingStarted = () => { - - {['search', 'kubernetes', 'infrastructure', 'siem'].map((useCase) => { - if (useCase === 'infrastructure') { - return ( - - - - ); - } - return ( - - - - ); - })} - - - + + +
      {/* data-test-subj used for FS tracking */} diff --git a/src/plugins/home/tsconfig.json b/src/plugins/home/tsconfig.json index 693c8189c504f..904cf7194c390 100644 --- a/src/plugins/home/tsconfig.json +++ b/src/plugins/home/tsconfig.json @@ -23,7 +23,6 @@ "@kbn/shared-ux-page-kibana-template", "@kbn/utility-types", "@kbn/guided-onboarding", - "@kbn/core-ui-settings-browser-mocks", "@kbn/ui-theme", "@kbn/config-schema", "@kbn/utility-types-jest", diff --git a/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap b/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap index 3ac183860d215..453d20385e706 100644 --- a/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap +++ b/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap @@ -457,6 +457,7 @@ exports[`Overview renders correctly when there is no user data view 1`] = ` "navigateToUrl": [MockFunction], }, "chrome": undefined, + "customBranding": undefined, "docLinks": Object { "links": Object { "kibana": Object { diff --git a/src/plugins/kibana_overview/public/components/overview/overview.tsx b/src/plugins/kibana_overview/public/components/overview/overview.tsx index f87c90a4591d4..f6d97d54681e8 100644 --- a/src/plugins/kibana_overview/public/components/overview/overview.tsx +++ b/src/plugins/kibana_overview/public/components/overview/overview.tsx @@ -62,8 +62,17 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) => const [hasDataView, setHasDataView] = useState(false); const [isLoading, setIsLoading] = useState(true); const { services } = useKibana(); - const { http, docLinks, dataViews, share, uiSettings, application, chrome, dataViewEditor } = - services; + const { + http, + docLinks, + dataViews, + share, + uiSettings, + application, + chrome, + dataViewEditor, + customBranding, + } = services; const addBasePath = http.basePath.prepend; const IS_DARK_THEME = uiSettings.get('theme:darkMode'); @@ -177,6 +186,7 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) => chrome, docLinks, http, + customBranding, }, dataViews: { ...dataViews, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index ec389fda1f084..14d104d84e334 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -570,8 +570,4 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, - 'enterpriseSearch:enableEnginesSection': { - type: 'boolean', - _meta: { description: 'Non-default value of setting.' }, - }, }; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 6a0743256465f..8103c0e93f301 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -152,5 +152,4 @@ export interface UsageStats { 'securitySolution:enableGroupedNav': boolean; 'securitySolution:showRelatedIntegrations': boolean; 'visualization:visualize:legacyGaugeChartsLibrary': boolean; - 'enterpriseSearch:enableEnginesSection': boolean; } diff --git a/src/plugins/newsfeed/public/components/__snapshots__/loading_news.test.tsx.snap b/src/plugins/newsfeed/public/components/__snapshots__/loading_news.test.tsx.snap index dfd1cf4dbcf5e..6b520e5e74346 100644 --- a/src/plugins/newsfeed/public/components/__snapshots__/loading_news.test.tsx.snap +++ b/src/plugins/newsfeed/public/components/__snapshots__/loading_news.test.tsx.snap @@ -1,5 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`news_loading rendering renders the News Loading with EuiLoadingSpinner 1`] = ` + + +

      + } + title={ + + } +/> +`; + exports[`news_loading rendering renders the default News Loading 1`] = ` ) => { +export const NewsfeedFlyout = (props: Partial & { showPlainSpinner: boolean }) => { const { newsFetchResult, setFlyoutVisible } = useContext(NewsfeedContext); const closeFlyout = useCallback(() => setFlyoutVisible(false), [setFlyoutVisible]); @@ -55,7 +55,7 @@ export const NewsfeedFlyout = (props: Partial) => { {!newsFetchResult ? ( - + ) : newsFetchResult.feedItems.length > 0 ? ( newsFetchResult.feedItems.map((item: NewsfeedItem) => { return ( diff --git a/src/plugins/newsfeed/public/components/loading_news.test.tsx b/src/plugins/newsfeed/public/components/loading_news.test.tsx index 6c857d9a8d803..1d5227e47d940 100644 --- a/src/plugins/newsfeed/public/components/loading_news.test.tsx +++ b/src/plugins/newsfeed/public/components/loading_news.test.tsx @@ -14,7 +14,11 @@ import { NewsLoadingPrompt } from './loading_news'; describe('news_loading', () => { describe('rendering', () => { it('renders the default News Loading', () => { - const wrapper = shallow(); + const wrapper = shallow(); + expect(toJson(wrapper)).toMatchSnapshot(); + }); + it('renders the News Loading with EuiLoadingSpinner', () => { + const wrapper = shallow(); expect(toJson(wrapper)).toMatchSnapshot(); }); }); diff --git a/src/plugins/newsfeed/public/components/loading_news.tsx b/src/plugins/newsfeed/public/components/loading_news.tsx index d268f67758edb..c596784f49335 100644 --- a/src/plugins/newsfeed/public/components/loading_news.tsx +++ b/src/plugins/newsfeed/public/components/loading_news.tsx @@ -9,13 +9,13 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiEmptyPrompt } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui'; import { EuiLoadingElastic } from '@elastic/eui'; -export const NewsLoadingPrompt = () => { +export const NewsLoadingPrompt = ({ showPlainSpinner }: { showPlainSpinner: boolean }) => { return ( } + title={showPlainSpinner ? : } body={

      ; } -export const NewsfeedNavButton = ({ newsfeedApi }: Props) => { +export const NewsfeedNavButton = ({ newsfeedApi, hasCustomBranding$ }: Props) => { const [flyoutVisible, setFlyoutVisible] = useState(false); const [newsFetchResult, setNewsFetchResult] = useState(null); + const hasCustomBranding = useObservable(hasCustomBranding$, false); const hasNew = useMemo(() => { return newsFetchResult ? newsFetchResult.hasNew : false; }, [newsFetchResult]); @@ -71,7 +75,12 @@ export const NewsfeedNavButton = ({ newsfeedApi }: Props) => { > - {flyoutVisible ? : null} + {flyoutVisible ? ( + + ) : null} ); diff --git a/src/plugins/newsfeed/public/plugin.tsx b/src/plugins/newsfeed/public/plugin.tsx index 344e99ee8e484..cb3ab294616ba 100644 --- a/src/plugins/newsfeed/public/plugin.tsx +++ b/src/plugins/newsfeed/public/plugin.tsx @@ -55,7 +55,8 @@ export class NewsfeedPublicPlugin const api = this.createNewsfeedApi(this.config, NewsfeedApiEndpoint.KIBANA, isScreenshotMode); core.chrome.navControls.registerRight({ order: 1000, - mount: (target) => this.mount(api, target, core.theme.theme$), + mount: (target) => + this.mount(api, target, core.theme.theme$, core.customBranding.hasCustomBranding$), }); return { @@ -91,11 +92,16 @@ export class NewsfeedPublicPlugin }; } - private mount(api: NewsfeedApi, targetDomElement: HTMLElement, theme$: Rx.Observable) { + private mount( + api: NewsfeedApi, + targetDomElement: HTMLElement, + theme$: Rx.Observable, + hasCustomBranding$: Rx.Observable + ) { ReactDOM.render( - + , targetDomElement diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 1219d8c7aad4e..ddc16fba2319a 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -9113,12 +9113,6 @@ "_meta": { "description": "Non-default value of setting." } - }, - "enterpriseSearch:enableEnginesSection": { - "type": "boolean", - "_meta": { - "description": "Non-default value of setting." - } } } }, diff --git a/src/plugins/unified_field_list/public/components/field_list_grouped/field_list_grouped.test.tsx b/src/plugins/unified_field_list/public/components/field_list_grouped/field_list_grouped.test.tsx index 7cabd28bc171d..9a86b56f52dc7 100644 --- a/src/plugins/unified_field_list/public/components/field_list_grouped/field_list_grouped.test.tsx +++ b/src/plugins/unified_field_list/public/components/field_list_grouped/field_list_grouped.test.tsx @@ -369,7 +369,7 @@ describe('UnifiedFieldList + useGroupedFields()', () => { expect(wrapper.find(`#${defaultProps.screenReaderDescriptionId}`).first().text()).toBe( '1 available field. 4 unmapped fields. 0 empty fields. 0 meta fields.' ); - }); + }, 10000); it('renders correctly when non-supported fields are filtered out', async () => { const hookParams = { diff --git a/src/plugins/unified_search/public/filter_badge/filter_badge_expression.tsx b/src/plugins/unified_search/public/filter_badge/filter_badge_expression.tsx index d01f7977c6b98..1fb9b1353087b 100644 --- a/src/plugins/unified_search/public/filter_badge/filter_badge_expression.tsx +++ b/src/plugins/unified_search/public/filter_badge/filter_badge_expression.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { getDisplayValueFromFilter, getFieldDisplayValueFromFilter } from '@kbn/data-plugin/public'; import type { Filter, DataViewBase } from '@kbn/es-query'; +import { isCombinedFilter } from '@kbn/es-query'; import { EuiTextColor } from '@elastic/eui'; import { FilterBadgeGroup } from './filter_badge_group'; import { FilterContent } from './filter_content'; @@ -54,9 +55,10 @@ export function FilterExpressionBadge({ dataViews, filterLabelStatus, }: FilterBadgeExpressionProps) { + const isCombined = isCombinedFilter(filter); const conditionalOperationType = getBooleanRelationType(filter); - return conditionalOperationType ? ( + return conditionalOperationType && isCombined ? ( <> {shouldShowBrackets && ( diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/filter_editor.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/filter_editor.tsx index 809544c017224..ab33242992067 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/filter_editor.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/filter_editor.tsx @@ -32,6 +32,7 @@ import { buildEmptyFilter, filterToQueryDsl, getFilterParams, + isCombinedFilter, } from '@kbn/es-query'; import { merge } from 'lodash'; import React, { Component } from 'react'; @@ -390,9 +391,10 @@ class FilterEditorComponent extends Component { }; private isUnknownFilterType() { - const { type, params } = this.props.filter.meta; - if (params && type === 'combined') { - return this.hasCombinedFilterCustomType(params); + const { type } = this.props.filter.meta; + if (isCombinedFilter(this.props.filter)) { + const { params } = this.props.filter.meta; + return params && this.hasCombinedFilterCustomType(params); } return !!type && !['phrase', 'phrases', 'range', 'exists', 'combined'].includes(type); } diff --git a/src/plugins/unified_search/public/filters_builder/filter_group.tsx b/src/plugins/unified_search/public/filters_builder/filter_group.tsx index daaefd6f90372..cad3cee7a9063 100644 --- a/src/plugins/unified_search/public/filters_builder/filter_group.tsx +++ b/src/plugins/unified_search/public/filters_builder/filter_group.tsx @@ -17,7 +17,7 @@ import { useEuiPaddingSize, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { type Filter, BooleanRelation } from '@kbn/es-query'; +import { BooleanRelation, type Filter } from '@kbn/es-query'; import { cx } from '@emotion/css'; import type { Path } from './types'; import { getBooleanRelationType } from '../utils'; @@ -85,9 +85,17 @@ export const FilterGroup = ({ const orDisabled = hideOr || (isDepthReached && booleanRelation === BooleanRelation.AND); const andDisabled = isDepthReached && booleanRelation === BooleanRelation.OR; - const removeDisabled = pathInArray.length <= 1 && filters.length === 1; + const removeDisabled = + pathInArray.length <= 1 && + filters !== undefined && + Array.isArray(filters) && + filters.length === 1; const shouldNormalizeFirstLevel = - !path && filters.length === 1 && getBooleanRelationType(filters[0]); + !path && + filters && + Array.isArray(filters) && + filters.length === 1 && + getBooleanRelationType(filters[0]); if (shouldNormalizeFirstLevel) { reverseBackground = true; @@ -96,38 +104,41 @@ export const FilterGroup = ({ const color = reverseBackground ? 'plain' : 'subdued'; - const renderedFilters = filters.map((filter, index, arrayRef) => { - const showDelimiter = booleanRelation && index + 1 < arrayRef.length; - return ( - - - - - - {showDelimiter && ( + const renderedFilters = + filters && + Array.isArray(filters) && + filters.map((filter, index, arrayRef) => { + const showDelimiter = booleanRelation && index + 1 < arrayRef.length; + return ( + - + - )} - - ); - }); + + {showDelimiter && ( + + + + )} + + ); + }); return shouldNormalizeFirstLevel ? ( <>{renderedFilters} diff --git a/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.tsx b/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.tsx index 071a35b289c91..b24429860d416 100644 --- a/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.tsx +++ b/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.tsx @@ -33,6 +33,7 @@ import { FilterGroup } from '../filter_group'; import type { Path } from '../types'; import { getFieldFromFilter, getOperatorFromFilter } from '../../filter_bar/filter_editor'; import { Operator } from '../../filter_bar/filter_editor'; +import { getGroupedFilters } from '../utils/filters_builder'; import { cursorAddCss, cursorOrCss, @@ -101,7 +102,7 @@ export function FilterItem({ const { euiTheme } = useEuiTheme(); let field: DataViewField | undefined; let operator: Operator | undefined; - let params: Filter['meta']['params'] | undefined; + let params: Filter['meta']['params']; const isMaxNesting = isMaxFilterNesting(path); if (!conditionalOperationType) { field = getFieldFromFilter(filter, dataView!); @@ -132,7 +133,7 @@ export function FilterItem({ ); const onHandleParamsChange = useCallback( - (selectedParams: unknown) => { + (selectedParams: Filter['meta']['params']) => { dispatch({ type: 'updateFilter', payload: { dest: { path, index }, field, operator, params: selectedParams }, @@ -146,7 +147,12 @@ export function FilterItem({ const paramsValues = Array.isArray(params) ? params : []; dispatch({ type: 'updateFilter', - payload: { dest: { path, index }, field, operator, params: [...paramsValues, value] }, + payload: { + dest: { path, index }, + field, + operator, + params: [...paramsValues, value] as Filter['meta']['params'], + }, }); }, [dispatch, path, index, field, operator, params] @@ -192,7 +198,7 @@ export function FilterItem({ diff --git a/src/plugins/unified_search/public/filters_builder/filter_item/params_editor.tsx b/src/plugins/unified_search/public/filters_builder/filter_item/params_editor.tsx index a6b1feb46c551..d2ac16e17ebdc 100644 --- a/src/plugins/unified_search/public/filters_builder/filter_item/params_editor.tsx +++ b/src/plugins/unified_search/public/filters_builder/filter_item/params_editor.tsx @@ -8,6 +8,7 @@ import React, { useCallback, useContext } from 'react'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import type { Filter } from '@kbn/es-query'; import { EuiToolTip, EuiFormRow } from '@elastic/eui'; import type { Operator } from '../../filter_bar/filter_editor'; import { getFieldValidityAndErrorMessage } from '../../filter_bar/filter_editor/lib'; @@ -17,8 +18,8 @@ import { ParamsEditorInput } from './params_editor_input'; interface ParamsEditorProps { dataView: DataView; params: unknown; - onHandleParamsChange: (params: unknown) => void; - onHandleParamsUpdate: (value: unknown) => void; + onHandleParamsChange: (params: Filter['meta']['params']) => void; + onHandleParamsUpdate: (value: string) => void; timeRangeForSuggestionsOverride?: boolean; field?: DataViewField; operator?: Operator; diff --git a/src/plugins/unified_search/public/filters_builder/utils/filters_builder.test.ts b/src/plugins/unified_search/public/filters_builder/utils/filters_builder.test.ts index 534237b0ff7bc..337722a756faa 100644 --- a/src/plugins/unified_search/public/filters_builder/utils/filters_builder.test.ts +++ b/src/plugins/unified_search/public/filters_builder/utils/filters_builder.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { buildEmptyFilter, type Filter, BooleanRelation } from '@kbn/es-query'; +import { buildEmptyFilter, type Filter, isCombinedFilter, BooleanRelation } from '@kbn/es-query'; import { DataView } from '@kbn/data-views-plugin/common'; import { getFilterByPath, @@ -188,7 +188,19 @@ describe('filters_builder', () => { beforeAll(() => { filter = filters[0]; filtersWithOrRelationships = filters[1]; - groupOfFilters = filters[1].meta.params[1]; + if (Array.isArray(filters[1].meta.params)) { + const secondFilter = filters[1].meta.params[1]; + if ( + typeof secondFilter !== 'number' && + typeof secondFilter !== 'string' && + typeof secondFilter !== 'boolean' && + isCombinedFilter(secondFilter) + ) { + groupOfFilters = secondFilter; + } + } else { + groupOfFilters = filters[0]; + } }); test('should return correct ConditionalOperationType', () => { diff --git a/src/plugins/unified_search/public/filters_builder/utils/filters_builder.ts b/src/plugins/unified_search/public/filters_builder/utils/filters_builder.ts index 63994fbc1d4e6..212754e891f15 100644 --- a/src/plugins/unified_search/public/filters_builder/utils/filters_builder.ts +++ b/src/plugins/unified_search/public/filters_builder/utils/filters_builder.ts @@ -19,8 +19,14 @@ const PATH_SEPARATOR = '.'; export const getPathInArray = (path: Path) => path.split(PATH_SEPARATOR).map(Number); -const getGroupedFilters = (filter: Filter): Filter[] => - Array.isArray(filter) ? filter : filter?.meta?.params ?? []; +export const getGroupedFilters = (filter: Filter): Filter[] => { + const isCombined = isCombinedFilter(filter); + if (isCombined) { + return filter?.meta?.params ?? []; + } else { + return []; + } +}; const doForFilterByPath = (filters: Filter[], path: Path, action: (filter: Filter) => T) => { const [first, ...restPath] = getPathInArray(path); diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js index 66a01793a0e96..72aee7fa575cf 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js @@ -46,8 +46,9 @@ function getColor(rules, colorKey, value) { } function sanitizeUrl(url) { + const { protocol } = parseUrl(url); // eslint-disable-next-line no-script-url - if (parseUrl(url).protocol === 'javascript:') { + if (protocol === 'javascript:' || protocol === 'data:' || protocol === 'vbscript:') { return ''; } return url; diff --git a/test/functional/apps/discover/group1/_discover.ts b/test/functional/apps/discover/group1/_discover.ts index 1cba5aa4812d8..c3be725737470 100644 --- a/test/functional/apps/discover/group1/_discover.ts +++ b/test/functional/apps/discover/group1/_discover.ts @@ -159,6 +159,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const isVisible = await PageObjects.discover.hasNoResultsTimepicker(); expect(isVisible).to.be(true); }); + + it('should show matches when time range is expanded', async () => { + await PageObjects.discover.expandTimeRangeAsSuggestedInNoResultsMessage(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await retry.try(async function () { + expect(await PageObjects.discover.hasNoResults()).to.be(false); + expect(await PageObjects.discover.getHitCountInt()).to.be.above(0); + }); + }); }); describe('nested query', () => { diff --git a/test/functional/apps/discover/group1/_sidebar.ts b/test/functional/apps/discover/group1/_sidebar.ts index 9d0878b107073..d57b3edfe83e0 100644 --- a/test/functional/apps/discover/group1/_sidebar.ts +++ b/test/functional/apps/discover/group1/_sidebar.ts @@ -577,6 +577,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { '_bytes-runtimefield', `emit((doc["bytes"].value * 2).toString())` ); + + await retry.waitFor('form to close', async () => { + return !(await testSubjects.exists('fieldEditor')); + }); + await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSidebarHasLoaded(); @@ -592,6 +597,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await fieldEditor.setCustomLabel('_bytes-runtimefield2'); await fieldEditor.save(); + await retry.waitFor('form to close', async () => { + return !(await testSubjects.exists('fieldEditor')); + }); + await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSidebarHasLoaded(); diff --git a/test/functional/apps/discover/group2/_adhoc_data_views.ts b/test/functional/apps/discover/group2/_adhoc_data_views.ts index 004eba75d3b6a..9699dc36dd81c 100644 --- a/test/functional/apps/discover/group2/_adhoc_data_views.ts +++ b/test/functional/apps/discover/group2/_adhoc_data_views.ts @@ -272,12 +272,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); const [firstToast, secondToast] = await toasts.getAllToastElements(); - expect(await firstToast.getVisibleText()).to.equal( - `"${first}" is not a configured data view ID\nShowing the saved data view: "logstas*" (${second})` - ); - expect(await secondToast.getVisibleText()).to.equal( - `Different index references\nData view id references in some of the applied filters differ from the current data view.` + expect([await firstToast.getVisibleText(), await secondToast.getVisibleText()].sort()).to.eql( + [ + `"${first}" is not a configured data view ID\nShowing the saved data view: "logstas*" (${second})`, + `Different index references\nData view id references in some of the applied filters differ from the current data view.`, + ].sort() ); }); }); diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index 46d2bd94423f9..8d4438ea91e1b 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -457,6 +457,13 @@ export class DiscoverPageObject extends FtrService { return await this.testSubjects.exists('discoverNoResultsTimefilter'); } + public async expandTimeRangeAsSuggestedInNoResultsMessage() { + await this.retry.waitFor('the button before pressing it', async () => { + return await this.testSubjects.exists('discoverNoResultsViewAllMatches'); + }); + return await this.testSubjects.click('discoverNoResultsViewAllMatches'); + } + public async getSidebarAriaDescription(): Promise { return await ( await this.testSubjects.find('fieldListGrouped__ariaDescription') diff --git a/test/functional/services/dashboard/add_panel.ts b/test/functional/services/dashboard/add_panel.ts index e42c221a49475..f3ee3cad65e1a 100644 --- a/test/functional/services/dashboard/add_panel.ts +++ b/test/functional/services/dashboard/add_panel.ts @@ -25,9 +25,14 @@ export class DashboardAddPanelService extends FtrService { async clickCreateNewLink() { this.log.debug('DashboardAddPanel.clickAddNewPanelButton'); - await this.testSubjects.click('dashboardAddNewPanelButton'); - // Give some time for the animation to complete - await this.common.sleep(500); + await this.retry.try(async () => { + await this.testSubjects.click('dashboardAddNewPanelButton'); + await this.testSubjects.waitForDeleted('dashboardAddNewPanelButton'); + await this.header.waitUntilLoadingHasFinished(); + await this.testSubjects.existOrFail('lnsApp', { + timeout: 5000, + }); + }); } async clickQuickButton(visType: string) { diff --git a/test/functional/services/filter_bar.ts b/test/functional/services/filter_bar.ts index 30cb97d3d6519..7e28176e764e7 100644 --- a/test/functional/services/filter_bar.ts +++ b/test/functional/services/filter_bar.ts @@ -302,7 +302,7 @@ export class FilterBarService extends FtrService { await this.createFilter(filter); - await this.testSubjects.clickWhenNotDisabledWithoutRetry('saveFilter'); + await this.testSubjects.clickWhenNotDisabled('saveFilter'); }); await this.header.awaitGlobalLoadingIndicatorHidden(); } diff --git a/x-pack/plugins/alerting/kibana.json b/x-pack/plugins/alerting/kibana.json index 9b17c97ae722e..ab0b708d59ee5 100644 --- a/x-pack/plugins/alerting/kibana.json +++ b/x-pack/plugins/alerting/kibana.json @@ -8,7 +8,10 @@ }, "version": "8.0.0", "kibanaVersion": "kibana", - "configPath": ["xpack", "alerting"], + "configPath": [ + "xpack", + "alerting" + ], "requiredPlugins": [ "actions", "data", @@ -19,9 +22,16 @@ "features", "kibanaUtils", "licensing", - "spaces", "taskManager" ], - "optionalPlugins": ["usageCollection", "security", "monitoringCollection"], - "extraPublicDirs": ["common", "common/parse_duration"] -} + "optionalPlugins": [ + "usageCollection", + "security", + "monitoringCollection", + "spaces" + ], + "extraPublicDirs": [ + "common", + "common/parse_duration" + ] +} \ No newline at end of file diff --git a/x-pack/plugins/alerting/server/alerting_authorization_client_factory.ts b/x-pack/plugins/alerting/server/alerting_authorization_client_factory.ts index 0ee4cce387d18..b0830a7127a38 100644 --- a/x-pack/plugins/alerting/server/alerting_authorization_client_factory.ts +++ b/x-pack/plugins/alerting/server/alerting_authorization_client_factory.ts @@ -16,7 +16,7 @@ export interface AlertingAuthorizationClientFactoryOpts { ruleTypeRegistry: RuleTypeRegistry; securityPluginSetup?: SecurityPluginSetup; securityPluginStart?: SecurityPluginStart; - getSpace: (request: KibanaRequest) => Promise; + getSpace: (request: KibanaRequest) => Promise; getSpaceId: (request: KibanaRequest) => string; features: FeaturesPluginStart; } @@ -26,7 +26,7 @@ export class AlertingAuthorizationClientFactory { private ruleTypeRegistry!: RuleTypeRegistry; private securityPluginStart?: SecurityPluginStart; private features!: FeaturesPluginStart; - private getSpace!: (request: KibanaRequest) => Promise; + private getSpace!: (request: KibanaRequest) => Promise; private getSpaceId!: (request: KibanaRequest) => string; public initialize(options: AlertingAuthorizationClientFactoryOpts) { diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts index 3d7f3b10390fe..11f57e8145e95 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts @@ -860,6 +860,45 @@ describe('AlertingEventLogger', () => { expect(alertingEventLogger.getEvent()).toEqual(loggedEvent); expect(eventLogger.logEvent).toHaveBeenCalledWith(loggedEvent); }); + + test('overwrites the message when the final status is error', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.setExecutionSucceeded('success message'); + + expect(alertingEventLogger.getEvent()!.message).toBe('success message'); + + alertingEventLogger.done({ + status: { + status: 'error', + lastExecutionDate: new Date(), + error: { reason: RuleExecutionStatusErrorReasons.Execute, message: 'failed execution' }, + }, + }); + + expect(alertingEventLogger.getEvent()!.message).toBe('test:123: execution failed'); + }); + + test('does not overwrites the message when there is already a failure message', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.setExecutionFailed('first failure message', 'failure error message'); + + expect(alertingEventLogger.getEvent()!.message).toBe('first failure message'); + + alertingEventLogger.done({ + status: { + status: 'error', + lastExecutionDate: new Date(), + error: { + reason: RuleExecutionStatusErrorReasons.Execute, + message: 'second failure execution', + }, + }, + }); + + expect(alertingEventLogger.getEvent()!.message).toBe('first failure message'); + }); }); }); diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts index 3422fb21bb1f9..fff026a358bc8 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts @@ -184,7 +184,7 @@ export class AlertingEventLogger { alertingOutcome: 'failure', reason: status.error?.reason || 'unknown', error: this.event?.error?.message || status.error.message, - ...(this.event.message + ...(this.event.message && this.event.event?.outcome === 'failure' ? {} : { message: `${this.ruleContext.ruleType.id}:${this.ruleContext.ruleId}: execution failed`, diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 5ae4be21d9a63..13f6e2ae1c9dc 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -20,6 +20,7 @@ import { TaskManagerSetupContract, TaskManagerStartContract, } from '@kbn/task-manager-plugin/server'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { SpacesPluginStart } from '@kbn/spaces-plugin/server'; import { KibanaRequest, @@ -162,7 +163,7 @@ export interface AlertingPluginsStart { features: FeaturesPluginStart; eventLog: IEventLogClientService; licensing: LicensingPluginStart; - spaces: SpacesPluginStart; + spaces?: SpacesPluginStart; security?: SecurityPluginStart; data: DataPluginStart; dataViews: DataViewsPluginStart; @@ -417,10 +418,10 @@ export class AlertingPlugin { securityPluginSetup: security, securityPluginStart: plugins.security, async getSpace(request: KibanaRequest) { - return plugins.spaces.spacesService.getActiveSpace(request); + return plugins.spaces?.spacesService.getActiveSpace(request); }, getSpaceId(request: KibanaRequest) { - return plugins.spaces.spacesService.getSpaceId(request); + return plugins.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; }, features: plugins.features, }); @@ -434,7 +435,7 @@ export class AlertingPlugin { encryptedSavedObjectsClient, spaceIdToNamespace, getSpaceId(request: KibanaRequest) { - return plugins.spaces?.spacesService.getSpaceId(request); + return plugins.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; }, actions: plugins.actions, eventLog: plugins.eventLog, diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts index 20a5623edd392..3c97080e2d655 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts @@ -26,6 +26,8 @@ export async function validateActions( return; } + const errors = []; + // check for actions using connectors with missing secrets const actionsClient = await context.getActionsClient(); const actionIds = [...new Set(actions.map((action) => action.id))]; @@ -35,7 +37,7 @@ export async function validateActions( ); if (actionsUsingConnectorsWithMissingSecrets.length) { - throw Boom.badRequest( + errors.push( i18n.translate('xpack.alerting.rulesClient.validateActions.misconfiguredConnector', { defaultMessage: 'Invalid connectors: {groups}', values: { @@ -55,7 +57,7 @@ export async function validateActions( (group) => !availableAlertTypeActionGroups.has(group) ); if (invalidActionGroups.length) { - throw Boom.badRequest( + errors.push( i18n.translate('xpack.alerting.rulesClient.validateActions.invalidGroups', { defaultMessage: 'Invalid action groups: {groups}', values: { @@ -69,7 +71,7 @@ export async function validateActions( if (hasRuleLevelNotifyWhen || hasRuleLevelThrottle) { const actionsWithFrequency = actions.filter((action) => Boolean(action.frequency)); if (actionsWithFrequency.length) { - throw Boom.badRequest( + errors.push( i18n.translate('xpack.alerting.rulesClient.validateActions.mixAndMatchFreqParams', { defaultMessage: 'Cannot specify per-action frequency params when notify_when or throttle are defined at the rule level: {groups}', @@ -82,7 +84,7 @@ export async function validateActions( } else { const actionsWithoutFrequency = actions.filter((action) => !action.frequency); if (actionsWithoutFrequency.length) { - throw Boom.badRequest( + errors.push( i18n.translate('xpack.alerting.rulesClient.validateActions.notAllActionsWithFreq', { defaultMessage: 'Actions missing frequency parameters: {groups}', values: { @@ -101,7 +103,7 @@ export async function validateActions( parseDuration(action.frequency.throttle!) < scheduleInterval ); if (actionsWithInvalidThrottles.length) { - throw Boom.badRequest( + errors.push( i18n.translate('xpack.alerting.rulesClient.validateActions.actionsWithInvalidThrottles', { defaultMessage: 'Action throttle cannot be shorter than the schedule interval of {scheduleIntervalText}: {groups}', @@ -114,4 +116,18 @@ export async function validateActions( }) ); } + + // Finalize and throw any errors present + if (errors.length) { + throw Boom.badRequest( + i18n.translate('xpack.alerting.rulesClient.validateActions.errorSummary', { + defaultMessage: + 'Failed to validate actions due to the following {errorNum, plural, one {error:} other {# errors:\n-}} {errorList}', + values: { + errorNum: errors.length, + errorList: errors.join('\n- '), + }, + }) + ); + } } diff --git a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts index 7963ebd885a77..c11dc8be21ca4 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts @@ -2602,7 +2602,7 @@ describe('create()', () => { }, ]); await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Invalid connectors: email connector"` + `"Failed to validate actions due to the following error: Invalid connectors: email connector"` ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); @@ -2760,7 +2760,7 @@ describe('create()', () => { ], }); await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Cannot specify per-action frequency params when notify_when or throttle are defined at the rule level: default, group2"` + `"Failed to validate actions due to the following error: Cannot specify per-action frequency params when notify_when or throttle are defined at the rule level: default, group2"` ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); @@ -2790,7 +2790,7 @@ describe('create()', () => { ], }); await expect(rulesClient.create({ data: data2 })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Cannot specify per-action frequency params when notify_when or throttle are defined at the rule level: default"` + `"Failed to validate actions due to the following error: Cannot specify per-action frequency params when notify_when or throttle are defined at the rule level: default"` ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); @@ -2833,7 +2833,7 @@ describe('create()', () => { ], }); await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Actions missing frequency parameters: default"` + `"Failed to validate actions due to the following error: Actions missing frequency parameters: default"` ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); @@ -2891,7 +2891,7 @@ describe('create()', () => { ], }); await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Actions missing frequency parameters: group2"` + `"Failed to validate actions due to the following error: Actions missing frequency parameters: group2"` ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); @@ -2968,9 +2968,83 @@ describe('create()', () => { ], }); await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Action throttle cannot be shorter than the schedule interval of 3h: default (1h), group2 (3m)"` + `"Failed to validate actions due to the following error: Action throttle cannot be shorter than the schedule interval of 3h: default (1h), group2 (3m)"` ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); }); + + test('throws multiple errors when actions have multiple problems', async () => { + rulesClient = new RulesClient({ + ...rulesClientParams, + minimumScheduleInterval: { value: '1m', enforce: true }, + }); + ruleTypeRegistry.get.mockImplementation(() => ({ + id: '123', + name: 'Test', + actionGroups: [ + { id: 'default', name: 'Default' }, + { id: 'group2', name: 'Action Group 2' }, + { id: 'group3', name: 'Action Group 3' }, + ], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + async executor() { + return { state: {} }; + }, + producer: 'alerts', + useSavedObjectReferences: { + extractReferences: jest.fn(), + injectReferences: jest.fn(), + }, + })); + + const data = getMockData({ + notifyWhen: undefined, + throttle: undefined, + schedule: { interval: '3h' }, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + frequency: { + summary: false, + notifyWhen: 'onThrottleInterval', + throttle: '1h', + }, + }, + { + group: 'group2', + id: '2', + params: { + foo: true, + }, + frequency: { + summary: false, + notifyWhen: 'onThrottleInterval', + throttle: '3m', + }, + }, + { + group: 'group3', + id: '3', + params: { + foo: true, + }, + }, + ], + }); + await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot(` + "Failed to validate actions due to the following 2 errors: + - Actions missing frequency parameters: group3 + - Action throttle cannot be shorter than the schedule interval of 3h: default (1h), group2 (3m)" + `); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + expect(taskManager.schedule).not.toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts index 0de571c72916b..44c4eeb50fe27 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts @@ -1767,7 +1767,7 @@ describe('update()', () => { }, }) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Cannot specify per-action frequency params when notify_when or throttle are defined at the rule level: default, group2"` + `"Failed to validate actions due to the following error: Cannot specify per-action frequency params when notify_when or throttle are defined at the rule level: default, group2"` ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); @@ -1808,7 +1808,7 @@ describe('update()', () => { }, }) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Cannot specify per-action frequency params when notify_when or throttle are defined at the rule level: default"` + `"Failed to validate actions due to the following error: Cannot specify per-action frequency params when notify_when or throttle are defined at the rule level: default"` ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); @@ -1844,7 +1844,7 @@ describe('update()', () => { }, }) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Actions missing frequency parameters: default"` + `"Failed to validate actions due to the following error: Actions missing frequency parameters: default"` ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); @@ -1892,7 +1892,7 @@ describe('update()', () => { }, }) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Actions missing frequency parameters: default"` + `"Failed to validate actions due to the following error: Actions missing frequency parameters: default"` ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); @@ -2016,7 +2016,9 @@ describe('update()', () => { ], }, }) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Invalid connectors: another connector"`); + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Failed to validate actions due to the following error: Invalid connectors: another connector"` + ); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); }); diff --git a/x-pack/plugins/apm/public/components/app/service_logs/index.test.ts b/x-pack/plugins/apm/public/components/app/service_logs/index.test.ts index dc0c486deeed9..2c3f5b49460fe 100644 --- a/x-pack/plugins/apm/public/components/app/service_logs/index.test.ts +++ b/x-pack/plugins/apm/public/components/app/service_logs/index.test.ts @@ -38,7 +38,7 @@ describe('service logs', () => { ); }); - it('filter by host names as fallback', () => { + it('does not filter by host names as fallback', () => { expect( getInfrastructureKQLFilter( { @@ -48,9 +48,7 @@ describe('service logs', () => { }, serviceName ) - ).toEqual( - 'service.name: "opbeans-node" or (not service.name and (host.name: "baz" or host.name: "quz"))' - ); + ).toEqual('service.name: "opbeans-node"'); }); }); }); diff --git a/x-pack/plugins/apm/public/components/app/service_logs/index.tsx b/x-pack/plugins/apm/public/components/app/service_logs/index.tsx index 6616eee3d85d1..02cdf96793982 100644 --- a/x-pack/plugins/apm/public/components/app/service_logs/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_logs/index.tsx @@ -12,11 +12,7 @@ import { useFetcher } from '../../../hooks/use_fetcher'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { APIReturnType } from '../../../services/rest/create_call_apm_api'; -import { - CONTAINER_ID, - HOST_NAME, - SERVICE_NAME, -} from '../../../../common/es_fields/apm'; +import { CONTAINER_ID, SERVICE_NAME } from '../../../../common/es_fields/apm'; import { useApmParams } from '../../../hooks/use_apm_params'; import { useTimeRange } from '../../../hooks/use_time_range'; @@ -70,16 +66,12 @@ export const getInfrastructureKQLFilter = ( | undefined, serviceName: string ) => { - const containerIds = data?.containerIds ?? []; - const hostNames = data?.hostNames ?? []; + const containerIds: string[] = data?.containerIds ?? []; + const containerIdKql = containerIds + .map((id) => `${CONTAINER_ID}: "${id}"`) + .join(' or '); - const infraAttributes = containerIds.length - ? containerIds.map((id) => `${CONTAINER_ID}: "${id}"`) - : hostNames.map((id) => `${HOST_NAME}: "${id}"`); - - const infraAttributesJoined = infraAttributes.join(' or '); - - return infraAttributes.length - ? `${SERVICE_NAME}: "${serviceName}" or (not ${SERVICE_NAME} and (${infraAttributesJoined}))` + return containerIds.length + ? `${SERVICE_NAME}: "${serviceName}" or (not ${SERVICE_NAME} and (${containerIdKql}))` : `${SERVICE_NAME}: "${serviceName}"`; }; diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx index e130802408d4c..5b3118e527fa4 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx @@ -23,6 +23,7 @@ import { } from '../../../utils/test_helpers'; import { fromQuery } from '../../shared/links/url_helpers'; import { TransactionOverview } from '.'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; const KibanaReactContext = createKibanaReactContext({ uiSettings: { get: () => true }, @@ -64,15 +65,17 @@ function setup({ jest.spyOn(useFetcherHook, 'useFetcher').mockReturnValue({} as any); return renderWithTheme( - - - - - - - - - + + + + + + + + + + + ); } diff --git a/x-pack/plugins/apm/public/components/shared/stacktrace/index.tsx b/x-pack/plugins/apm/public/components/shared/stacktrace/index.tsx index eb32a2aa8ce4d..4a3646dbb78c1 100644 --- a/x-pack/plugins/apm/public/components/shared/stacktrace/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/stacktrace/index.tsx @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import { isEmpty, last } from 'lodash'; import React, { Fragment } from 'react'; +import { EuiCodeBlock } from '@elastic/eui'; import { Stackframe } from '../../../../typings/es_schemas/raw/fields/stackframe'; import { EmptyMessage } from '../empty_message'; import { LibraryStacktrace } from './library_stacktrace'; @@ -36,34 +37,36 @@ export function Stacktrace({ stackframes = [], codeLanguage }: Props) { const groups = getGroupedStackframes(stackframes); return ( - - {groups.map((group, i) => { - // library frame - if (group.isLibraryFrame && groups.length > 1) { - return ( - - + + {groups.map((group, i) => { + // library frame + if (group.isLibraryFrame && groups.length > 1) { + return ( + + + + ); + } + + // non-library frame + return group.stackframes.map((stackframe, idx) => ( + + 1} + stackframe={stackframe} /> - ); - } - - // non-library frame - return group.stackframes.map((stackframe, idx) => ( - - 1} - stackframe={stackframe} - /> - - )); - })} - + )); + })} + + ); } diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index ce5c58bcffe35..86907fdd570be 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -225,6 +225,9 @@ export class APMPlugin coreStartPromise: getCoreStart(), plugins: resourcePlugins, config: currentConfig, + }).catch((e) => { + this.logger?.error('Failed to register APM Fleet policy callbacks'); + this.logger?.error(e); }); // This will add an API key to all existing APM package policies @@ -232,6 +235,9 @@ export class APMPlugin coreStartPromise: getCoreStart(), pluginStartPromise: getPluginStart(), logger: this.logger, + }).catch((e) => { + this.logger?.error('Failed to add API keys to APM package policies'); + this.logger?.error(e); }); const taskManager = plugins.taskManager; diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts index b55e2888c3c7f..75e1aef357088 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts @@ -59,8 +59,8 @@ import { } from '../get_service_group_fields'; const paramsSchema = schema.object({ - serviceName: schema.string(), - transactionType: schema.string(), + serviceName: schema.maybe(schema.string()), + transactionType: schema.maybe(schema.string()), windowSize: schema.number(), windowUnit: schema.string(), threshold: schema.number(), diff --git a/x-pack/plugins/apm/server/routes/fleet/register_fleet_policy_callbacks.ts b/x-pack/plugins/apm/server/routes/fleet/register_fleet_policy_callbacks.ts index 9057ab065cacb..13815779c6013 100644 --- a/x-pack/plugins/apm/server/routes/fleet/register_fleet_policy_callbacks.ts +++ b/x-pack/plugins/apm/server/routes/fleet/register_fleet_policy_callbacks.ts @@ -75,7 +75,6 @@ function onPackagePolicyDelete({ logger: Logger; }): PostPackagePolicyDeleteCallback { return async (packagePolicies) => { - // console.log(`packagePolicyDelete:`, packagePolicies); const promises = packagePolicies.map(async (packagePolicy) => { if (packagePolicy.package?.name !== 'apm') { return packagePolicy; diff --git a/x-pack/plugins/apm/server/routes/source_maps/create_apm_source_map_index_template.ts b/x-pack/plugins/apm/server/routes/source_maps/create_apm_source_map_index_template.ts index 25a7cf5ae5815..4fa24358e7b07 100644 --- a/x-pack/plugins/apm/server/routes/source_maps/create_apm_source_map_index_template.ts +++ b/x-pack/plugins/apm/server/routes/source_maps/create_apm_source_map_index_template.ts @@ -17,8 +17,9 @@ const indexTemplate: IndicesPutIndexTemplateRequest = { index_patterns: [APM_SOURCE_MAP_INDEX], template: { settings: { - number_of_shards: 1, index: { + number_of_shards: 1, + auto_expand_replicas: '0-2', hidden: true, }, }, diff --git a/x-pack/plugins/banners/server/ui_settings.ts b/x-pack/plugins/banners/server/ui_settings.ts index 7d6f12589d164..fe16b6592dd5f 100644 --- a/x-pack/plugins/banners/server/ui_settings.ts +++ b/x-pack/plugins/banners/server/ui_settings.ts @@ -30,8 +30,7 @@ export const registerSettings = (uiSettings: UiSettingsServiceSetup, config: Ban defaultMessage: 'Banner placement', }), description: i18n.translate('xpack.banners.settings.placement.description', { - defaultMessage: - 'Display a top banner for this space, above the Elastic header. {subscriptionLink}', + defaultMessage: 'Display a top banner above the Elastic header. {subscriptionLink}', values: { subscriptionLink, }, diff --git a/x-pack/plugins/cases/common/api/cases/comment.ts b/x-pack/plugins/cases/common/api/cases/comment.ts index 66204d829b6d4..b768b8f0b593f 100644 --- a/x-pack/plugins/cases/common/api/cases/comment.ts +++ b/x-pack/plugins/cases/common/api/cases/comment.ts @@ -279,6 +279,22 @@ export const FindQueryParamsRt = rt.partial({ export const BulkCreateCommentRequestRt = rt.array(CommentRequestRt); +export const BulkGetAttachmentsRequestRt = rt.type({ + ids: rt.array(rt.string), +}); + +export const BulkGetAttachmentsResponseRt = rt.type({ + attachments: AllCommentsResponseRt, + errors: rt.array( + rt.type({ + error: rt.string, + message: rt.string, + status: rt.union([rt.undefined, rt.number]), + attachmentId: rt.string, + }) + ), +}); + export type FindQueryParams = rt.TypeOf; export type AttributesTypeActions = rt.TypeOf; export type AttributesTypeAlerts = rt.TypeOf; @@ -317,3 +333,5 @@ export type CommentRequestExternalReferenceType = rt.TypeOf; export type CommentRequestExternalReferenceNoSOType = rt.TypeOf; export type CommentRequestPersistableStateType = rt.TypeOf; +export type BulkGetAttachmentsResponse = rt.TypeOf; +export type BulkGetAttachmentsRequest = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/api/cases/user_actions/response.ts b/x-pack/plugins/cases/common/api/cases/user_actions/response.ts index b36cc7104001c..8f0ef5517db4f 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions/response.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions/response.ts @@ -8,16 +8,17 @@ import * as rt from 'io-ts'; import type { ActionsRt, ActionTypeValues } from './common'; + import { CaseUserActionInjectedIdsRt, CaseUserActionInjectedDeprecatedIdsRt, UserActionCommonAttributesRt, } from './common'; -import { CreateCaseUserActionRt } from './create_case'; +import { CreateCaseUserActionRt, CreateCaseUserActionWithoutConnectorIdRt } from './create_case'; import { DescriptionUserActionRt } from './description'; import { CommentUserActionRt } from './comment'; -import { ConnectorUserActionRt } from './connector'; -import { PushedUserActionRt } from './pushed'; +import { ConnectorUserActionRt, ConnectorUserActionWithoutConnectorIdRt } from './connector'; +import { PushedUserActionRt, PushedUserActionWithoutConnectorIdRt } from './pushed'; import { TagsUserActionRt } from './tags'; import { TitleUserActionRt } from './title'; import { SettingsUserActionRt } from './settings'; @@ -25,6 +26,7 @@ import { StatusUserActionRt } from './status'; import { DeleteCaseUserActionRt } from './delete_case'; import { SeverityUserActionRt } from './severity'; import { AssigneesUserActionRt } from './assignees'; +import { CaseUserActionStatsRt } from './stats'; const CommonUserActionsRt = rt.union([ DescriptionUserActionRt, @@ -45,11 +47,11 @@ export const UserActionsRt = rt.union([ DeleteCaseUserActionRt, ]); -export const UserActionsWithoutConnectorIdRt = rt.union([ +const UserActionsWithoutConnectorIdRt = rt.union([ CommonUserActionsRt, - CreateCaseUserActionRt, - ConnectorUserActionRt, - PushedUserActionRt, + CreateCaseUserActionWithoutConnectorIdRt, + ConnectorUserActionWithoutConnectorIdRt, + PushedUserActionWithoutConnectorIdRt, DeleteCaseUserActionRt, ]); @@ -80,14 +82,16 @@ const CaseUserActionResponseRt = rt.intersection([ }), ]); -export const CaseUserActionAttributesRt = CaseUserActionBasicRt; +const CaseUserActionAttributesRt = CaseUserActionBasicRt; export const CaseUserActionsResponseRt = rt.array(CaseUserActionResponseRt); export const CaseUserActionsDeprecatedResponseRt = rt.array(CaseUserActionDeprecatedResponseRt); +export const CaseUserActionStatsResponseRt = CaseUserActionStatsRt; export type CaseUserActionAttributes = rt.TypeOf; export type CaseUserActionAttributesWithoutConnectorId = rt.TypeOf< - typeof CaseUserActionAttributesRt + typeof CaseUserActionBasicWithoutConnectorIdRt >; +export type CaseUserActionStatsResponse = rt.TypeOf; export type CaseUserActionsResponse = rt.TypeOf; export type CaseUserActionResponse = rt.TypeOf; export type CaseUserActionsDeprecatedResponse = rt.TypeOf< @@ -100,6 +104,3 @@ export type UserAction = rt.TypeOf; export type UserActionTypes = ActionTypeValues; export type CaseUserAction = rt.TypeOf; -export type CaseUserActionWithoutConnectorId = rt.TypeOf< - typeof CaseUserActionBasicWithoutConnectorIdRt ->; diff --git a/x-pack/plugins/cases/common/api/cases/user_actions/stats.ts b/x-pack/plugins/cases/common/api/cases/user_actions/stats.ts new file mode 100644 index 0000000000000..de0a6439e0bdb --- /dev/null +++ b/x-pack/plugins/cases/common/api/cases/user_actions/stats.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 rt from 'io-ts'; + +export const CaseUserActionStatsRt = rt.type({ + total: rt.number, + total_comments: rt.number, + total_other_actions: rt.number, +}); + +export type CaseUserActionStats = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/api/connectors/connector.ts b/x-pack/plugins/cases/common/api/connectors/connector.ts index f3c733c85cd8c..7d96593d01316 100644 --- a/x-pack/plugins/cases/common/api/connectors/connector.ts +++ b/x-pack/plugins/cases/common/api/connectors/connector.ts @@ -18,14 +18,6 @@ import { SwimlaneFieldsRT } from './swimlane'; export type ActionConnector = ActionResult; export type ActionTypeConnector = ActionType; -export const ConnectorFieldsRt = rt.union([ - JiraFieldsRT, - ResilientFieldsRT, - ServiceNowITSMFieldsRT, - ServiceNowSIRFieldsRT, - rt.null, -]); - export enum ConnectorTypes { casesWebhook = '.cases-webhook', jira = '.jira', @@ -114,6 +106,3 @@ export type ConnectorServiceNowITSMTypeFields = rt.TypeOf< typeof ConnectorServiceNowITSMTypeFieldsRt >; export type ConnectorServiceNowSIRTypeFields = rt.TypeOf; - -// we need to change these types back and forth for storing in ES (arrays overwrite, objects merge) -export type ConnectorFields = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/api/connectors/get_connectors.ts b/x-pack/plugins/cases/common/api/connectors/get_connectors.ts index 5c29d585287da..8342c270b160e 100644 --- a/x-pack/plugins/cases/common/api/connectors/get_connectors.ts +++ b/x-pack/plugins/cases/common/api/connectors/get_connectors.ts @@ -9,15 +9,19 @@ import * as rt from 'io-ts'; import { CaseConnectorRt } from './connector'; import { CaseExternalServiceBasicRt } from '../cases'; +const PushDetailsRt = rt.type({ + latestUserActionPushDate: rt.string, + oldestUserActionPushDate: rt.string, + externalService: CaseExternalServiceBasicRt, +}); + const CaseConnectorPushInfoRt = rt.intersection([ rt.type({ needsToBePushed: rt.boolean, hasBeenPushed: rt.boolean, }), rt.partial({ - latestUserActionPushDate: rt.string, - oldestUserActionPushDate: rt.string, - externalService: CaseExternalServiceBasicRt, + details: PushDetailsRt, }), ]); @@ -32,3 +36,4 @@ export const GetCaseConnectorsResponseRt = rt.record( ); export type GetCaseConnectorsResponse = rt.TypeOf; +export type GetCaseConnectorsPushDetails = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/api/helpers.ts b/x-pack/plugins/cases/common/api/helpers.ts index 2f2fe14a4f991..746f2bd6972ab 100644 --- a/x-pack/plugins/cases/common/api/helpers.ts +++ b/x-pack/plugins/cases/common/api/helpers.ts @@ -16,6 +16,9 @@ import { CASE_ALERTS_URL, CASE_COMMENT_DELETE_URL, CASE_FIND_USER_ACTIONS_URL, + INTERNAL_GET_CASE_USER_ACTIONS_STATS_URL, + INTERNAL_BULK_GET_ATTACHMENTS_URL, + INTERNAL_CONNECTORS_URL, } from '../constants'; export const getCaseDetailsUrl = (id: string): string => { @@ -42,6 +45,10 @@ export const getCaseUserActionUrl = (id: string): string => { return CASE_USER_ACTIONS_URL.replace('{case_id}', id); }; +export const getCaseUserActionStatsUrl = (id: string): string => { + return INTERNAL_GET_CASE_USER_ACTIONS_STATS_URL.replace('{case_id}', id); +}; + export const getCaseFindUserActionsUrl = (id: string): string => { return CASE_FIND_USER_ACTIONS_URL.replace('{case_id}', id); }; @@ -57,3 +64,11 @@ export const getCaseConfigurationDetailsUrl = (configureID: string): string => { export const getCasesFromAlertsUrl = (alertId: string): string => { return CASE_ALERTS_URL.replace('{alert_id}', alertId); }; + +export const getCaseBulkGetAttachmentsUrl = (id: string): string => { + return INTERNAL_BULK_GET_ATTACHMENTS_URL.replace('{case_id}', id); +}; + +export const getCaseConnectorsUrl = (id: string): string => { + return INTERNAL_CONNECTORS_URL.replace('{case_id}', id); +}; diff --git a/x-pack/plugins/cases/common/constants.ts b/x-pack/plugins/cases/common/constants.ts index c1e65087224cf..c359353f80b80 100644 --- a/x-pack/plugins/cases/common/constants.ts +++ b/x-pack/plugins/cases/common/constants.ts @@ -88,10 +88,14 @@ export const CASE_METRICS_DETAILS_URL = `${CASES_URL}/metrics/{case_id}` as cons export const CASES_INTERNAL_URL = '/internal/cases' as const; export const INTERNAL_BULK_CREATE_ATTACHMENTS_URL = `${CASES_INTERNAL_URL}/{case_id}/attachments/_bulk_create` as const; +export const INTERNAL_BULK_GET_ATTACHMENTS_URL = + `${CASES_INTERNAL_URL}/{case_id}/attachments/_bulk_get` as const; export const INTERNAL_SUGGEST_USER_PROFILES_URL = `${CASES_INTERNAL_URL}/_suggest_user_profiles` as const; export const INTERNAL_CONNECTORS_URL = `${CASES_INTERNAL_URL}/{case_id}/_connectors` as const; export const INTERNAL_BULK_GET_CASES_URL = `${CASES_INTERNAL_URL}/_bulk_get` as const; +export const INTERNAL_GET_CASE_USER_ACTIONS_STATS_URL = + `${CASES_INTERNAL_URL}/{case_id}/user_actions/_stats` as const; /** * Action routes @@ -138,6 +142,7 @@ export const OWNER_INFO = { * Searching */ export const MAX_DOCS_PER_PAGE = 10000 as const; +export const MAX_BULK_GET_ATTACHMENTS = MAX_DOCS_PER_PAGE; export const MAX_CONCURRENT_SEARCHES = 10 as const; export const MAX_BULK_GET_CASES = 1000 as const; diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index 8231dd945bf9e..f4df0c1b72f98 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -17,7 +17,6 @@ import type { CaseStatuses, User, ActionConnector, - CaseExternalServiceBasic, CaseUserActionResponse, SingleCaseMetricsResponse, CommentResponse, @@ -30,6 +29,7 @@ import type { CaseSeverity, CommentResponseExternalReferenceType, CommentResponseTypePersistableState, + GetCaseConnectorsResponse, } from '../api'; import type { PUSH_CASES_CAPABILITY } from '../constants'; import type { SnakeToCamelCase } from '../types'; @@ -81,12 +81,12 @@ export type CaseUserActions = SnakeToCamelCase; export type FindCaseUserActions = Omit, 'userActions'> & { userActions: CaseUserActions[]; }; -export type CaseExternalService = SnakeToCamelCase; export type Case = Omit, 'comments'> & { comments: Comment[] }; export type Cases = Omit, 'cases'> & { cases: Case[] }; export type CasesStatus = SnakeToCamelCase; export type CasesMetrics = SnakeToCamelCase; export type CaseUpdateRequest = SnakeToCamelCase; +export type CaseConnectors = SnakeToCamelCase; export interface ResolvedCase { case: Case; diff --git a/x-pack/plugins/cases/docs/openapi/bundled.json b/x-pack/plugins/cases/docs/openapi/bundled.json index 7260061d76b0b..37647324ccf7a 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.json +++ b/x-pack/plugins/cases/docs/openapi/bundled.json @@ -2111,9 +2111,9 @@ "description": "The page number to return.", "schema": { "type": "string", - "default": 1 + "default": "1" }, - "example": 1 + "example": "1" }, { "name": "perPage", @@ -2121,9 +2121,9 @@ "description": "The number of user actions to return per page.", "schema": { "type": "string", - "default": 20 + "default": "20" }, - "example": 20 + "example": "20" }, { "name": "sortOrder", @@ -2245,6 +2245,7 @@ }, "in": "header", "name": "kbn-xsrf", + "description": "Cross-site request forgery protection", "required": true }, "space_id": { diff --git a/x-pack/plugins/cases/docs/openapi/bundled.yaml b/x-pack/plugins/cases/docs/openapi/bundled.yaml index aa55e180191cb..8098a2d8787ff 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.yaml +++ b/x-pack/plugins/cases/docs/openapi/bundled.yaml @@ -1309,15 +1309,15 @@ paths: description: The page number to return. schema: type: string - default: 1 - example: 1 + default: '1' + example: '1' - name: perPage in: query description: The number of user actions to return per page. schema: type: string - default: 20 - example: 20 + default: '20' + example: '20' - name: sortOrder in: query description: Determines the sort order. @@ -1398,6 +1398,7 @@ components: type: string in: header name: kbn-xsrf + description: Cross-site request forgery protection required: true space_id: in: path diff --git a/x-pack/plugins/cases/docs/openapi/components/headers/kbn_xsrf.yaml b/x-pack/plugins/cases/docs/openapi/components/headers/kbn_xsrf.yaml index 3d8dfae634e68..fe0402a43aa03 100644 --- a/x-pack/plugins/cases/docs/openapi/components/headers/kbn_xsrf.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/headers/kbn_xsrf.yaml @@ -2,4 +2,5 @@ schema: type: string in: header name: kbn-xsrf +description: Cross-site request forgery protection required: true diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@user_actions@_find.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@user_actions@_find.yaml index 8ec83b50416ef..8cdeca5b9a7a9 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@user_actions@_find.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@user_actions@_find.yaml @@ -15,15 +15,15 @@ get: description: The page number to return. schema: type: string - default: 1 - example: 1 + default: "1" + example: "1" - name: perPage in: query description: The number of user actions to return per page. schema: type: string - default: 20 - example: 20 + default: "20" + example: "20" - name: sortOrder in: query description: Determines the sort order. diff --git a/x-pack/plugins/cases/public/common/mock/connectors.ts b/x-pack/plugins/cases/public/common/mock/connectors.ts index e0b7b26a4c8a4..a036b5a6b8981 100644 --- a/x-pack/plugins/cases/public/common/mock/connectors.ts +++ b/x-pack/plugins/cases/public/common/mock/connectors.ts @@ -5,13 +5,16 @@ * 2.0. */ +import { set } from 'lodash'; import type { ActionConnector, ActionTypeConnector } from '../../../common/api'; +import { basicPush } from '../../containers/mock'; +import type { CaseConnectors } from '../../containers/types'; export const connectorsMock: ActionConnector[] = [ { id: 'servicenow-1', actionTypeId: '.servicenow', - name: 'My Connector', + name: 'My SN connector', config: { apiUrl: 'https://instance1.service-now.com', }, @@ -21,7 +24,7 @@ export const connectorsMock: ActionConnector[] = [ { id: 'resilient-2', actionTypeId: '.resilient', - name: 'My Connector 2', + name: 'My Resilient connector', config: { apiUrl: 'https://test/', orgId: '201', @@ -52,7 +55,7 @@ export const connectorsMock: ActionConnector[] = [ { id: 'servicenow-uses-table-api', actionTypeId: '.servicenow', - name: 'My Connector', + name: 'My deprecated SN connector', config: { apiUrl: 'https://instance1.service-now.com', usesTableApi: true, @@ -118,3 +121,52 @@ export const actionTypesMock: ActionTypeConnector[] = [ supportedFeatureIds: ['alerting', 'cases'], }, ]; + +/** + * Construct a mock getConnectors response object + * + * @param overrides is an object where the key is the path for setting a field in the returned object. For example to set + * the externalService.connectorId pass the following overrides object: + * + * ``` + * { + * 'push.details.externalService.connectorId': '123' + * } + * ``` + */ +export const getCaseConnectorsMockResponse = ( + overrides?: Record +): CaseConnectors => { + return connectorsMock.reduce((acc, connector) => { + const newConnectors: CaseConnectors = { + ...acc, + [connector.id]: { + id: connector.id, + name: connector.name, + type: connector.actionTypeId, + fields: null, + push: { + needsToBePushed: false, + hasBeenPushed: true, + details: { + oldestUserActionPushDate: '2023-01-17T09:46:29.813Z', + latestUserActionPushDate: '2023-01-17T09:46:29.813Z', + externalService: { + ...basicPush, + connectorId: connector.id, + connectorName: connector.name, + }, + }, + }, + }, + }; + + if (overrides != null) { + for (const path of Object.keys(overrides)) { + set(newConnectors[connector.id], path, overrides[path]); + } + } + + return newConnectors; + }, {}); +}; diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx index 65156c39202b4..909bb1dd24ea0 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx @@ -36,7 +36,7 @@ import { registerConnectorsToMockActionRegistry } from '../../common/mock/regist import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; import { waitForComponentToUpdate } from '../../common/test_utils'; import { useCreateAttachments } from '../../containers/use_create_attachments'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; import { useGetTags } from '../../containers/use_get_tags'; import { useUpdateCase } from '../../containers/use_update_case'; import { useGetCases, DEFAULT_QUERY_PARAMS } from '../../containers/use_get_cases'; @@ -52,7 +52,7 @@ jest.mock('../../containers/use_get_action_license'); jest.mock('../../containers/use_get_tags'); jest.mock('../../containers/user_profiles/use_get_current_user_profile'); jest.mock('../../containers/user_profiles/use_bulk_get_user_profiles'); -jest.mock('../../containers/configure/use_connectors'); +jest.mock('../../containers/configure/use_get_supported_action_connectors'); jest.mock('../../common/lib/kibana'); jest.mock('../../common/navigation/hooks'); jest.mock('../app/use_available_owners', () => ({ @@ -66,7 +66,7 @@ const useGetTagsMock = useGetTags as jest.Mock; const useGetCurrentUserProfileMock = useGetCurrentUserProfile as jest.Mock; const useBulkGetUserProfilesMock = useBulkGetUserProfiles as jest.Mock; const useKibanaMock = useKibana as jest.MockedFunction; -const useGetConnectorsMock = useGetConnectors as jest.Mock; +const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock; const useCreateAttachmentsMock = useCreateAttachments as jest.Mock; const useUpdateCaseMock = useUpdateCase as jest.Mock; const useLicenseMock = useLicense as jest.Mock; diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx index 3b2b510a26f82..ab50f16083f8b 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx @@ -23,7 +23,7 @@ import type { EuiBasicTableOnChange } from './types'; import { CasesTable } from './table'; import { useCasesContext } from '../cases_context/use_cases_context'; import { CasesMetrics } from './cases_metrics'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; import { initialData, useGetCases } from '../../containers/use_get_cases'; import { useBulkGetUserProfiles } from '../../containers/user_profiles/use_bulk_get_user_profiles'; import { useGetCurrentUserProfile } from '../../containers/user_profiles/use_get_current_user_profile'; @@ -98,7 +98,7 @@ export const AllCasesList = React.memo( const { data: currentUserProfile, isLoading: isLoadingCurrentUserProfile } = useGetCurrentUserProfile(); - const { data: connectors = [] } = useGetConnectors(); + const { data: connectors = [] } = useGetSupportedActionConnectors(); const sorting = useMemo( () => ({ diff --git a/x-pack/plugins/cases/public/components/all_cases/index.test.tsx b/x-pack/plugins/cases/public/components/all_cases/index.test.tsx index 72cdbc0c0ed5b..7ce32a2f123a5 100644 --- a/x-pack/plugins/cases/public/components/all_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/index.test.tsx @@ -13,7 +13,7 @@ import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer, noCreateCasesPermissions } from '../../common/mock'; import { useGetActionLicense } from '../../containers/use_get_action_license'; import { connectorsMock, useGetCasesMockState } from '../../containers/mock'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; import { useGetTags } from '../../containers/use_get_tags'; import { useGetCases } from '../../containers/use_get_cases'; import { useGetCurrentUserProfile } from '../../containers/user_profiles/use_get_current_user_profile'; @@ -26,14 +26,14 @@ jest.mock('../../containers/use_get_action_license', () => { useGetActionLicense: jest.fn(), }; }); -jest.mock('../../containers/configure/use_connectors'); +jest.mock('../../containers/configure/use_get_supported_action_connectors'); jest.mock('../../containers/api'); jest.mock('../../containers/use_get_cases'); jest.mock('../../containers/user_profiles/use_get_current_user_profile'); jest.mock('../../containers/user_profiles/use_bulk_get_user_profiles'); jest.mock('../../api'); -const useGetConnectorsMock = useGetConnectors as jest.Mock; +const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock; const useGetCasesMock = useGetCases as jest.Mock; const useGetActionLicenseMock = useGetActionLicense as jest.Mock; const useGetCurrentUserProfileMock = useGetCurrentUserProfile as jest.Mock; diff --git a/x-pack/plugins/cases/public/components/case_action_bar/actions.test.tsx b/x-pack/plugins/cases/public/components/case_action_bar/actions.test.tsx index 16bef4b933d6e..195d02f7931cf 100644 --- a/x-pack/plugins/cases/public/components/case_action_bar/actions.test.tsx +++ b/x-pack/plugins/cases/public/components/case_action_bar/actions.test.tsx @@ -29,10 +29,6 @@ jest.mock('react-router-dom', () => { }); const defaultProps = { - allCasesNavigation: { - href: 'all-cases-href', - onClick: () => {}, - }, caseData: basicCase, currentExternalIncident: null, }; @@ -123,10 +119,6 @@ describe('CaseView actions', () => { {...defaultProps} currentExternalIncident={{ ...basicPush, - firstPushIndex: 5, - lastPushIndex: 5, - commentsToUpdate: [], - hasDataToPush: false, }} /> diff --git a/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx b/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx index b0697f9c962e7..d464946b60f82 100644 --- a/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx +++ b/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx @@ -13,14 +13,13 @@ import { useDeleteCases } from '../../containers/use_delete_cases'; import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; import { PropertyActions } from '../property_actions'; import type { Case } from '../../../common/ui/types'; -import type { CaseService } from '../../containers/use_find_case_user_actions'; import { useAllCasesNavigation } from '../../common/navigation'; import { useCasesContext } from '../cases_context/use_cases_context'; import { useCasesToast } from '../../common/use_cases_toast'; interface CaseViewActions { caseData: Case; - currentExternalIncident: CaseService | null; + currentExternalIncident: Case['externalService']; } const ActionsComponent: React.FC = ({ caseData, currentExternalIncident }) => { @@ -48,22 +47,22 @@ const ActionsComponent: React.FC = ({ caseData, currentExternal showSuccessToast(i18n.COPY_ID_ACTION_SUCCESS); }, }, - ...(permissions.delete + ...(currentExternalIncident != null && !isEmpty(currentExternalIncident?.externalUrl) ? [ { - iconType: 'trash', - label: i18n.DELETE_CASE(), - color: 'danger' as const, - onClick: openModal, + iconType: 'popout', + label: i18n.VIEW_INCIDENT(currentExternalIncident?.externalTitle ?? ''), + onClick: () => window.open(currentExternalIncident?.externalUrl, '_blank'), }, ] : []), - ...(currentExternalIncident != null && !isEmpty(currentExternalIncident?.externalUrl) + ...(permissions.delete ? [ { - iconType: 'popout', - label: i18n.VIEW_INCIDENT(currentExternalIncident?.externalTitle ?? ''), - onClick: () => window.open(currentExternalIncident?.externalUrl, '_blank'), + iconType: 'trash', + label: i18n.DELETE_CASE(), + color: 'danger' as const, + onClick: openModal, }, ] : []), diff --git a/x-pack/plugins/cases/public/components/case_action_bar/index.test.tsx b/x-pack/plugins/cases/public/components/case_action_bar/index.test.tsx index 81560f99e5427..7263514fb6baa 100644 --- a/x-pack/plugins/cases/public/components/case_action_bar/index.test.tsx +++ b/x-pack/plugins/cases/public/components/case_action_bar/index.test.tsx @@ -7,10 +7,10 @@ import React from 'react'; import { mount } from 'enzyme'; -import { render, screen } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { basicCase, caseUserActions, getAlertUserAction } from '../../containers/mock'; +import { basicCase } from '../../containers/mock'; import type { CaseActionBarProps } from '.'; import { CaseActionBar } from '.'; import { @@ -19,42 +19,31 @@ import { noUpdateCasesPermissions, TestProviders, } from '../../common/mock'; -import { useFindCaseUserActions } from '../../containers/use_find_case_user_actions'; +import { useGetCaseConnectors } from '../../containers/use_get_case_connectors'; import { useRefreshCaseViewPage } from '../case_view/use_on_refresh_case_view_page'; +import { getCaseConnectorsMockResponse } from '../../common/mock/connectors'; -jest.mock('../../containers/use_find_case_user_actions'); +jest.mock('../../containers/use_get_case_connectors'); jest.mock('../case_view/use_on_refresh_case_view_page'); -const useFindCaseUserActionsMock = useFindCaseUserActions as jest.Mock; -const defaultUseFindCaseUserActions = { - data: { - caseUserActions: [...caseUserActions, getAlertUserAction()], - caseServices: {}, - hasDataToPush: false, - participants: [basicCase.createdBy], - }, - isLoading: false, - isError: false, -}; +const useGetCaseConnectorsMock = useGetCaseConnectors as jest.Mock; describe('CaseActionBar', () => { + const caseConnectors = getCaseConnectorsMockResponse(); + const onUpdateField = jest.fn(); - const defaultProps = { - allCasesNavigation: { - href: 'all-cases-href', - onClick: () => {}, - }, + const defaultProps: CaseActionBarProps = { caseData: basicCase, - disableAlerting: false, isLoading: false, onUpdateField, - currentExternalIncident: null, - metricsFeatures: [], }; beforeEach(() => { jest.clearAllMocks(); - useFindCaseUserActionsMock.mockReturnValue(defaultUseFindCaseUserActions); + useGetCaseConnectorsMock.mockReturnValue({ + isLoading: false, + data: caseConnectors, + }); }); it('renders', () => { @@ -70,21 +59,6 @@ describe('CaseActionBar', () => { expect(wrapper.find(`[data-test-subj="sync-alerts-switch"]`).exists()).toBeTruthy(); expect(wrapper.find(`[data-test-subj="case-refresh"]`).exists()).toBeTruthy(); expect(wrapper.find(`[data-test-subj="case-view-actions"]`).exists()).toBeTruthy(); - // no loading bar - expect(wrapper.find(`[data-test-subj="case-view-action-bar-spinner"]`).exists()).toBeFalsy(); - }); - - it('shows a loading bar when user actions are loaded', async () => { - useFindCaseUserActionsMock.mockReturnValue({ - data: undefined, - isLoading: true, - }); - const wrapper = mount( - - - - ); - expect(wrapper.find(`[data-test-subj="case-view-action-bar-spinner"]`).exists()).toBeTruthy(); }); it('should show correct status', () => { @@ -249,4 +223,38 @@ describe('CaseActionBar', () => { userEvent.click(screen.getByTestId('property-actions-ellipses')); expect(queryByText('Delete case')).toBeInTheDocument(); }); + + it('shows the external incident action', async () => { + const connector = caseConnectors['servicenow-1']; + const { push, ...connectorWithoutPush } = connector; + + const props = { + ...defaultProps, + caseData: { ...defaultProps.caseData, connector: connectorWithoutPush }, + }; + + render( + + + + ); + + userEvent.click(screen.getByTestId('property-actions-ellipses')); + + await waitFor(() => { + expect(screen.getByTestId('property-actions-popout')).toBeInTheDocument(); + }); + }); + + it('does not show the external incident action', async () => { + render( + + + + ); + + userEvent.click(screen.getByTestId('property-actions-ellipses')); + + expect(screen.queryByTestId('property-actions-popout')).not.toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/cases/public/components/case_action_bar/index.tsx b/x-pack/plugins/cases/public/components/case_action_bar/index.tsx index d21427d579893..b8be843e8c659 100644 --- a/x-pack/plugins/cases/public/components/case_action_bar/index.tsx +++ b/x-pack/plugins/cases/public/components/case_action_bar/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import styled, { css } from 'styled-components'; import { EuiButtonEmpty, @@ -15,13 +15,11 @@ import { EuiFlexGroup, EuiFlexItem, EuiIconTip, - EuiLoadingSpinner, } from '@elastic/eui'; import type { Case } from '../../../common/ui/types'; import type { CaseStatuses } from '../../../common/api'; import * as i18n from '../case_view/translations'; import { Actions } from './actions'; -import { useFindCaseUserActions } from '../../containers/use_find_case_user_actions'; import { StatusContextMenu } from './status_context_menu'; import { SyncAlertsSwitch } from '../case_settings/sync_alerts_switch'; import type { OnUpdateFields } from '../case_view/types'; @@ -30,6 +28,7 @@ import { getStatusDate, getStatusTitle } from './helpers'; import { useRefreshCaseViewPage } from '../case_view/use_on_refresh_case_view_page'; import { useCasesContext } from '../cases_context/use_cases_context'; import { useCasesFeatures } from '../../common/use_cases_features'; +import { useGetCaseConnectors } from '../../containers/use_get_case_connectors'; const MyDescriptionList = styled(EuiDescriptionList)` ${({ theme }) => css` @@ -56,9 +55,14 @@ const CaseActionBarComponent: React.FC = ({ }) => { const { permissions } = useCasesContext(); const { isSyncAlertsEnabled, metricsFeatures } = useCasesFeatures(); - const date = useMemo(() => getStatusDate(caseData), [caseData]); - const title = useMemo(() => getStatusTitle(caseData.status), [caseData.status]); + + const { data: caseConnectors } = useGetCaseConnectors(caseData.id); + + const date = getStatusDate(caseData); + const title = getStatusTitle(caseData.status); + const refreshCaseViewPage = useRefreshCaseViewPage(); + const onStatusChanged = useCallback( (status: CaseStatuses) => onUpdateField({ @@ -68,19 +72,8 @@ const CaseActionBarComponent: React.FC = ({ [onUpdateField] ); - const { data: userActionsData, isLoading: isLoadingUserActions } = useFindCaseUserActions( - caseData.id, - caseData.connector.id - ); - - const currentExternalIncident = useMemo( - () => - userActionsData?.caseServices != null && - userActionsData.caseServices[caseData.connector.id] != null - ? userActionsData.caseServices[caseData.connector.id] - : null, - [userActionsData?.caseServices, caseData.connector] - ); + const currentExternalIncident = + caseConnectors?.[caseData.connector.id]?.push.details?.externalService ?? null; const onSyncAlertsChanged = useCallback( (syncAlerts: boolean) => @@ -93,92 +86,84 @@ const CaseActionBarComponent: React.FC = ({ return ( - {isLoadingUserActions ? ( - - - - ) : ( - <> - - - - - {i18n.STATUS} - - - - - {!metricsFeatures.includes('lifespan') ? ( - - {title} - - - - - ) : null} - - - - - - - {permissions.update && isSyncAlertsEnabled && ( - - - - - {i18n.SYNC_ALERTS} - - - - - - - - - - - )} - - - - {i18n.CASE_REFRESH} - - - - - - - - - )} + + + + + {i18n.STATUS} + + + + + {!metricsFeatures.includes('lifespan') ? ( + + {title} + + + + + ) : null} + + + + + + + {permissions.update && isSyncAlertsEnabled && ( + + + + + {i18n.SYNC_ALERTS} + + + + + + + + + + + )} + + + + {i18n.CASE_REFRESH} + + + + + + + ); }; diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx index 1dc62eeba4d97..6c5814c7830f9 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx @@ -14,7 +14,7 @@ import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer } from '../../common/mock'; import '../../common/mock/match_media'; import { useCaseViewNavigation, useUrlParams } from '../../common/navigation/hooks'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; import { basicCaseClosed, connectorsMock } from '../../containers/mock'; import type { UseGetCase } from '../../containers/use_get_case'; import { useGetCase } from '../../containers/use_get_case'; @@ -22,6 +22,7 @@ import { useGetCaseMetrics } from '../../containers/use_get_case_metrics'; import { useFindCaseUserActions } from '../../containers/use_find_case_user_actions'; import { useGetTags } from '../../containers/use_get_tags'; import { usePostPushToService } from '../../containers/use_post_push_to_service'; +import { useGetCaseConnectors } from '../../containers/use_get_case_connectors'; import { useUpdateCase } from '../../containers/use_update_case'; import { useBulkGetUserProfiles } from '../../containers/user_profiles/use_bulk_get_user_profiles'; import { CaseViewPage } from './case_view_page'; @@ -37,6 +38,7 @@ import type { CaseViewPageProps } from './types'; import { userProfiles } from '../../containers/user_profiles/api.mock'; import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; import { CASE_VIEW_PAGE_TABS } from '../../../common/types'; +import { getCaseConnectorsMockResponse } from '../../common/mock/connectors'; jest.mock('../../containers/use_get_action_license'); jest.mock('../../containers/use_update_case'); @@ -44,8 +46,9 @@ jest.mock('../../containers/use_get_case_metrics'); jest.mock('../../containers/use_find_case_user_actions'); jest.mock('../../containers/use_get_tags'); jest.mock('../../containers/use_get_case'); -jest.mock('../../containers/configure/use_connectors'); +jest.mock('../../containers/configure/use_get_supported_action_connectors'); jest.mock('../../containers/use_post_push_to_service'); +jest.mock('../../containers/use_get_case_connectors'); jest.mock('../../containers/user_profiles/use_bulk_get_user_profiles'); jest.mock('../user_actions/timestamp', () => ({ UserActionTimestamp: () => <>, @@ -59,8 +62,9 @@ const useUrlParamsMock = useUrlParams as jest.Mock; const useCaseViewNavigationMock = useCaseViewNavigation as jest.Mock; const useUpdateCaseMock = useUpdateCase as jest.Mock; const useFindCaseUserActionsMock = useFindCaseUserActions as jest.Mock; -const useGetConnectorsMock = useGetConnectors as jest.Mock; +const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock; const usePostPushToServiceMock = usePostPushToService as jest.Mock; +const useGetCaseConnectorsMock = useGetCaseConnectors as jest.Mock; const useGetCaseMetricsMock = useGetCaseMetrics as jest.Mock; const useGetTagsMock = useGetTags as jest.Mock; const useBulkGetUserProfilesMock = useBulkGetUserProfiles as jest.Mock; @@ -94,6 +98,7 @@ describe('CaseViewPage', () => { const pushCaseToExternalService = jest.fn(); const data = caseProps.caseData; let appMockRenderer: AppMockRenderer; + const caseConnectors = getCaseConnectorsMockResponse(); beforeEach(() => { mockGetCase(); @@ -102,6 +107,10 @@ describe('CaseViewPage', () => { useGetCaseMetricsMock.mockReturnValue(defaultGetCaseMetrics); useFindCaseUserActionsMock.mockReturnValue(defaultUseFindCaseUserActions); usePostPushToServiceMock.mockReturnValue({ isLoading: false, pushCaseToExternalService }); + useGetCaseConnectorsMock.mockReturnValue({ + isLoading: false, + data: caseConnectors, + }); useGetConnectorsMock.mockReturnValue({ data: connectorsMock, isLoading: false }); useGetTagsMock.mockReturnValue({ data: [], isLoading: false }); useBulkGetUserProfilesMock.mockReturnValue({ data: new Map(), isLoading: false }); @@ -249,17 +258,20 @@ describe('CaseViewPage', () => { }); it('should push updates on button click', async () => { - useFindCaseUserActionsMock.mockImplementation(() => ({ - ...defaultUseFindCaseUserActions, + useGetCaseConnectorsMock.mockImplementation(() => ({ + isLoading: false, data: { - ...defaultUseFindCaseUserActions.data, - hasDataToPush: true, + ...caseConnectors, + 'resilient-2': { + ...caseConnectors['resilient-2'], + push: { ...caseConnectors['resilient-2'].push, needsToBePushed: true }, + }, }, })); const result = appMockRenderer.render(); - expect(result.getByTestId('has-data-to-push-button')).toBeInTheDocument(); + expect(result.getByTestId('push-to-external-service')).toBeInTheDocument(); userEvent.click(result.getByTestId('push-to-external-service')); @@ -314,7 +326,7 @@ describe('CaseViewPage', () => { expect(updateObject.updateKey).toEqual('connector'); expect(updateObject.updateValue).toEqual({ id: 'resilient-2', - name: 'My Connector 2', + name: 'My Resilient connector', type: ConnectorTypes.resilient, fields: { incidentTypes: null, @@ -391,20 +403,15 @@ describe('CaseViewPage', () => { it('should show the correct connector name on the push button', async () => { useGetConnectorsMock.mockImplementation(() => ({ data: connectorsMock, isLoading: false })); - useFindCaseUserActionsMock.mockImplementation(() => ({ - ...defaultUseFindCaseUserActions, - data: { - ...defaultUseFindCaseUserActions.data, - hasDataToPush: true, - }, - })); const result = appMockRenderer.render( ); await waitFor(() => { - expect(result.getByTestId('has-data-to-push-button')).toHaveTextContent('My Connector 2'); + expect(result.getByTestId('push-to-external-service')).toHaveTextContent( + 'My Resilient connector' + ); }); }); diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx index af796ff796d85..6e7e7348a8ab4 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx @@ -59,7 +59,6 @@ export const CaseViewPage = React.memo( const timelineUi = useTimelineContext()?.ui; const { onUpdateField, isLoading, loadingKey } = useOnUpdateField({ - caseId, caseData, }); diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx index bc6b1d7c0dc81..941a983659183 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx @@ -21,14 +21,17 @@ import type { Case } from '../../../../common'; import type { CaseViewProps } from '../types'; import { useFindCaseUserActions } from '../../../containers/use_find_case_user_actions'; import { usePostPushToService } from '../../../containers/use_post_push_to_service'; -import { useGetConnectors } from '../../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../../containers/configure/use_get_supported_action_connectors'; import { useGetTags } from '../../../containers/use_get_tags'; import { useBulkGetUserProfiles } from '../../../containers/user_profiles/use_bulk_get_user_profiles'; +import { useGetCaseConnectors } from '../../../containers/use_get_case_connectors'; import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; import { waitForComponentToUpdate } from '../../../common/test_utils'; +import { waitFor } from '@testing-library/dom'; +import { getCaseConnectorsMockResponse } from '../../../common/mock/connectors'; jest.mock('../../../containers/use_find_case_user_actions'); -jest.mock('../../../containers/configure/use_connectors'); +jest.mock('../../../containers/configure/use_get_supported_action_connectors'); jest.mock('../../../containers/use_post_push_to_service'); jest.mock('../../user_actions/timestamp', () => ({ UserActionTimestamp: () => <>, @@ -37,6 +40,7 @@ jest.mock('../../../common/navigation/hooks'); jest.mock('../../../containers/use_get_action_license'); jest.mock('../../../containers/use_get_tags'); jest.mock('../../../containers/user_profiles/use_bulk_get_user_profiles'); +jest.mock('../../../containers/use_get_case_connectors'); (useGetTags as jest.Mock).mockReturnValue({ data: ['coke', 'pepsi'], refetch: jest.fn() }); @@ -76,8 +80,6 @@ const pushCaseToExternalService = jest.fn(); const defaultUseFindCaseUserActions = { data: { caseUserActions: [...caseUserActions, getAlertUserAction()], - caseServices: {}, - hasDataToPush: false, participants: [caseData.createdBy], }, refetch: fetchCaseUserActions, @@ -92,16 +94,23 @@ export const caseProps = { }; const useFindCaseUserActionsMock = useFindCaseUserActions as jest.Mock; -const useGetConnectorsMock = useGetConnectors as jest.Mock; +const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock; const usePostPushToServiceMock = usePostPushToService as jest.Mock; const useBulkGetUserProfilesMock = useBulkGetUserProfiles as jest.Mock; +const useGetCaseConnectorsMock = useGetCaseConnectors as jest.Mock; describe('Case View Page activity tab', () => { + const caseConnectors = getCaseConnectorsMockResponse(); + beforeAll(() => { useFindCaseUserActionsMock.mockReturnValue(defaultUseFindCaseUserActions); useGetConnectorsMock.mockReturnValue({ data: connectorsMock, isLoading: false }); usePostPushToServiceMock.mockReturnValue({ isLoading: false, pushCaseToExternalService }); useBulkGetUserProfilesMock.mockReturnValue({ isLoading: false, data: new Map() }); + useGetCaseConnectorsMock.mockReturnValue({ + isLoading: false, + data: caseConnectors, + }); }); let appMockRender: AppMockRenderer; @@ -126,7 +135,7 @@ describe('Case View Page activity tab', () => { expect(result.getByTestId('case-tags')).toBeTruthy(); expect(result.getByTestId('connector-edit-header')).toBeTruthy(); expect(result.getByTestId('case-view-status-action-button')).toBeTruthy(); - expect(useFindCaseUserActionsMock).toHaveBeenCalledWith(caseData.id, caseData.connector.id); + expect(useFindCaseUserActionsMock).toHaveBeenCalledWith(caseData.id); await waitForComponentToUpdate(); }); @@ -143,7 +152,7 @@ describe('Case View Page activity tab', () => { expect(result.getByTestId('case-tags')).toBeTruthy(); expect(result.getByTestId('connector-edit-header')).toBeTruthy(); expect(result.queryByTestId('case-view-status-action-button')).not.toBeInTheDocument(); - expect(useFindCaseUserActionsMock).toHaveBeenCalledWith(caseData.id, caseData.connector.id); + expect(useFindCaseUserActionsMock).toHaveBeenCalledWith(caseData.id); await waitForComponentToUpdate(); }); @@ -160,7 +169,7 @@ describe('Case View Page activity tab', () => { expect(result.getByTestId('case-tags')).toBeTruthy(); expect(result.getByTestId('connector-edit-header')).toBeTruthy(); expect(result.getByTestId('case-severity-selection')).toBeDisabled(); - expect(useFindCaseUserActionsMock).toHaveBeenCalledWith(caseData.id, caseData.connector.id); + expect(useFindCaseUserActionsMock).toHaveBeenCalledWith(caseData.id); await waitForComponentToUpdate(); }); @@ -174,7 +183,7 @@ describe('Case View Page activity tab', () => { const result = appMockRender.render(); expect(result.getByTestId('case-view-loading-content')).toBeTruthy(); expect(result.queryByTestId('case-view-activity')).toBeFalsy(); - expect(useFindCaseUserActionsMock).toHaveBeenCalledWith(caseData.id, caseData.connector.id); + expect(useFindCaseUserActionsMock).toHaveBeenCalledWith(caseData.id); }); it('should not render the assignees on basic license', () => { @@ -205,7 +214,8 @@ describe('Case View Page activity tab', () => { const result = appMockRender.render(); - expect(result.getByTestId('case-view-edit-connector')).toBeInTheDocument(); - await waitForComponentToUpdate(); + await waitFor(() => { + expect(result.getByTestId('case-view-edit-connector')).toBeInTheDocument(); + }); }); }); diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx index 6218c152e5c85..bba0106f9984f 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx @@ -5,13 +5,16 @@ * 2.0. */ +/* eslint-disable complexity */ + import { EuiFlexGroup, EuiFlexItem, EuiLoadingContent } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { isEqual, uniq } from 'lodash'; +import { useGetCaseConnectors } from '../../../containers/use_get_case_connectors'; import { useCasesFeatures } from '../../../common/use_cases_features'; import { useGetCurrentUserProfile } from '../../../containers/user_profiles/use_get_current_user_profile'; import { useBulkGetUserProfiles } from '../../../containers/user_profiles/use_bulk_get_user_profiles'; -import { useGetConnectors } from '../../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../../containers/configure/use_get_supported_action_connectors'; import type { CaseSeverity } from '../../../../common/api'; import { useCaseViewNavigation } from '../../../common/navigation'; import type { UseFetchAlertData } from '../../../../common/ui/types'; @@ -25,8 +28,6 @@ import { UserList } from './user_list'; import { useOnUpdateField } from '../use_on_update_field'; import { useCasesContext } from '../../cases_context/use_cases_context'; import * as i18n from '../translations'; -import { getNoneConnector, normalizeActionConnector } from '../../configure_cases/utils'; -import { getConnectorById } from '../../utils'; import { SeveritySidebarSelector } from '../../severity/sidebar_selector'; import { useFindCaseUserActions } from '../../../containers/use_find_case_user_actions'; import { AssignUsers } from './assign_users'; @@ -49,9 +50,12 @@ export const CaseViewActivity = ({ const { getCaseViewUrl } = useCaseViewNavigation(); const { caseAssignmentAuthorized, pushToServiceAuthorized } = useCasesFeatures(); + const { data: caseConnectors, isLoading: isLoadingCaseConnectors } = useGetCaseConnectors( + caseData.id + ); + const { data: userActionsData, isLoading: isLoadingUserActions } = useFindCaseUserActions( - caseData.id, - caseData.connector.id + caseData.id ); const assignees = useMemo( @@ -79,7 +83,6 @@ export const CaseViewActivity = ({ ); const { onUpdateField, isLoading, loadingKey } = useOnUpdateField({ - caseId: caseData.id, caseData, }); @@ -125,37 +128,38 @@ export const CaseViewActivity = ({ [assignees, onUpdateField] ); - const { isLoading: isLoadingConnectors, data: connectors = [] } = useGetConnectors(); - - const [connectorName, isValidConnector] = useMemo(() => { - const connector = connectors.find((c) => c.id === caseData.connector.id); - return [connector?.name ?? '', !!connector]; - }, [connectors, caseData.connector]); + const { isLoading: isLoadingAllAvailableConnectors, data: supportedActionConnectors } = + useGetSupportedActionConnectors(); const onSubmitConnector = useCallback( - (connectorId, connectorFields, onError, onSuccess) => { - const connector = getConnectorById(connectorId, connectors); - const connectorToUpdate = connector - ? normalizeActionConnector(connector) - : getNoneConnector(); - + (connector, onError, onSuccess) => { onUpdateField({ key: 'connector', - value: { ...connectorToUpdate, fields: connectorFields }, + value: connector, onSuccess, onError, }); }, - [onUpdateField, connectors] + [onUpdateField] ); + const showUserActions = + !isLoadingUserActions && + !isLoadingCaseConnectors && + userActionsData && + caseConnectors && + userProfiles; + + const showConnectorSidebar = + pushToServiceAuthorized && userActionsData && caseConnectors && supportedActionConnectors; + return ( <> - {isLoadingUserActions && ( + {(isLoadingUserActions || isLoadingCaseConnectors) && ( )} - {!isLoadingUserActions && userActionsData && userProfiles && ( + {showUserActions && ( - {pushToServiceAuthorized && userActionsData ? ( + {showConnectorSidebar ? ( ) : null} diff --git a/x-pack/plugins/cases/public/components/case_view/index.test.tsx b/x-pack/plugins/cases/public/components/case_view/index.test.tsx index 369947fcc2b87..1004ce3d2f437 100644 --- a/x-pack/plugins/cases/public/components/case_view/index.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/index.test.tsx @@ -26,7 +26,7 @@ import { useGetCaseMetrics } from '../../containers/use_get_case_metrics'; import { usePostPushToService } from '../../containers/use_post_push_to_service'; import { useKibana } from '../../common/lib/kibana'; import { useFindCaseUserActions } from '../../containers/use_find_case_user_actions'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer } from '../../common/mock'; import CaseView from '.'; @@ -49,7 +49,7 @@ jest.mock('../../containers/use_get_tags'); jest.mock('../../containers/use_find_case_user_actions'); jest.mock('../../containers/use_get_case'); jest.mock('../../containers/use_get_case_metrics'); -jest.mock('../../containers/configure/use_connectors'); +jest.mock('../../containers/configure/use_get_supported_action_connectors'); jest.mock('../../containers/use_post_push_to_service'); jest.mock('../user_actions/timestamp', () => ({ UserActionTimestamp: () => <>, @@ -62,7 +62,7 @@ const useFetchCaseMock = useGetCase as jest.Mock; const useGetCaseMetricsMock = useGetCaseMetrics as jest.Mock; const useUpdateCaseMock = useUpdateCase as jest.Mock; const useFindCaseUserActionsMock = useFindCaseUserActions as jest.Mock; -const useGetConnectorsMock = useGetConnectors as jest.Mock; +const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock; const usePostPushToServiceMock = usePostPushToService as jest.Mock; const useKibanaMock = useKibana as jest.MockedFunction; const useGetTagsMock = useGetTags as jest.Mock; diff --git a/x-pack/plugins/cases/public/components/case_view/mocks.ts b/x-pack/plugins/cases/public/components/case_view/mocks.ts index 88069fe79cfe5..4c4bb40c81df6 100644 --- a/x-pack/plugins/cases/public/components/case_view/mocks.ts +++ b/x-pack/plugins/cases/public/components/case_view/mocks.ts @@ -102,8 +102,6 @@ export const defaultUpdateCaseState = { export const defaultUseFindCaseUserActions = { data: { caseUserActions: [...caseUserActions, getAlertUserAction()], - caseServices: {}, - hasDataToPush: false, participants: [caseData.createdBy], }, refetch: jest.fn(), diff --git a/x-pack/plugins/cases/public/components/case_view/use_on_update_field.ts b/x-pack/plugins/cases/public/components/case_view/use_on_update_field.ts index 449eb7b2ca8a9..d0fc6f98f0ce6 100644 --- a/x-pack/plugins/cases/public/components/case_view/use_on_update_field.ts +++ b/x-pack/plugins/cases/public/components/case_view/use_on_update_field.ts @@ -16,7 +16,7 @@ import { useUpdateCase } from '../../containers/use_update_case'; import { getTypedPayload } from '../../containers/utils'; import type { OnUpdateFields } from './types'; -export const useOnUpdateField = ({ caseData, caseId }: { caseData: Case; caseId: string }) => { +export const useOnUpdateField = ({ caseData }: { caseData: Case }) => { const { isLoading, updateKey: loadingKey, updateCaseProperty } = useUpdateCase(); const onUpdateField = useCallback( diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx index 2057d0153be68..19ac7d0b667c1 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx @@ -121,7 +121,7 @@ describe('Connectors', () => { newWrapper .find('button[data-test-subj="case-configure-update-selected-connector-button"]') .text() - ).toBe('Update My Connector'); + ).toBe('Update My SN connector'); }); it('shows the deprecated callout when the connector is deprecated', async () => { diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx index 3f3bdedff3e3c..9c5681c4025b6 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx @@ -83,7 +83,7 @@ describe('ConnectorsDropdown', () => { grow={false} > - My Connector + My SN connector , @@ -108,7 +108,7 @@ describe('ConnectorsDropdown', () => { grow={false} > - My Connector 2 + My Resilient connector , @@ -183,7 +183,7 @@ describe('ConnectorsDropdown', () => { grow={false} > - My Connector + My deprecated SN connector (deprecated) @@ -235,7 +235,7 @@ describe('ConnectorsDropdown', () => { .find('[data-test-subj="dropdown-connectors"]') .first() .text() - .includes('My Connector, is selected') + .includes('My SN connector, is selected') ).toBeTruthy(); }); diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx index 2699557bb51f1..bad53d324da3c 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx @@ -28,15 +28,15 @@ import { import { ConnectorTypes } from '../../../common/api'; import { actionTypeRegistryMock } from '@kbn/triggers-actions-ui-plugin/public/application/action_type_registry.mock'; import { useGetActionTypes } from '../../containers/configure/use_action_types'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; jest.mock('../../common/lib/kibana'); -jest.mock('../../containers/configure/use_connectors'); +jest.mock('../../containers/configure/use_get_supported_action_connectors'); jest.mock('../../containers/configure/use_configure'); jest.mock('../../containers/configure/use_action_types'); const useKibanaMock = useKibana as jest.Mocked; -const useGetConnectorsMock = useGetConnectors as jest.Mock; +const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock; const useCaseConfigureMock = useCaseConfigure as jest.Mock; const useGetUrlSearchMock = jest.fn(); const useGetActionTypesMock = useGetActionTypes as jest.Mock; @@ -417,7 +417,7 @@ describe('ConfigureCases', () => { expect(persistCaseConfigure).toHaveBeenCalledWith({ connector: { id: 'resilient-2', - name: 'My Connector 2', + name: 'My Resilient connector', type: ConnectorTypes.resilient, fields: null, }, @@ -440,7 +440,7 @@ describe('ConfigureCases', () => { ...useCaseConfigureResponse, connector: { id: 'resilient-2', - name: 'My connector 2', + name: 'My Resilient connector', type: ConnectorTypes.resilient, fields: null, }, @@ -459,7 +459,7 @@ describe('ConfigureCases', () => { wrapper .find('button[data-test-subj="case-configure-update-selected-connector-button"]') .text() - ).toBe('Update My Connector 2'); + ).toBe('Update My Resilient connector'); }); }); diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.tsx index ec9b8a8c7a2fb..2f7db2202a76b 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/index.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/index.tsx @@ -29,7 +29,7 @@ import { HeaderPage } from '../header_page'; import { useCasesContext } from '../cases_context/use_cases_context'; import { useCasesBreadcrumbs } from '../use_breadcrumbs'; import { CasesDeepLinkId } from '../../common/navigation'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; const FormWrapper = styled.div` ${({ theme }) => css` @@ -77,7 +77,7 @@ export const ConfigureCases: React.FC = React.memo(() => { isLoading: isLoadingConnectors, data: connectors = [], refetch: refetchConnectors, - } = useGetConnectors(); + } = useGetSupportedActionConnectors(); const { isLoading: isLoadingActionTypes, data: actionTypes = [], diff --git a/x-pack/plugins/cases/public/components/connectors/card.test.tsx b/x-pack/plugins/cases/public/components/connectors/card.test.tsx index 6254150620fd4..a654821830b23 100644 --- a/x-pack/plugins/cases/public/components/connectors/card.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/card.test.tsx @@ -6,15 +6,16 @@ */ import React from 'react'; -import { mount } from 'enzyme'; +import { render, screen } from '@testing-library/react'; import { ConnectorTypes } from '../../../common/api'; import { ConnectorCard } from './card'; +import { createQueryWithMarkup } from '../../common/test_utils'; describe('ConnectorCard ', () => { - it('it does not throw when accessing the icon if the connector type is not registered', () => { + it('does not throw when accessing the icon if the connector type is not registered', () => { expect(() => - mount( + render( { ) ).not.toThrowError(); }); + + it('shows the loading spinner if loading', () => { + render( + + ); + + expect(screen.getByTestId('connector-card-loading')).toBeInTheDocument(); + expect(screen.queryByTestId('connector-card')).not.toBeInTheDocument(); + }); + + it('shows the connector title', () => { + render( + + ); + + expect(screen.getByText('My connector')).toBeInTheDocument(); + }); + + it('shows the connector list items', () => { + const listItems = [ + { title: 'item 1 title', description: 'item 1 desc' }, + { title: 'item 2 title', description: 'item 2 desc' }, + ]; + + render( + + ); + + const getByText = createQueryWithMarkup(screen.getByText); + + for (const item of listItems) { + expect(getByText(`${item.title}: ${item.description}`)).toBeInTheDocument(); + } + }); }); diff --git a/x-pack/plugins/cases/public/components/connectors/card.tsx b/x-pack/plugins/cases/public/components/connectors/card.tsx index c4cd24787b01c..a9bf98dd7cf9d 100644 --- a/x-pack/plugins/cases/public/components/connectors/card.tsx +++ b/x-pack/plugins/cases/public/components/connectors/card.tsx @@ -5,9 +5,8 @@ * 2.0. */ -import React, { memo, useMemo } from 'react'; -import { EuiCard, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLoadingSpinner } from '@elastic/eui'; -import styled from 'styled-components'; +import React, { memo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLoadingSpinner, EuiText } from '@elastic/eui'; import type { ConnectorTypes } from '../../../common/api'; import { useKibana } from '../../common/lib/kibana'; @@ -20,12 +19,6 @@ interface ConnectorCardProps { isLoading: boolean; } -const StyledText = styled.span` - span { - display: block; - } -`; - const ConnectorCardDisplay: React.FC = ({ connectorType, title, @@ -34,44 +27,30 @@ const ConnectorCardDisplay: React.FC = ({ }) => { const { triggersActionsUi } = useKibana().services; - const description = useMemo( - () => ( - - {listItems.length > 0 && - listItems.map((item, i) => ( - - {`${item.title}: `} - {item.description} - - ))} - - ), - [listItems] - ); - - const icon = useMemo( - () => , - // eslint-disable-next-line react-hooks/exhaustive-deps - [connectorType] - ); - return ( <> {isLoading && } {!isLoading && ( - - - + + + + + {title} + + + + + + + + {listItems.length > 0 && + listItems.map((item, i) => ( + + {`${item.title}: `} + {`${item.description}`} + + ))} - {icon} )} diff --git a/x-pack/plugins/cases/public/components/connectors/jira/case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/jira/case_fields.test.tsx index 0e4d92139234b..751e9dadca491 100644 --- a/x-pack/plugins/cases/public/components/connectors/jira/case_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/jira/case_fields.test.tsx @@ -122,15 +122,12 @@ describe('Jira Fields', () => { connector={connector} /> ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(0).text()).toEqual( - 'Issue type: Task' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(1).text()).toEqual( - 'Parent issue: Parent Task' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(2).text()).toEqual( - 'Priority: High' - ); + + const nodes = wrapper.find('[data-test-subj="card-list-item"]').hostNodes(); + + expect(nodes.at(0).text()).toEqual('Issue type: Task'); + expect(nodes.at(1).text()).toEqual('Parent issue: Parent Task'); + expect(nodes.at(2).text()).toEqual('Priority: High'); }); test('it sets parent correctly', async () => { diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx index 2273343d119b8..95510090cada1 100644 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx @@ -71,15 +71,11 @@ describe('ServiceNowITSM Fields', () => { onChoicesSuccess(mockChoices); }); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(0).text()).toEqual( - 'Urgency: 2 - High' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(1).text()).toEqual( - 'Severity: 1 - Critical' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(2).text()).toEqual( - 'Impact: 3 - Moderate' - ); + const nodes = wrapper.find('[data-test-subj="card-list-item"]').hostNodes(); + + expect(nodes.at(0).text()).toEqual('Urgency: 2 - High'); + expect(nodes.at(1).text()).toEqual('Severity: 1 - Critical'); + expect(nodes.at(2).text()).toEqual('Impact: 3 - Moderate'); }); it('transforms the categories to options correctly', async () => { diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx index ee8681c7487dc..c66d40eb6827b 100644 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx @@ -75,29 +75,17 @@ describe('ServiceNowSIR Fields', () => { act(() => { onChoicesSuccess(mockChoices); }); - wrapper.update(); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(0).text()).toEqual( - 'Destination IPs: Yes' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(1).text()).toEqual( - 'Source IPs: Yes' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(2).text()).toEqual( - 'Malware URLs: Yes' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(3).text()).toEqual( - 'Malware Hashes: Yes' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(4).text()).toEqual( - 'Priority: 1 - Critical' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(5).text()).toEqual( - 'Category: Denial of Service' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(6).text()).toEqual( - 'Subcategory: Single or distributed (DoS or DDoS)' - ); + wrapper.update(); + const nodes = wrapper.find('[data-test-subj="card-list-item"]').hostNodes(); + + expect(nodes.at(0).text()).toEqual('Destination IPs: Yes'); + expect(nodes.at(1).text()).toEqual('Source IPs: Yes'); + expect(nodes.at(2).text()).toEqual('Malware URLs: Yes'); + expect(nodes.at(3).text()).toEqual('Malware Hashes: Yes'); + expect(nodes.at(4).text()).toEqual('Priority: 1 - Critical'); + expect(nodes.at(5).text()).toEqual('Category: Denial of Service'); + expect(nodes.at(6).text()).toEqual('Subcategory: Single or distributed (DoS or DDoS)'); }); test('it transforms the categories to options correctly', async () => { diff --git a/x-pack/plugins/cases/public/components/create/form.test.tsx b/x-pack/plugins/cases/public/components/create/form.test.tsx index d95f264089643..8aa50a4270414 100644 --- a/x-pack/plugins/cases/public/components/create/form.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form.test.tsx @@ -22,11 +22,11 @@ import { CreateCaseForm } from './form'; import { useCaseConfigure } from '../../containers/configure/use_configure'; import { useCaseConfigureResponse } from '../configure_cases/__mock__'; import { TestProviders } from '../../common/mock'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; import { useGetTags } from '../../containers/use_get_tags'; jest.mock('../../containers/use_get_tags'); -jest.mock('../../containers/configure/use_connectors'); +jest.mock('../../containers/configure/use_get_supported_action_connectors'); jest.mock('../../containers/configure/use_configure'); jest.mock('../markdown_editor/plugins/lens/use_lens_draft_comment'); jest.mock('../app/use_available_owners', () => ({ @@ -34,7 +34,7 @@ jest.mock('../app/use_available_owners', () => ({ })); const useGetTagsMock = useGetTags as jest.Mock; -const useGetConnectorsMock = useGetConnectors as jest.Mock; +const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock; const useCaseConfigureMock = useCaseConfigure as jest.Mock; const initialCaseValue: FormProps = { diff --git a/x-pack/plugins/cases/public/components/create/form_context.test.tsx b/x-pack/plugins/cases/public/components/create/form_context.test.tsx index 37971667da37b..192de92777fb6 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.test.tsx @@ -41,7 +41,7 @@ import { usePostPushToService } from '../../containers/use_post_push_to_service' import userEvent from '@testing-library/user-event'; import { connectorsMock } from '../../common/mock/connectors'; import type { CaseAttachments } from '../../types'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; import { useGetTags } from '../../containers/use_get_tags'; import { waitForComponentToUpdate } from '../../common/test_utils'; import { userProfiles } from '../../containers/user_profiles/api.mock'; @@ -51,7 +51,7 @@ jest.mock('../../containers/use_post_case'); jest.mock('../../containers/use_create_attachments'); jest.mock('../../containers/use_post_push_to_service'); jest.mock('../../containers/use_get_tags'); -jest.mock('../../containers/configure/use_connectors'); +jest.mock('../../containers/configure/use_get_supported_action_connectors'); jest.mock('../../containers/configure/use_configure'); jest.mock('../connectors/resilient/use_get_incident_types'); jest.mock('../connectors/resilient/use_get_severity'); @@ -64,7 +64,7 @@ jest.mock('../../common/lib/kibana'); jest.mock('../../containers/user_profiles/api'); jest.mock('../../common/use_license'); -const useGetConnectorsMock = useGetConnectors as jest.Mock; +const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock; const useCaseConfigureMock = useCaseConfigure as jest.Mock; const usePostCaseMock = usePostCase as jest.Mock; const useCreateAttachmentsMock = useCreateAttachments as jest.Mock; @@ -407,7 +407,7 @@ describe('Create case', () => { subcategory: null, }, id: 'servicenow-1', - name: 'My Connector', + name: 'My SN connector', type: '.servicenow', }, }); diff --git a/x-pack/plugins/cases/public/components/create/form_context.tsx b/x-pack/plugins/cases/public/components/create/form_context.tsx index 3a1c99f6d6218..ba859b21d7b0f 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.tsx @@ -22,7 +22,7 @@ import { useCasesContext } from '../cases_context/use_cases_context'; import { useCasesFeatures } from '../../common/use_cases_features'; import { getConnectorById } from '../utils'; import type { CaseAttachmentsWithoutOwner } from '../../types'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; import { useCreateCaseWithAttachmentsTransaction } from '../../common/apm/use_cases_transactions'; const initialCaseValue: FormProps = { @@ -55,7 +55,8 @@ export const FormContext: React.FC = ({ attachments, initialValue, }) => { - const { data: connectors = [], isLoading: isLoadingConnectors } = useGetConnectors(); + const { data: connectors = [], isLoading: isLoadingConnectors } = + useGetSupportedActionConnectors(); const { owner, appId } = useCasesContext(); const { isSyncAlertsEnabled } = useCasesFeatures(); const { postCase } = usePostCase(); diff --git a/x-pack/plugins/cases/public/components/create/index.test.tsx b/x-pack/plugins/cases/public/components/create/index.test.tsx index 24798c114fede..07a705ef41a90 100644 --- a/x-pack/plugins/cases/public/components/create/index.test.tsx +++ b/x-pack/plugins/cases/public/components/create/index.test.tsx @@ -30,13 +30,13 @@ import { useGetFieldsByIssueTypeResponse, } from './mock'; import { CreateCase } from '.'; -import { useGetConnectors } from '../../containers/configure/use_connectors'; +import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors'; import { useGetTags } from '../../containers/use_get_tags'; jest.mock('../../containers/api'); jest.mock('../../containers/user_profiles/api'); jest.mock('../../containers/use_get_tags'); -jest.mock('../../containers/configure/use_connectors'); +jest.mock('../../containers/configure/use_get_supported_action_connectors'); jest.mock('../../containers/configure/use_configure'); jest.mock('../connectors/resilient/use_get_incident_types'); jest.mock('../connectors/resilient/use_get_severity'); @@ -45,7 +45,7 @@ jest.mock('../connectors/jira/use_get_fields_by_issue_type'); jest.mock('../connectors/jira/use_get_single_issue'); jest.mock('../connectors/jira/use_get_issues'); -const useGetConnectorsMock = useGetConnectors as jest.Mock; +const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock; const useCaseConfigureMock = useCaseConfigure as jest.Mock; const useGetTagsMock = useGetTags as jest.Mock; const useGetIncidentTypesMock = useGetIncidentTypes as jest.Mock; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts deleted file mode 100644 index a3c13c1213e64..0000000000000 --- a/x-pack/plugins/cases/public/components/edit_connector/helpers.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { ConnectorUserAction } from '../../../common/api'; -import { Actions, ConnectorTypes } from '../../../common/api'; -import type { CaseUserActions } from '../../containers/types'; -import { getConnectorFieldsFromUserActions } from './helpers'; - -const defaultJiraFields = { - issueType: '1', - parent: null, - priority: null, -}; - -describe('helpers', () => { - describe('getConnectorFieldsFromUserActions', () => { - it('returns null when it cannot find the connector id', () => { - expect(getConnectorFieldsFromUserActions('a', [])).toBeNull(); - }); - - it('returns null when it cannot find the connector id in a non empty array', () => { - expect( - getConnectorFieldsFromUserActions('a', [ - createConnectorUserAction({ - // @ts-expect-error payload missing fields - payload: { a: '1' }, - }), - ]) - ).toBeNull(); - }); - - it('returns the fields when it finds the connector id', () => { - expect(getConnectorFieldsFromUserActions('a', [createConnectorUserAction()])).toEqual( - defaultJiraFields - ); - }); - - it('returns the fields when it finds the connector id in the second user action', () => { - const expectedFields = { ...defaultJiraFields, issueType: '5' }; - - expect( - getConnectorFieldsFromUserActions('id-to-find', [ - createConnectorUserAction({}), - createConnectorUserAction({ - payload: { - connector: { - id: 'id-to-find', - name: 'test', - fields: expectedFields, - type: ConnectorTypes.jira, - }, - }, - }), - ]) - ).toEqual(expectedFields); - }); - - it('returns null when the action is not a connector', () => { - expect( - getConnectorFieldsFromUserActions('id-to-find', [ - createConnectorUserAction({ - // @ts-expect-error - type: 'not-a-connector', - }), - ]) - ).toBeNull(); - }); - }); -}); - -function createConnectorUserAction(attributes: Partial = {}): CaseUserActions { - return { - action: Actions.update, - createdBy: { username: 'user', fullName: null, email: null }, - createdAt: '2021-12-08T11:28:32.623Z', - type: 'connector', - id: '', - commentId: '', - payload: { - connector: { id: 'a', name: 'test', fields: defaultJiraFields, type: ConnectorTypes.jira }, - }, - ...attributes, - } as CaseUserActions; -} diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts deleted file mode 100644 index 84d6984f35bbc..0000000000000 --- a/x-pack/plugins/cases/public/components/edit_connector/helpers.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { isConnectorUserAction, isCreateCaseUserAction } from '../../../common/utils/user_actions'; -import type { ConnectorTypeFields } from '../../../common/api'; -import type { CaseUserActions } from '../../containers/types'; - -export const getConnectorFieldsFromUserActions = ( - id: string, - userActions: CaseUserActions[] -): ConnectorTypeFields['fields'] => { - for (const action of [...userActions].reverse()) { - if (isConnectorUserAction(action) || isCreateCaseUserAction(action)) { - const connector = action.payload.connector; - - if (connector && id === connector.id) { - return connector.fields; - } - } - } - - return null; -}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx b/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx index 401f43ae9afff..fe24145b10d68 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx +++ b/x-pack/plugins/cases/public/components/edit_connector/index.test.tsx @@ -6,7 +6,6 @@ */ import React from 'react'; -import { mount } from 'enzyme'; import { render, waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; @@ -20,47 +19,58 @@ import { noPushCasesPermissions, TestProviders, } from '../../common/mock'; -import { basicCase, basicPush, caseUserActions, connectorsMock } from '../../containers/mock'; +import { basicCase, connectorsMock } from '../../containers/mock'; import type { CaseConnector } from '../../containers/configure/types'; +import { getCaseConnectorsMockResponse } from '../../common/mock/connectors'; const onSubmit = jest.fn(); -const caseServices = { - '123': { - ...basicPush, - firstPushIndex: 0, - lastPushIndex: 0, - commentsToUpdate: [], - hasDataToPush: true, - }, -}; -const getDefaultProps = (): EditConnectorProps => { - return { - caseData: basicCase, - caseServices, - connectorName: connectorsMock[0].name, - connectors: connectorsMock, - hasDataToPush: true, - isLoading: false, - isValidConnector: true, - onSubmit, - userActions: caseUserActions, - }; +const caseConnectors = getCaseConnectorsMockResponse(); + +const defaultProps: EditConnectorProps = { + caseData: basicCase, + supportedActionConnectors: connectorsMock, + isLoading: false, + caseConnectors, + onSubmit, }; describe('EditConnector ', () => { let appMockRender: AppMockRenderer; + beforeEach(() => { jest.clearAllMocks(); appMockRender = createAppMockRenderer(); }); + it('Renders the none connector', async () => { + render( + + + + ); + + expect( + await screen.findByText( + 'To create and update a case in an external system, select a connector.' + ) + ).toBeInTheDocument(); + + userEvent.click(screen.getByTestId('connector-edit-button')); + + await waitFor(() => { + expect(screen.getAllByTestId('dropdown-connector-no-connector').length).toBeGreaterThan(0); + }); + }); + it('Renders servicenow connector from case initially', async () => { - const defaultProps = getDefaultProps(); const serviceNowProps = { ...defaultProps, caseData: { ...defaultProps.caseData, - connector: { ...defaultProps.caseData.connector, id: 'servicenow-1' }, + connector: { + ...defaultProps.caseData.connector, + id: 'servicenow-1', + }, }, }; @@ -70,54 +80,76 @@ describe('EditConnector ', () => { ); - expect(await screen.findByText('My Connector')).toBeInTheDocument(); + expect(await screen.findByText('My SN connector')).toBeInTheDocument(); }); it('Renders no connector, and then edit', async () => { - const defaultProps = getDefaultProps(); - const wrapper = mount( + render( ); - expect(wrapper.find(`[data-test-subj="has-data-to-push-button"]`).exists()).toBeTruthy(); - wrapper.find('[data-test-subj="connector-edit"] button').simulate('click'); - expect( - wrapper.find(`span[data-test-subj="dropdown-connector-no-connector"]`).last().exists() - ).toBeTruthy(); + userEvent.click(screen.getByTestId('connector-edit-button')); + + await waitFor(() => { + expect(screen.getByTestId('caseConnectors')).toBeInTheDocument(); + }); + + userEvent.click(screen.getByTestId('dropdown-connectors')); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.update(); - wrapper.find('button[data-test-subj="dropdown-connector-resilient-2"]').simulate('click'); - await waitFor(() => wrapper.update()); + await waitFor(() => { + expect(screen.getByTestId('dropdown-connector-resilient-2')).toBeInTheDocument(); + }); - expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists()).toBeTruthy(); + userEvent.click(screen.getByTestId('dropdown-connector-resilient-2')); + + await waitFor(() => { + expect(screen.getByTestId('edit-connectors-submit')).toBeInTheDocument(); + }); }); it('Edit external service on submit', async () => { - const defaultProps = getDefaultProps(); - const wrapper = mount( + render( ); - wrapper.find('[data-test-subj="connector-edit"] button').simulate('click'); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.update(); - wrapper.find('button[data-test-subj="dropdown-connector-resilient-2"]').simulate('click'); - wrapper.update(); + userEvent.click(screen.getByTestId('connector-edit-button')); + userEvent.click(screen.getByTestId('dropdown-connectors')); - expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists()).toBeTruthy(); + await waitFor(() => { + expect(screen.getByTestId('dropdown-connector-resilient-2')).toBeInTheDocument(); + }); + + userEvent.click(screen.getByTestId('dropdown-connector-resilient-2'), undefined, { + skipPointerEventsCheck: true, + }); - wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click'); - await waitFor(() => expect(onSubmit.mock.calls[0][0]).toBe('resilient-2')); + expect(screen.getByTestId('edit-connectors-submit')).toBeInTheDocument(); + + userEvent.click(screen.getByTestId('edit-connectors-submit')); + + await waitFor(() => + expect(onSubmit).toHaveBeenCalledWith( + { + fields: { + incidentTypes: null, + severityCode: null, + }, + id: 'resilient-2', + name: 'My Resilient connector', + type: '.resilient', + }, + expect.anything(), + expect.anything() + ) + ); }); it('Revert to initial external service on error', async () => { - const defaultProps = getDefaultProps(); - onSubmit.mockImplementation((connector, onSuccess, onError) => { + onSubmit.mockImplementation((connector, onError, onSuccess) => { onError(new Error('An error has occurred')); }); @@ -125,43 +157,46 @@ describe('EditConnector ', () => { ...defaultProps, caseData: { ...defaultProps.caseData, - connector: { ...defaultProps.caseData.connector, id: 'servicenow-1' }, + connector: { + ...defaultProps.caseData.connector, + id: 'servicenow-1', + }, }, }; - const wrapper = mount( + render( ); - wrapper.find('[data-test-subj="connector-edit"] button').simulate('click'); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); + userEvent.click(screen.getByTestId('connector-edit-button')); + userEvent.click(screen.getByTestId('dropdown-connectors')); + + await waitFor(() => { + expect(screen.getByTestId('dropdown-connector-resilient-2')).toBeInTheDocument(); + }); + + userEvent.click(screen.getByTestId('dropdown-connector-resilient-2'), undefined, { + skipPointerEventsCheck: true, + }); + + userEvent.click(screen.getByTestId('edit-connectors-submit')); + await waitFor(() => { - wrapper.update(); - wrapper.find('button[data-test-subj="dropdown-connector-resilient-2"]').simulate('click'); - wrapper.update(); - expect( - wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists() - ).toBeTruthy(); - wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click'); + expect(screen.queryByTestId('edit-connectors-submit')).not.toBeInTheDocument(); }); await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).exists()).toBeFalsy(); + expect(onSubmit).toHaveBeenCalled(); }); - /** - * If an error is being throw on submit the selected connector should - * be reverted to the initial one. In our test the initial one is the .servicenow-1 - * connector. The title of the .servicenow-1 connector is My Connector. - */ - expect(wrapper.text().includes('My Connector')).toBeTruthy(); + await waitFor(() => { + expect(screen.getByText('My SN connector')).toBeInTheDocument(); + }); }); it('Resets selector on cancel', async () => { - const defaultProps = getDefaultProps(); const props = { ...defaultProps, caseData: { @@ -173,57 +208,76 @@ describe('EditConnector ', () => { }, }; - const wrapper = mount( + render( ); - wrapper.find('[data-test-subj="connector-edit"] button').simulate('click'); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.update(); - wrapper.find('button[data-test-subj="dropdown-connector-resilient-2"]').simulate('click'); - wrapper.update(); - wrapper.find(`[data-test-subj="edit-connectors-cancel"]`).last().simulate('click'); + userEvent.click(screen.getByTestId('connector-edit-button')); + userEvent.click(screen.getByTestId('dropdown-connectors')); await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).exists()).toBeFalsy(); + expect(screen.getByTestId('dropdown-connector-resilient-2')).toBeInTheDocument(); }); - expect(wrapper.text().includes('My Connector')).toBeTruthy(); + userEvent.click(screen.getByTestId('dropdown-connector-resilient-2')); + userEvent.click(screen.getByTestId('edit-connectors-cancel')); + + await waitFor(() => { + expect(screen.queryByTestId('edit-connectors-submit')).not.toBeInTheDocument(); + }); + + expect(screen.getByText('My SN connector')).toBeInTheDocument(); }); - it('Renders loading spinner', async () => { - const defaultProps = getDefaultProps(); + it('disabled the edit button when is loading', async () => { const props = { ...defaultProps, isLoading: true }; - const wrapper = mount( + + render( ); - await waitFor(() => - expect(wrapper.find(`[data-test-subj="connector-loading"]`).last().exists()).toBeTruthy() + + await waitFor(() => { + expect(screen.queryByTestId('connector-edit-button')).not.toBeInTheDocument(); + }); + }); + + it('does not shows the callouts when is loading', async () => { + const props = { ...defaultProps, isLoading: true }; + + render( + + + ); + + await waitFor(() => { + expect(screen.queryByTestId('push-callouts')).not.toBeInTheDocument(); + }); }); it('does not allow the connector to be edited when the user does not have write permissions', async () => { - const wrapper = mount( + render( - + ); - await waitFor(() => - expect(wrapper.find(`[data-test-subj="connector-edit"]`).exists()).toBeFalsy() - ); - expect(wrapper.find(`[data-test-subj="has-data-to-push-button"]`).exists()).toBeFalsy(); + await waitFor(() => { + expect(screen.queryByTestId('connector-edit-button')).not.toBeInTheDocument(); + }); + + await waitFor(() => { + expect(screen.queryByTestId('push-to-external-service')).not.toBeInTheDocument(); + }); }); it('display the callout message when none is selected', async () => { - const defaultProps = getDefaultProps(); - const props = { ...defaultProps, connectors: [] }; - const result = appMockRender.render(); + // default props has the none connector as selected + const result = appMockRender.render(); await waitFor(() => { expect(result.getByTestId('push-callouts')).toBeInTheDocument(); @@ -231,7 +285,6 @@ describe('EditConnector ', () => { }); it('disables the save button until changes are done ', async () => { - const defaultProps = getDefaultProps(); const serviceNowProps = { ...defaultProps, caseData: { @@ -261,18 +314,17 @@ describe('EditConnector ', () => { // simulate changing the connector userEvent.click(result.getByTestId('dropdown-connectors')); + await waitForEuiPopoverOpen(); + userEvent.click(result.getAllByTestId('dropdown-connector-no-connector')[0]); - expect(result.getByTestId('edit-connectors-submit')).toBeEnabled(); - // this strange assertion is required because of existing race conditions inside the EditConnector component await waitFor(() => { - expect(true).toBeTruthy(); + expect(result.getByTestId('edit-connectors-submit')).toBeEnabled(); }); }); it('disables the save button when no connector is the default', async () => { - const defaultProps = getDefaultProps(); const noneConnector = { ...defaultProps, caseData: { @@ -295,18 +347,17 @@ describe('EditConnector ', () => { // simulate changing the connector userEvent.click(result.getByTestId('dropdown-connectors')); + await waitForEuiPopoverOpen(); + userEvent.click(result.getAllByTestId('dropdown-connector-resilient-2')[0]); - expect(result.getByTestId('edit-connectors-submit')).toBeEnabled(); - // this strange assertion is required because of existing race conditions inside the EditConnector component await waitFor(() => { - expect(true).toBeTruthy(); + expect(result.getByTestId('edit-connectors-submit')).toBeEnabled(); }); }); it('shows the actions permission message if the user does not have read access to actions', async () => { - const defaultProps = getDefaultProps(); appMockRender.coreStart.application.capabilities = { ...appMockRender.coreStart.application.capabilities, actions: { save: false, show: false }, @@ -319,7 +370,6 @@ describe('EditConnector ', () => { }); it('does not show the actions permission message if the user has read access to actions', async () => { - const defaultProps = getDefaultProps(); appMockRender.coreStart.application.capabilities = { ...appMockRender.coreStart.application.capabilities, actions: { save: true, show: true }, @@ -332,7 +382,6 @@ describe('EditConnector ', () => { }); it('does not show the callout if the user does not have read access to actions', async () => { - const defaultProps = getDefaultProps(); const props = { ...defaultProps, connectors: [] }; appMockRender.coreStart.application.capabilities = { ...appMockRender.coreStart.application.capabilities, @@ -347,7 +396,6 @@ describe('EditConnector ', () => { }); it('does not show the push button if the user does not have read access to actions', async () => { - const defaultProps = getDefaultProps(); appMockRender.coreStart.application.capabilities = { ...appMockRender.coreStart.application.capabilities, actions: { save: false, show: false }, @@ -355,44 +403,43 @@ describe('EditConnector ', () => { const result = appMockRender.render(); await waitFor(() => { - expect(result.queryByTestId('has-data-to-push-button')).toBe(null); + expect(result.queryByTestId('push-to-external-service')).toBe(null); }); }); it('does not show the push button if the user does not have push permissions', async () => { - const defaultProps = getDefaultProps(); - appMockRender = createAppMockRenderer({ permissions: noPushCasesPermissions() }); const result = appMockRender.render(); + await waitFor(() => { - expect(result.queryByTestId('has-data-to-push-button')).toBe(null); + expect(result.queryByTestId('push-to-external-service')).toBe(null); }); }); it('does not show the edit connectors pencil if the user does not have read access to actions', async () => { - const defaultProps = getDefaultProps(); const props = { ...defaultProps, connectors: [] }; appMockRender.coreStart.application.capabilities = { ...appMockRender.coreStart.application.capabilities, actions: { save: false, show: false }, }; - const result = appMockRender.render(); + appMockRender.render(); + await waitFor(() => { - expect(result.getByTestId('connector-edit-header')).toBeInTheDocument(); - expect(result.queryByTestId('connector-edit')).toBe(null); + expect(screen.getByTestId('connector-edit-header')).toBeInTheDocument(); + expect(screen.queryByTestId('connector-edit-button')).not.toBeInTheDocument(); }); }); it('does not show the edit connectors pencil if the user does not have push permissions', async () => { - const defaultProps = getDefaultProps(); const props = { ...defaultProps, connectors: [] }; appMockRender = createAppMockRenderer({ permissions: noPushCasesPermissions() }); - const result = appMockRender.render(); + appMockRender.render(); + await waitFor(() => { - expect(result.getByTestId('connector-edit-header')).toBeInTheDocument(); - expect(result.queryByTestId('connector-edit')).toBe(null); + expect(screen.getByTestId('connector-edit-header')).toBeInTheDocument(); + expect(screen.queryByTestId('connector-edit-button')).toBe(null); }); }); }); diff --git a/x-pack/plugins/cases/public/components/edit_connector/index.tsx b/x-pack/plugins/cases/public/components/edit_connector/index.tsx index 25fc2a69ac4dc..8a63c9d02ad26 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/index.tsx +++ b/x-pack/plugins/cases/public/components/edit_connector/index.tsx @@ -4,7 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useEffect, useReducer, useState } from 'react'; + +/* eslint-disable complexity */ + +import React, { useCallback, useReducer } from 'react'; import deepEqual from 'fast-deep-equal'; import { EuiText, @@ -13,64 +16,34 @@ import { EuiFlexItem, EuiButton, EuiButtonEmpty, - EuiLoadingSpinner, EuiButtonIcon, } from '@elastic/eui'; -import styled from 'styled-components'; import { isEmpty, noop } from 'lodash/fp'; import type { FieldConfig } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { Form, UseField, useForm } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -import type { Case } from '../../../common/ui/types'; -import type { ActionConnector, ConnectorTypeFields } from '../../../common/api'; +import type { Case, CaseConnectors } from '../../../common/ui/types'; +import type { ActionConnector, CaseConnector, ConnectorTypeFields } from '../../../common/api'; import { NONE_CONNECTOR_ID } from '../../../common/api'; import { ConnectorSelector } from '../connector_selector/form'; import { ConnectorFieldsForm } from '../connectors/fields_form'; -import type { CaseUserActions } from '../../containers/types'; import { schema } from './schema'; -import { getConnectorFieldsFromUserActions } from './helpers'; import * as i18n from './translations'; import { getConnectorById, getConnectorsFormValidators } from '../utils'; import { usePushToService } from '../use_push_to_service'; -import type { CaseServices } from '../../containers/use_find_case_user_actions'; import { useApplicationCapabilities } from '../../common/lib/kibana'; -import { useCasesContext } from '../cases_context/use_cases_context'; +import { PushButton } from './push_button'; +import { PushCallouts } from './push_callouts'; +import { normalizeActionConnector, getNoneConnector } from '../configure_cases/utils'; export interface EditConnectorProps { caseData: Case; - caseServices: CaseServices; - connectorName: string; - connectors: ActionConnector[]; - hasDataToPush: boolean; + caseConnectors: CaseConnectors; + supportedActionConnectors: ActionConnector[]; isLoading: boolean; - isValidConnector: boolean; - onSubmit: ( - connectorId: string, - connectorFields: ConnectorTypeFields['fields'], - onError: () => void, - onSuccess: () => void - ) => void; - userActions: CaseUserActions[]; + onSubmit: (connector: CaseConnector, onError: () => void, onSuccess: () => void) => void; } -const MyFlexGroup = styled(EuiFlexGroup)` - ${({ theme }) => ` - p { - font-size: ${theme.eui.euiSizeM}; - } - `} -`; -const DisappearingFlexItem = styled(EuiFlexItem)` - ${({ $isHidden }: { $isHidden: boolean }) => - $isHidden && - ` - margin: 0 !important; - & .euiFlexItem { - margin: 0 !important; - } - `} -`; - interface State { currentConnector: ActionConnector | null; fields: ConnectorTypeFields['fields']; @@ -81,6 +54,7 @@ type Action = | { type: 'SET_CURRENT_CONNECTOR'; payload: State['currentConnector'] } | { type: 'SET_FIELDS'; payload: State['fields'] } | { type: 'SET_EDIT_CONNECTOR'; payload: State['editConnector'] }; + const editConnectorReducer = (state: State, action: Action) => { switch (action.type) { case 'SET_CURRENT_CONNECTOR': @@ -112,18 +86,15 @@ const initialState = { export const EditConnector = React.memo( ({ caseData, - caseServices, - connectorName, - connectors, - hasDataToPush, + caseConnectors, + supportedActionConnectors, isLoading, - isValidConnector, onSubmit, - userActions, }: EditConnectorProps) => { - const { permissions } = useCasesContext(); const caseFields = caseData.connector.fields; const selectedConnector = caseData.connector.id; + const actionConnector = getConnectorById(caseData.connector.id, supportedActionConnectors); + const isValidConnector = !!actionConnector; const { form } = useForm({ defaultValue: { connectorId: selectedConnector }, @@ -133,79 +104,46 @@ export const EditConnector = React.memo( const { actions } = useApplicationCapabilities(); const actionsReadCapabilities = actions.read; - // by default save if disabled - const [enableSave, setEnableSave] = useState(false); - const { setFieldValue, submit } = form; const [{ currentConnector, fields, editConnector }, dispatch] = useReducer( editConnectorReducer, - { ...initialState, fields: caseFields } - ); - - // only enable the save button if changes were made to the previous selected - // connector or its fields - useEffect(() => { - // null and none are equivalent to `no connector`. - // This makes sure we don't enable the button when the "no connector" option is selected - // by default. e.g. when a case is created without a selector - const isNoConnectorDeafultValue = - currentConnector === null && selectedConnector === NONE_CONNECTOR_ID; - const enable = - (!isNoConnectorDeafultValue && currentConnector?.id !== selectedConnector) || - !deepEqual(fields, caseFields); - - setEnableSave(enable); - }, [caseFields, currentConnector, fields, selectedConnector]); - - useEffect(() => { - // Initialize the current connector with the connector information attached to the case if we can find that - // connector in the retrieved connectors from the API call - if (!isLoading) { - dispatch({ - type: 'SET_CURRENT_CONNECTOR', - payload: getConnectorById(caseData.connector.id, connectors), - }); - - // Set the fields initially to whatever is present in the case, this should match with - // the latest user action for an update connector as well - dispatch({ - type: 'SET_FIELDS', - payload: caseFields, - }); + { + ...initialState, + fields: caseFields, + currentConnector: actionConnector, } - }, [caseData.connector.id, connectors, isLoading, caseFields]); + ); /** - * There is a race condition with this callback. At some point during the initial mounting of this component, this - * callback will be called. There are a couple problems with this: - * - * 1. If the call occurs before the above useEffect does its dispatches (aka while the connectors are still loading) this will - * result in setting the current connector to null when in fact we might have a valid connector. It could also - * cause issues when setting the fields because if there are no user actions then the getConnectorFieldsFromUserActions - * will return null even when the caseData.connector.fields is valid and populated. - * - * 2. If the call occurs after the above useEffect then the currentConnector should === newConnectorId - * - * As far as I know dispatch is synchronous so if the useEffect runs first it should successfully set currentConnector. If - * onChangeConnector runs first and sets stuff to null, then when useEffect runs it'll switch everything back to what we need it to be - * initially. + * only enable the save button if changes were made to the previous selected + * connector or its fields + * null and none are equivalent to `no connector`. + * This makes sure we don't enable the button when the "no connector" option is selected + * by default. e.g. when a case is created without a connector */ + const isDefaultNoneConnectorSelected = + currentConnector === null && selectedConnector === NONE_CONNECTOR_ID; + + const enableSave = + (!isDefaultNoneConnectorSelected && currentConnector?.id !== selectedConnector) || + !deepEqual(fields, caseFields); + const onChangeConnector = useCallback( (newConnectorId) => { // change connector on dropdown action if (currentConnector?.id !== newConnectorId) { dispatch({ type: 'SET_CURRENT_CONNECTOR', - payload: getConnectorById(newConnectorId, connectors), + payload: getConnectorById(newConnectorId, supportedActionConnectors), }); dispatch({ type: 'SET_FIELDS', - payload: getConnectorFieldsFromUserActions(newConnectorId, userActions ?? []), + payload: caseConnectors[newConnectorId]?.fields ?? null, }); } }, - [currentConnector, userActions, connectors] + [currentConnector, caseConnectors, supportedActionConnectors] ); const onFieldsChange = useCallback( @@ -220,36 +158,51 @@ export const EditConnector = React.memo( [fields, dispatch] ); - const onError = useCallback(() => { + const resetConnector = useCallback(() => { setFieldValue('connectorId', selectedConnector); + dispatch({ - type: 'SET_EDIT_CONNECTOR', - payload: false, + type: 'SET_CURRENT_CONNECTOR', + payload: actionConnector, }); - }, [dispatch, setFieldValue, selectedConnector]); - const onCancelConnector = useCallback(() => { - setFieldValue('connectorId', selectedConnector); dispatch({ type: 'SET_FIELDS', payload: caseFields, }); + dispatch({ type: 'SET_EDIT_CONNECTOR', payload: false, }); - }, [dispatch, selectedConnector, setFieldValue, caseFields]); + }, [actionConnector, caseFields, selectedConnector, setFieldValue]); + + const onError = useCallback(() => { + resetConnector(); + }, [resetConnector]); + + const onCancelConnector = useCallback(() => { + resetConnector(); + }, [resetConnector]); const onSubmitConnector = useCallback(async () => { const { isValid, data: newData } = await submit(); + if (isValid && newData.connectorId) { - onSubmit(newData.connectorId, fields, onError, noop); + const connector = getConnectorById(newData.connectorId, supportedActionConnectors); + const connectorToUpdate = connector + ? normalizeActionConnector(connector) + : getNoneConnector(); + + const connectorWithFields = { ...connectorToUpdate, fields } as CaseConnector; + onSubmit(connectorWithFields, onError, noop); + dispatch({ type: 'SET_EDIT_CONNECTOR', payload: false, }); } - }, [dispatch, submit, fields, onSubmit, onError]); + }, [submit, supportedActionConnectors, fields, onSubmit, onError]); const onEditClick = useCallback(() => { dispatch({ @@ -260,27 +213,42 @@ export const EditConnector = React.memo( const connectorIdConfig = getConnectorsFormValidators({ config: schema.connectorId as FieldConfig, - connectors, + connectors: supportedActionConnectors, }); - const { pushButton, pushCallouts } = usePushToService({ - connector: { - ...caseData.connector, - name: isEmpty(connectorName) ? caseData.connector.name : connectorName, - }, - caseServices, + const connectorWithName = { + ...caseData.connector, + name: isEmpty(actionConnector?.name) ? caseData.connector.name : actionConnector?.name ?? '', + }; + + const { + errorsMsg, + needsToBePushed, + hasBeenPushed, + isLoading: isLoadingPushToService, + hasPushPermissions, + hasErrorMessages, + hasLicenseError, + handlePushToService, + } = usePushToService({ + connector: connectorWithName, + caseConnectors, caseId: caseData.id, caseStatus: caseData.status, - connectors, - hasDataToPush, - onEditClick, isValidConnector, }); + const disablePushButton = + isLoadingPushToService || + errorsMsg.length > 0 || + !hasPushPermissions || + !isValidConnector || + !needsToBePushed; + return ( -

      {i18n.CONNECTORS}

      - {isLoading && } - {!isLoading && !editConnector && permissions.push && actionsReadCapabilities && ( + {!isLoading && !editConnector && hasPushPermissions && actionsReadCapabilities ? ( - )} - + ) : null} + - - {!isLoading && !editConnector && pushCallouts && actionsReadCapabilities && ( - {pushCallouts} + + {!isLoading && !editConnector && hasErrorMessages && actionsReadCapabilities && ( + + 0} + onEditClick={onEditClick} + /> + )} - -
      + + - -
      + + {!editConnector && !actionsReadCapabilities && ( @@ -372,16 +346,26 @@ export const EditConnector = React.memo(
      )} - {pushCallouts == null && + {!hasErrorMessages && !isLoading && !editConnector && - permissions.push && + hasPushPermissions && actionsReadCapabilities && ( - - {pushButton} + + + 0 || !needsToBePushed || !hasPushPermissions} + connectorName={connectorWithName.name} + /> + )} -
      + ); diff --git a/x-pack/plugins/cases/public/components/edit_connector/push_button.test.tsx b/x-pack/plugins/cases/public/components/edit_connector/push_button.test.tsx new file mode 100644 index 0000000000000..fee6fdc8d1557 --- /dev/null +++ b/x-pack/plugins/cases/public/components/edit_connector/push_button.test.tsx @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { AppMockRenderer } from '../../common/mock'; +import { createAppMockRenderer } from '../../common/mock'; +import { PushButton } from './push_button'; + +const pushToService = jest.fn(); + +const defaultProps = { + disabled: false, + isLoading: false, + errorsMsg: [], + hasBeenPushed: false, + showTooltip: false, + connectorName: 'My SN connector', + pushToService, +}; + +describe('PushButton ', () => { + let appMockRender: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders the button without tooltip', async () => { + appMockRender.render(); + + expect(screen.getByTestId('push-to-external-service')).toBeInTheDocument(); + expect(screen.queryByTestId('push-button-tooltip')).not.toBeInTheDocument(); + }); + + it('renders the correct label when the connector has not been pushed', async () => { + appMockRender.render(); + + expect(screen.getByText('Push as My SN connector incident')).toBeInTheDocument(); + }); + + it('renders the correct label when the connector has been pushed', async () => { + appMockRender.render(); + + expect(screen.getByText('Update My SN connector incident')).toBeInTheDocument(); + }); + + it('pushed correctly', async () => { + appMockRender.render(); + + userEvent.click(screen.getByTestId('push-to-external-service')); + expect(pushToService).toHaveBeenCalled(); + }); + + it('disables the button', async () => { + appMockRender.render(); + + expect(screen.getByTestId('push-to-external-service')).toBeDisabled(); + }); + + it('shows the tooltip context correctly', async () => { + appMockRender.render(); + + userEvent.hover(screen.getByTestId('push-to-external-service')); + + expect(await screen.findByText('My SN connector incident is up to date')).toBeInTheDocument(); + expect(await screen.findByText('No update is required')).toBeInTheDocument(); + }); + + it('shows the tooltip context correctly with custom message', async () => { + appMockRender.render( + + ); + + userEvent.hover(screen.getByTestId('push-to-external-service')); + + expect(await screen.findByText('My title')).toBeInTheDocument(); + expect(await screen.findByText('My desc')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/edit_connector/push_button.tsx b/x-pack/plugins/cases/public/components/edit_connector/push_button.tsx new file mode 100644 index 0000000000000..d004d69da85cf --- /dev/null +++ b/x-pack/plugins/cases/public/components/edit_connector/push_button.tsx @@ -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 React from 'react'; +import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; +import type { ErrorMessage } from '../use_push_to_service/callout/types'; +import * as i18n from './translations'; + +interface PushButtonProps { + isLoading: boolean; + disabled: boolean; + errorsMsg: ErrorMessage[]; + hasBeenPushed: boolean; + showTooltip: boolean; + connectorName: string; + pushToService: () => Promise; +} + +const PushButtonComponent: React.FC = ({ + disabled, + errorsMsg, + isLoading, + hasBeenPushed, + connectorName, + showTooltip, + pushToService, +}) => { + const button = ( + + {hasBeenPushed ? i18n.UPDATE_INCIDENT(connectorName) : i18n.PUSH_INCIDENT(connectorName)} + + ); + + return showTooltip ? ( + 0 ? errorsMsg[0].title : i18n.PUSH_LOCKED_TITLE(connectorName)} + content={

      {errorsMsg.length > 0 ? errorsMsg[0].description : i18n.PUSH_LOCKED_DESC}

      } + data-test-subj="push-button-tooltip" + > + {button} +
      + ) : ( + <>{button} + ); +}; + +PushButtonComponent.displayName = 'PushButton'; + +export const PushButton = React.memo(PushButtonComponent); diff --git a/x-pack/plugins/cases/public/components/edit_connector/push_callouts.test.tsx b/x-pack/plugins/cases/public/components/edit_connector/push_callouts.test.tsx new file mode 100644 index 0000000000000..46e2d56fbce9a --- /dev/null +++ b/x-pack/plugins/cases/public/components/edit_connector/push_callouts.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 { screen } from '@testing-library/react'; + +import type { AppMockRenderer } from '../../common/mock'; +import { createAppMockRenderer } from '../../common/mock'; +import { PushCallouts } from './push_callouts'; + +const onEditClick = jest.fn(); + +const defaultProps = { + hasConnectors: false, + hasLicenseError: false, + errorsMsg: [{ id: 'test-id', title: 'My title', description: 'My desc' }], + onEditClick, +}; + +describe('PushCallouts ', () => { + let appMockRender: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders', async () => { + appMockRender.render(); + + expect(await screen.findByText('My title')).toBeInTheDocument(); + expect(await screen.findByText('My desc')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/edit_connector/push_callouts.tsx b/x-pack/plugins/cases/public/components/edit_connector/push_callouts.tsx new file mode 100644 index 0000000000000..65ea0f20a7e82 --- /dev/null +++ b/x-pack/plugins/cases/public/components/edit_connector/push_callouts.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 { CaseCallOut } from '../use_push_to_service/callout'; +import type { ErrorMessage } from '../use_push_to_service/callout/types'; + +interface PushCalloutsProps { + hasConnectors: boolean; + hasLicenseError: boolean; + errorsMsg: ErrorMessage[]; + onEditClick: () => void; +} + +const PushCalloutsComponent: React.FC = ({ + hasConnectors, + hasLicenseError, + errorsMsg, + onEditClick, +}) => { + return ( + + ); +}; + +PushCalloutsComponent.displayName = 'PushCalloutsComponent'; + +export const PushCallouts = React.memo(PushCalloutsComponent); diff --git a/x-pack/plugins/cases/public/components/edit_connector/translations.ts b/x-pack/plugins/cases/public/components/edit_connector/translations.ts index ab69c94321703..532e0bb70e093 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/translations.ts +++ b/x-pack/plugins/cases/public/components/edit_connector/translations.ts @@ -8,6 +8,12 @@ import { i18n } from '@kbn/i18n'; export * from '../../common/translations'; +export { + UPDATE_INCIDENT, + PUSH_INCIDENT, + PUSH_LOCKED_TITLE, + PUSH_LOCKED_DESC, +} from '../use_push_to_service/translations'; export const EDIT_CONNECTOR_ARIA = i18n.translate( 'xpack.cases.editConnector.editConnectorLinkAria', diff --git a/x-pack/plugins/cases/public/components/use_push_to_service/callout/index.tsx b/x-pack/plugins/cases/public/components/use_push_to_service/callout/index.tsx index 08192a1efc68f..07f4ab2768c0b 100644 --- a/x-pack/plugins/cases/public/components/use_push_to_service/callout/index.tsx +++ b/x-pack/plugins/cases/public/components/use_push_to_service/callout/index.tsx @@ -67,25 +67,26 @@ const CaseCallOutComponent = ({ [messages] ); + const groupedByTypeErrorMessagesKeys = Object.keys(groupedByTypeErrorMessages) as Array< + keyof ErrorMessage['errorType'] + >; return ( <> - {(Object.keys(groupedByTypeErrorMessages) as Array).map( - (type: NonNullable) => { - const id = createCalloutId(groupedByTypeErrorMessages[type].messagesId); - return ( - - - - - ); - } - )} + {groupedByTypeErrorMessagesKeys.map((type: NonNullable, index) => { + const id = createCalloutId(groupedByTypeErrorMessages[type].messagesId); + return ( + + + {index !== groupedByTypeErrorMessagesKeys.length - 1 ? : null} + + ); + })} ); }; diff --git a/x-pack/plugins/cases/public/components/use_push_to_service/helpers.tsx b/x-pack/plugins/cases/public/components/use_push_to_service/helpers.tsx index 0a58678da6d0e..f2f19a91d11e5 100644 --- a/x-pack/plugins/cases/public/components/use_push_to_service/helpers.tsx +++ b/x-pack/plugins/cases/public/components/use_push_to_service/helpers.tsx @@ -42,12 +42,15 @@ export const getKibanaConfigError = () => ({ title: i18n.PUSH_DISABLE_BY_KIBANA_CONFIG_TITLE, description: ( - {'coming soon...'} + + {i18n.LINK_ACTIONS_CONFIGURATION} ), }} diff --git a/x-pack/plugins/cases/public/components/use_push_to_service/index.test.tsx b/x-pack/plugins/cases/public/components/use_push_to_service/index.test.tsx index 2cd381e7035b2..52598a80f000e 100644 --- a/x-pack/plugins/cases/public/components/use_push_to_service/index.test.tsx +++ b/x-pack/plugins/cases/public/components/use_push_to_service/index.test.tsx @@ -7,18 +7,19 @@ import React from 'react'; import { renderHook, act } from '@testing-library/react-hooks'; -import { render, screen } from '@testing-library/react'; import '../../common/mock/match_media'; import type { ReturnUsePushToService, UsePushToService } from '.'; import { usePushToService } from '.'; import { noPushCasesPermissions, readCasesPermissions, TestProviders } from '../../common/mock'; +import type { CaseConnector } from '../../../common/api'; import { CaseStatuses, ConnectorTypes } from '../../../common/api'; import { usePostPushToService } from '../../containers/use_post_push_to_service'; -import { basicPush, actionLicenses, connectorsMock } from '../../containers/mock'; +import { actionLicenses } from '../../containers/mock'; import { CLOSED_CASE_PUSH_ERROR_ID } from './callout/types'; -import * as i18n from './translations'; import { useGetActionLicense } from '../../containers/use_get_action_license'; +import { getCaseConnectorsMockResponse } from '../../common/mock/connectors'; +import { useRefreshCaseViewPage } from '../case_view/use_on_refresh_case_view_page'; jest.mock('../../containers/use_get_action_license', () => { return { @@ -28,80 +29,60 @@ jest.mock('../../containers/use_get_action_license', () => { jest.mock('../../containers/use_post_push_to_service'); jest.mock('../../containers/configure/api'); jest.mock('../../common/navigation/hooks'); +jest.mock('../case_view/use_on_refresh_case_view_page'); const useFetchActionLicenseMock = useGetActionLicense as jest.Mock; +const usePostPushToServiceMock = usePostPushToService as jest.Mock; describe('usePushToService', () => { const caseId = '12345'; - const onEditClick = jest.fn(); - const pushCaseToExternalService = jest.fn(); + const pushCaseToExternalService = jest.fn().mockReturnValue({}); const mockPostPush = { isLoading: false, pushCaseToExternalService, }; - const mockConnector = connectorsMock[0]; + const caseConnectors = getCaseConnectorsMockResponse(); + const mockConnector = caseConnectors['jira-1']; const actionLicense = actionLicenses[0]; - const caseServices = { - '123': { - ...basicPush, - firstPushIndex: 0, - lastPushIndex: 0, - commentsToUpdate: [], - hasDataToPush: true, - }, - }; const defaultArgs = { - actionsErrors: [], + caseId, + caseStatus: CaseStatuses.open, connector: { id: mockConnector.id, name: mockConnector.name, - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - caseId, - caseServices, - caseStatus: CaseStatuses.open, - configureCasesNavigation: { - href: 'href', - onClick: jest.fn(), - }, - connectors: connectorsMock, - hasDataToPush: true, - onEditClick, + type: mockConnector.type, + fields: mockConnector.fields, + } as CaseConnector, + caseConnectors, isValidConnector: true, }; beforeEach(() => { jest.clearAllMocks(); - (usePostPushToService as jest.Mock).mockImplementation(() => mockPostPush); - useFetchActionLicenseMock.mockImplementation(() => ({ + usePostPushToServiceMock.mockReturnValue(mockPostPush); + useFetchActionLicenseMock.mockReturnValue({ isLoading: false, data: actionLicense, - })); + }); }); - it('push case button posts the push with correct args', async () => { + it('calls pushCaseToExternalService with correct arguments', async () => { + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => {children}, + } + ); + await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => usePushToService(defaultArgs), - { - wrapper: ({ children }) => {children}, - } - ); - await waitForNextUpdate(); - result.current.pushButton.props.children.props.onClick(); - expect(pushCaseToExternalService).toBeCalledWith({ - caseId, - connector: { - fields: null, - id: 'servicenow-1', - name: 'My Connector', - type: ConnectorTypes.serviceNowITSM, - }, - }); - expect(result.current.pushCallouts).toBeNull(); + await result.current.handlePushToService(); + }); + + expect(pushCaseToExternalService).toBeCalledWith({ + caseId, + connector: defaultArgs.connector, }); }); @@ -113,18 +94,18 @@ describe('usePushToService', () => { enabledInLicense: false, }, })); - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => usePushToService(defaultArgs), - { - wrapper: ({ children }) => {children}, - } - ); - await waitForNextUpdate(); - const errorsMsg = result.current.pushCallouts?.props.messages; - expect(errorsMsg).toHaveLength(1); - expect(errorsMsg[0].id).toEqual('license-error'); - }); + + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => {children}, + } + ); + + const errorsMsg = result.current.errorsMsg; + expect(errorsMsg).toHaveLength(1); + expect(errorsMsg[0].id).toEqual('license-error'); + expect(result.current.hasErrorMessages).toBe(true); }); it('Displays message when user does not have case enabled in config', async () => { @@ -136,28 +117,188 @@ describe('usePushToService', () => { }, })); + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => {children}, + } + ); + + const errorsMsg = result.current.errorsMsg; + expect(errorsMsg).toHaveLength(1); + expect(errorsMsg[0].id).toEqual('kibana-config-error'); + expect(result.current.hasErrorMessages).toBe(true); + }); + + it('Displays message when user has select none as connector', async () => { + const { result } = renderHook( + () => + usePushToService({ + ...defaultArgs, + connector: { + id: 'none', + name: 'none', + type: ConnectorTypes.none, + fields: null, + }, + }), + { + wrapper: ({ children }) => {children}, + } + ); + + const errorsMsg = result.current.errorsMsg; + expect(errorsMsg).toHaveLength(1); + expect(errorsMsg[0].id).toEqual('connector-missing-error'); + expect(result.current.hasErrorMessages).toBe(true); + }); + + it('Displays message when connector is deleted', async () => { + const { result } = renderHook( + () => + usePushToService({ + ...defaultArgs, + connector: { + id: 'not-exist', + name: 'not-exist', + type: ConnectorTypes.none, + fields: null, + }, + isValidConnector: false, + }), + { + wrapper: ({ children }) => {children}, + } + ); + + const errorsMsg = result.current.errorsMsg; + expect(errorsMsg).toHaveLength(1); + expect(errorsMsg[0].id).toEqual('connector-deleted-error'); + expect(result.current.hasErrorMessages).toBe(true); + }); + + it('Displays message when case is closed', async () => { + const { result } = renderHook( + () => + usePushToService({ + ...defaultArgs, + caseStatus: CaseStatuses.closed, + }), + { + wrapper: ({ children }) => {children}, + } + ); + + const errorsMsg = result.current.errorsMsg; + expect(errorsMsg).toHaveLength(1); + expect(errorsMsg[0].id).toEqual(CLOSED_CASE_PUSH_ERROR_ID); + expect(result.current.hasErrorMessages).toBe(true); + }); + + it('should not call pushCaseToExternalService when the selected connector is none', async () => { + const { result } = renderHook( + () => + usePushToService({ + ...defaultArgs, + connector: { + id: 'none', + name: 'none', + type: ConnectorTypes.none, + fields: null, + }, + }), + { + wrapper: ({ children }) => {children}, + } + ); + await act(async () => { - const { result, waitForNextUpdate } = renderHook( + await result.current.handlePushToService(); + }); + + expect(pushCaseToExternalService).not.toBeCalled(); + }); + + it('refresh case view page after push', async () => { + const { result, waitFor } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => {children}, + } + ); + + await act(async () => { + await result.current.handlePushToService(); + }); + + await waitFor(() => { + expect(useRefreshCaseViewPage()).toHaveBeenCalled(); + }); + }); + + describe('user does not have write or push permissions', () => { + it('returns correct information about push permissions', async () => { + const { result } = renderHook( () => usePushToService(defaultArgs), { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => ( + {children} + ), } ); - await waitForNextUpdate(); - const errorsMsg = result.current.pushCallouts?.props.messages; - expect(errorsMsg).toHaveLength(1); - expect(errorsMsg[0].id).toEqual('kibana-config-error'); + expect(result.current.hasPushPermissions).toBe(false); }); - }); - it('Displays message when user does not have any connector configured', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( + it('does not display a message when user does not have a premium license', async () => { + useFetchActionLicenseMock.mockImplementation(() => ({ + isLoading: false, + data: { + ...actionLicense, + enabledInLicense: false, + }, + })); + + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => ( + {children} + ), + } + ); + + expect(result.current.errorsMsg).toEqual([]); + expect(result.current.hasErrorMessages).toBe(false); + }); + + it('does not display a message when user does not have case enabled in config', async () => { + useFetchActionLicenseMock.mockImplementation(() => ({ + isLoading: false, + data: { + ...actionLicense, + enabledInConfig: false, + }, + })); + + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => ( + {children} + ), + } + ); + + expect(result.current.errorsMsg).toEqual([]); + expect(result.current.hasErrorMessages).toBe(false); + }); + + it('does not display a message when user does not have any connector configured', async () => { + const { result } = renderHook( () => usePushToService({ ...defaultArgs, - connectors: [], connector: { id: 'none', name: 'none', @@ -166,24 +307,18 @@ describe('usePushToService', () => { }, }), { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => ( + {children} + ), } ); - await waitForNextUpdate(); - - render(result.current.pushCallouts ?? <>); - // getByText will thrown an error if the element is not found. - screen.getByText(i18n.CONFIGURE_CONNECTOR); - - const errorsMsg = result.current.pushCallouts?.props.messages; - expect(errorsMsg).toHaveLength(1); + expect(result.current.errorsMsg).toEqual([]); + expect(result.current.hasErrorMessages).toBe(false); }); - }); - it('Displays message when user does have a connector but is configured to none', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( + it('does not display a message when user does have a connector but is configured to none', async () => { + const { result } = renderHook( () => usePushToService({ ...defaultArgs, @@ -195,24 +330,18 @@ describe('usePushToService', () => { }, }), { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => ( + {children} + ), } ); - await waitForNextUpdate(); - - render(result.current.pushCallouts ?? <>); - // getByText will thrown an error if the element is not found. - screen.getByText(i18n.CONFIGURE_CONNECTOR); - - const errorsMsg = result.current.pushCallouts?.props.messages; - expect(errorsMsg).toHaveLength(1); + expect(result.current.errorsMsg).toEqual([]); + expect(result.current.hasErrorMessages).toBe(false); }); - }); - it('Displays message when connector is deleted', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( + it('does not display a message when connector is deleted', async () => { + const { result } = renderHook( () => usePushToService({ ...defaultArgs, @@ -225,23 +354,125 @@ describe('usePushToService', () => { isValidConnector: false, }), { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => ( + {children} + ), } ); - await waitForNextUpdate(); - const errorsMsg = result.current.pushCallouts?.props.messages; - expect(errorsMsg).toHaveLength(1); - expect(errorsMsg[0].id).toEqual('connector-deleted-error'); + + expect(result.current.errorsMsg).toEqual([]); + expect(result.current.hasErrorMessages).toBe(false); + }); + + it('does not display a message when case is closed', async () => { + const { result } = renderHook( + () => + usePushToService({ + ...defaultArgs, + caseStatus: CaseStatuses.closed, + }), + { + wrapper: ({ children }) => ( + {children} + ), + } + ); + + expect(result.current.errorsMsg).toEqual([]); + expect(result.current.hasErrorMessages).toBe(false); }); }); - it('Displays message when connector is deleted with empty connectors', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( + describe('returned values', () => { + it('initial', async () => { + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => {children}, + } + ); + + const { handlePushToService, errorsMsg, ...rest } = result.current; + + expect(rest).toEqual({ + hasBeenPushed: true, + hasErrorMessages: false, + hasLicenseError: false, + hasPushPermissions: true, + isLoading: false, + needsToBePushed: false, + }); + }); + + it('isLoading is true when usePostPushToService is loading', async () => { + usePostPushToServiceMock.mockReturnValue({ ...mockPostPush, isLoading: true }); + + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => {children}, + } + ); + + expect(result.current.isLoading).toBe(true); + }); + + it('isLoading is true when loading license', async () => { + useFetchActionLicenseMock.mockReturnValue({ + isLoading: true, + data: actionLicense, + }); + + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => {children}, + } + ); + + expect(result.current.isLoading).toBe(true); + }); + + it('hasErrorMessages=true if there are error messages', async () => { + const { result } = renderHook( + () => usePushToService({ ...defaultArgs, caseStatus: CaseStatuses.closed }), + { + wrapper: ({ children }) => {children}, + } + ); + + expect(result.current.hasErrorMessages).toBe(true); + }); + + it('needsToBePushed=true if the connector needs to be pushed', async () => { + const { result } = renderHook( + () => + usePushToService({ + ...defaultArgs, + caseConnectors: { + ...caseConnectors, + [mockConnector.id]: { + ...caseConnectors[mockConnector.id], + push: { + ...caseConnectors[mockConnector.id].push, + needsToBePushed: true, + }, + }, + }, + }), + { + wrapper: ({ children }) => {children}, + } + ); + + expect(result.current.needsToBePushed).toBe(true); + }); + + it('needsToBePushed=false if the connector does not exist', async () => { + const { result } = renderHook( () => usePushToService({ ...defaultArgs, - connectors: [], connector: { id: 'not-exist', name: 'not-exist', @@ -254,212 +485,106 @@ describe('usePushToService', () => { wrapper: ({ children }) => {children}, } ); - await waitForNextUpdate(); - const errorsMsg = result.current.pushCallouts?.props.messages; - expect(errorsMsg).toHaveLength(1); - expect(errorsMsg[0].id).toEqual('connector-deleted-error'); + + expect(result.current.needsToBePushed).toBe(false); }); - }); - it('Displays message when case is closed', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( + it('hasBeenPushed=false if the connector has been pushed', async () => { + const { result } = renderHook( () => usePushToService({ ...defaultArgs, - caseStatus: CaseStatuses.closed, + caseConnectors: { + ...caseConnectors, + [mockConnector.id]: { + ...caseConnectors[mockConnector.id], + push: { + ...caseConnectors[mockConnector.id].push, + hasBeenPushed: false, + }, + }, + }, }), { wrapper: ({ children }) => {children}, } ); - await waitForNextUpdate(); - const errorsMsg = result.current.pushCallouts?.props.messages; - expect(errorsMsg).toHaveLength(1); - expect(errorsMsg[0].id).toEqual(CLOSED_CASE_PUSH_ERROR_ID); + + expect(result.current.hasBeenPushed).toBe(false); }); - }); - describe('user does not have write permissions', () => { - it('disables the push button when the user does not have push permissions', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => usePushToService(defaultArgs), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); - await waitForNextUpdate(); - - const { getByTestId } = render(result.current.pushButton); - - expect(getByTestId('push-to-external-service')).toBeDisabled(); - }); + it('hasBeenPushed=false if the connector does not exist', async () => { + const { result } = renderHook( + () => + usePushToService({ + ...defaultArgs, + connector: { + id: 'not-exist', + name: 'not-exist', + type: ConnectorTypes.none, + fields: null, + }, + isValidConnector: false, + }), + { + wrapper: ({ children }) => {children}, + } + ); + + expect(result.current.hasBeenPushed).toBe(false); }); - it('does not display a message when user does not have a premium license', async () => { - useFetchActionLicenseMock.mockImplementation(() => ({ - isLoading: false, - data: { - ...actionLicense, - enabledInLicense: false, - }, - })); - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => usePushToService(defaultArgs), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); - await waitForNextUpdate(); - expect(result.current.pushCallouts).toBeNull(); + it('hasPushPermissions=false if it does not have push permission', async () => { + useFetchActionLicenseMock.mockReturnValue({ + isLoading: true, + data: actionLicense, }); + + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => ( + {children} + ), + } + ); + + expect(result.current.hasPushPermissions).toBe(false); }); - it('does not display a message when user does not have case enabled in config', async () => { + it('hasLicenseError=true if enabledInLicense=false', async () => { useFetchActionLicenseMock.mockImplementation(() => ({ isLoading: false, data: { ...actionLicense, - enabledInConfig: false, + enabledInLicense: false, }, })); - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => usePushToService(defaultArgs), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); - await waitForNextUpdate(); - expect(result.current.pushCallouts).toBeNull(); - }); - }); - it('does not display a message when user does not have any connector configured', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => - usePushToService({ - ...defaultArgs, - connectors: [], - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); - await waitForNextUpdate(); - expect(result.current.pushCallouts).toBeNull(); - }); - }); + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => {children}, + } + ); - it('does not display a message when user does have a connector but is configured to none', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => - usePushToService({ - ...defaultArgs, - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); - await waitForNextUpdate(); - expect(result.current.pushCallouts).toBeNull(); - }); + expect(result.current.hasLicenseError).toBe(true); }); - it('does not display a message when connector is deleted', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => - usePushToService({ - ...defaultArgs, - connector: { - id: 'not-exist', - name: 'not-exist', - type: ConnectorTypes.none, - fields: null, - }, - isValidConnector: false, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); - await waitForNextUpdate(); - expect(result.current.pushCallouts).toBeNull(); - }); - }); + it('hasLicenseError=false if data=undefined', async () => { + useFetchActionLicenseMock.mockImplementation(() => ({ + isLoading: false, + data: undefined, + })); - it('does not display a message when connector is deleted with empty connectors', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => - usePushToService({ - ...defaultArgs, - connectors: [], - connector: { - id: 'not-exist', - name: 'not-exist', - type: ConnectorTypes.none, - fields: null, - }, - isValidConnector: false, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); - await waitForNextUpdate(); - expect(result.current.pushCallouts).toBeNull(); - }); - }); + const { result } = renderHook( + () => usePushToService(defaultArgs), + { + wrapper: ({ children }) => {children}, + } + ); - it('does not display a message when case is closed', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => - usePushToService({ - ...defaultArgs, - caseStatus: CaseStatuses.closed, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); - await waitForNextUpdate(); - expect(result.current.pushCallouts).toBeNull(); - }); + expect(result.current.hasLicenseError).toBe(false); }); }); }); diff --git a/x-pack/plugins/cases/public/components/use_push_to_service/index.tsx b/x-pack/plugins/cases/public/components/use_push_to_service/index.tsx index 71a6184c7286f..b31a4faa80524 100644 --- a/x-pack/plugins/cases/public/components/use_push_to_service/index.tsx +++ b/x-pack/plugins/cases/public/components/use_push_to_service/index.tsx @@ -5,11 +5,9 @@ * 2.0. */ -import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; -import React, { useCallback, useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { usePostPushToService } from '../../containers/use_post_push_to_service'; -import { CaseCallOut } from './callout'; import { getLicenseError, getKibanaConfigError, @@ -17,47 +15,48 @@ import { getDeletedConnectorError, getCaseClosedInfo, } from './helpers'; -import * as i18n from './translations'; -import type { CaseConnector, ActionConnector } from '../../../common/api'; +import type { CaseConnector } from '../../../common/api'; import { CaseStatuses } from '../../../common/api'; -import type { CaseServices } from '../../containers/use_find_case_user_actions'; import type { ErrorMessage } from './callout/types'; import { useRefreshCaseViewPage } from '../case_view/use_on_refresh_case_view_page'; import { useGetActionLicense } from '../../containers/use_get_action_license'; import { useCasesContext } from '../cases_context/use_cases_context'; +import type { CaseConnectors } from '../../containers/types'; export interface UsePushToService { + caseConnectors: CaseConnectors; caseId: string; - caseServices: CaseServices; caseStatus: string; connector: CaseConnector; - connectors: ActionConnector[]; - hasDataToPush: boolean; isValidConnector: boolean; - onEditClick: () => void; } export interface ReturnUsePushToService { - pushButton: JSX.Element; - pushCallouts: JSX.Element | null; + errorsMsg: ErrorMessage[]; + hasBeenPushed: boolean; + needsToBePushed: boolean; + hasPushPermissions: boolean; + isLoading: boolean; + hasErrorMessages: boolean; + hasLicenseError: boolean; + handlePushToService: () => Promise; } export const usePushToService = ({ caseId, - caseServices, caseStatus, + caseConnectors, connector, - connectors, - hasDataToPush, isValidConnector, - onEditClick, }: UsePushToService): ReturnUsePushToService => { const { permissions } = useCasesContext(); const { isLoading, pushCaseToExternalService } = usePostPushToService(); + const refreshCaseViewPage = useRefreshCaseViewPage(); - const { isLoading: loadingLicense, data: actionLicense = null } = useGetActionLicense(); + const { isLoading: isLoadingLicense, data: actionLicense = null } = useGetActionLicense(); const hasLicenseError = actionLicense != null && !actionLicense.enabledInLicense; - const refreshCaseViewPage = useRefreshCaseViewPage(); + const needsToBePushed = !!caseConnectors[connector.id]?.push.needsToBePushed; + const hasBeenPushed = !!caseConnectors[connector.id]?.push.hasBeenPushed; const handlePushToService = useCallback(async () => { if (connector.id != null && connector.id !== 'none') { @@ -88,7 +87,7 @@ export const usePushToService = ({ * By priority of importance: * 1. Show license error. * 2. Show configuration error. - * 3. Show connector configuration error if the connector is set to none or no connectors have been created. + * 3. Show connector missing information if the connector is set to none. * 4. Show an error message if the connector has been deleted or the user does not have access to it. * 5. Show case closed message. */ @@ -101,11 +100,11 @@ export const usePushToService = ({ return [getKibanaConfigError()]; } - if (connector.id === 'none' && !loadingLicense && !hasLicenseError) { + if (connector.id === 'none' && !isLoadingLicense && !hasLicenseError) { return [getConnectorMissingInfo()]; } - if (!isValidConnector && !loadingLicense && !hasLicenseError) { + if (!isValidConnector && !isLoadingLicense && !hasLicenseError) { return [getDeletedConnectorError()]; } @@ -114,79 +113,24 @@ export const usePushToService = ({ } return errors; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [actionLicense, caseStatus, connectors.length, connector, loadingLicense, permissions.update]); - - const pushToServiceButton = useMemo( - () => ( - 0 || - !permissions.push || - !isValidConnector || - !hasDataToPush - } - isLoading={isLoading} - > - {caseServices[connector.id] - ? i18n.UPDATE_THIRD(connector.name) - : i18n.PUSH_THIRD(connector.name)} - - ), - // eslint-disable-next-line react-hooks/exhaustive-deps - [ - connector, - connectors, - errorsMsg, - handlePushToService, - hasDataToPush, - isLoading, - loadingLicense, - permissions.push, - isValidConnector, - ] - ); - - const objToReturn = useMemo(() => { - const hidePushButton = errorsMsg.length > 0 || !hasDataToPush || !permissions.push; - - return { - pushButton: hidePushButton ? ( - 0 ? errorsMsg[0].title : i18n.PUSH_LOCKED_TITLE(connector.name)} - content={

      {errorsMsg.length > 0 ? errorsMsg[0].description : i18n.PUSH_LOCKED_DESC}

      } - > - {pushToServiceButton} -
      - ) : ( - <>{pushToServiceButton} - ), - pushCallouts: - errorsMsg.length > 0 ? ( - 0} - hasLicenseError={hasLicenseError} - messages={errorsMsg} - onEditClick={onEditClick} - /> - ) : null, - }; }, [ - connector.name, - connectors.length, - errorsMsg, - hasDataToPush, + actionLicense, + caseStatus, + connector.id, hasLicenseError, - onEditClick, - pushToServiceButton, - permissions.push, + isValidConnector, + isLoadingLicense, + permissions.update, ]); - return objToReturn; + return { + errorsMsg, + hasErrorMessages: errorsMsg.length > 0, + needsToBePushed, + hasBeenPushed, + isLoading: isLoading || isLoadingLicense, + hasPushPermissions: permissions.push, + hasLicenseError, + handlePushToService, + }; }; diff --git a/x-pack/plugins/cases/public/components/use_push_to_service/translations.ts b/x-pack/plugins/cases/public/components/use_push_to_service/translations.ts index 1214b9f790e0a..0f7e98a3845f2 100644 --- a/x-pack/plugins/cases/public/components/use_push_to_service/translations.ts +++ b/x-pack/plugins/cases/public/components/use_push_to_service/translations.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; export * from '../../common/translations'; -export const PUSH_THIRD = (thirdParty: string) => { +export const PUSH_INCIDENT = (thirdParty: string) => { if (thirdParty === 'none') { return i18n.translate('xpack.cases.caseView.pushThirdPartyIncident', { defaultMessage: 'Push as external incident', @@ -22,7 +22,7 @@ export const PUSH_THIRD = (thirdParty: string) => { }); }; -export const UPDATE_THIRD = (thirdParty: string) => { +export const UPDATE_INCIDENT = (thirdParty: string) => { if (thirdParty === 'none') { return i18n.translate('xpack.cases.caseView.updateThirdPartyIncident', { defaultMessage: 'Update external incident', @@ -76,3 +76,10 @@ export const PUSH_DISABLE_BY_LICENSE_TITLE = i18n.translate( export const LINK_CLOUD_DEPLOYMENT = i18n.translate('xpack.cases.caseView.cloudDeploymentLink', { defaultMessage: 'cloud deployment', }); + +export const LINK_ACTIONS_CONFIGURATION = i18n.translate( + 'xpack.cases.caseView.actionsConfigurationLink', + { + defaultMessage: 'Alerting and action settings in Kibana', + } +); diff --git a/x-pack/plugins/cases/public/components/user_actions/comment/comment.tsx b/x-pack/plugins/cases/public/components/user_actions/comment/comment.tsx index f5ec9ec742d55..c63f53f13bbe6 100644 --- a/x-pack/plugins/cases/public/components/user_actions/comment/comment.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/comment/comment.tsx @@ -86,7 +86,7 @@ const getCreateCommentUserAction = ({ comment: Comment; } & Omit< UserActionBuilderArgs, - 'caseServices' | 'comments' | 'index' | 'handleOutlineComment' | 'currentUserProfile' + 'comments' | 'index' | 'handleOutlineComment' | 'currentUserProfile' >): EuiCommentProps[] => { switch (comment.type) { case CommentType.user: @@ -185,6 +185,7 @@ export const createCommentUserActionBuilder: UserActionBuilder = ({ handleManageQuote, handleOutlineComment, actionsNavigation, + caseConnectors, }) => ({ build: () => { const commentUserAction = userAction as UserActionResponse; @@ -226,6 +227,7 @@ export const createCommentUserActionBuilder: UserActionBuilder = ({ handleDeleteComment, handleManageQuote, actionsNavigation, + caseConnectors, }); return commentAction; diff --git a/x-pack/plugins/cases/public/components/user_actions/index.test.tsx b/x-pack/plugins/cases/public/components/user_actions/index.test.tsx index 4ba2143254065..2cfc535940f45 100644 --- a/x-pack/plugins/cases/public/components/user_actions/index.test.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/index.test.tsx @@ -14,7 +14,6 @@ import routeData from 'react-router'; import { useUpdateComment } from '../../containers/use_update_comment'; import { basicCase, - basicPush, getUserAction, getHostIsolationUserAction, hostIsolationComment, @@ -24,6 +23,7 @@ import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer, TestProviders } from '../../common/mock'; import { Actions } from '../../../common/api'; import { userProfiles, userProfilesMap } from '../../containers/user_profiles/api.mock'; +import { connectorsMock, getCaseConnectorsMockResponse } from '../../common/mock/connectors'; const fetchUserActions = jest.fn(); const onUpdateField = jest.fn(); @@ -31,11 +31,11 @@ const updateCase = jest.fn(); const onShowAlertDetails = jest.fn(); const defaultProps = { - caseServices: {}, + caseConnectors: getCaseConnectorsMockResponse(), caseUserActions: [], userProfiles: new Map(), currentUserProfile: undefined, - connectors: [], + connectors: connectorsMock, actionsNavigation: { href: jest.fn(), onClick: jest.fn() }, getRuleDetailsHref: jest.fn(), onRuleDetailsClick: jest.fn(), @@ -95,29 +95,26 @@ describe(`UserActions`, () => { }); it('Renders service now update line with top and bottom when push is required', async () => { + const caseConnectors = getCaseConnectorsMockResponse({ 'push.needsToBePushed': true }); + const ourActions = [ - getUserAction('pushed', 'push_to_service'), - getUserAction('comment', Actions.update), + getUserAction('pushed', 'push_to_service', { + createdAt: '2023-01-17T09:46:29.813Z', + }), ]; const props = { ...defaultProps, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 0, - lastPushIndex: 0, - commentsToUpdate: [`${ourActions[ourActions.length - 1].commentId}`], - hasDataToPush: true, - }, - }, + caseConnectors, caseUserActions: ourActions, }; + const wrapper = mount( ); + await waitFor(() => { expect(wrapper.find(`[data-test-subj="top-footer"]`).exists()).toEqual(true); expect(wrapper.find(`[data-test-subj="bottom-footer"]`).exists()).toEqual(true); @@ -125,19 +122,15 @@ describe(`UserActions`, () => { }); it('Renders service now update line with top only when push is up to date', async () => { - const ourActions = [getUserAction('pushed', 'push_to_service')]; + const ourActions = [ + getUserAction('pushed', 'push_to_service', { + createdAt: '2023-01-17T09:46:29.813Z', + }), + ]; + const props = { ...defaultProps, caseUserActions: ourActions, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 0, - lastPushIndex: 0, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, }; const wrapper = mount( @@ -150,6 +143,7 @@ describe(`UserActions`, () => { expect(wrapper.find(`[data-test-subj="bottom-footer"]`).exists()).toEqual(false); }); }); + it('Outlines comment when update move to link is clicked', async () => { const ourActions = [ getUserAction('comment', Actions.create), diff --git a/x-pack/plugins/cases/public/components/user_actions/index.tsx b/x-pack/plugins/cases/public/components/user_actions/index.tsx index bf6aaf6ea2a3a..74c5cca881c2d 100644 --- a/x-pack/plugins/cases/public/components/user_actions/index.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/index.tsx @@ -80,7 +80,7 @@ const MyEuiCommentList = styled(EuiCommentList)` export const UserActions = React.memo( ({ - caseServices, + caseConnectors, caseUserActions, userProfiles, currentUserProfile, @@ -190,12 +190,12 @@ export const UserActions = React.memo( const userActionBuilder = builder({ appId, caseData, + caseConnectors, externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, userAction, userProfiles, currentUserProfile, - caseServices, comments: caseData.comments, index, commentRefs, @@ -220,6 +220,7 @@ export const UserActions = React.memo( ), [ appId, + caseConnectors, caseUserActions, userProfiles, currentUserProfile, @@ -227,7 +228,6 @@ export const UserActions = React.memo( persistableStateAttachmentTypeRegistry, descriptionCommentListObj, caseData, - caseServices, commentRefs, manageMarkdownEditIds, selectedOutlineCommentId, diff --git a/x-pack/plugins/cases/public/components/user_actions/mock.ts b/x-pack/plugins/cases/public/components/user_actions/mock.ts index 33eae2b43fbae..87034e4ccf191 100644 --- a/x-pack/plugins/cases/public/components/user_actions/mock.ts +++ b/x-pack/plugins/cases/public/components/user_actions/mock.ts @@ -9,22 +9,14 @@ import { Actions } from '../../../common/api'; import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { ExternalReferenceAttachmentTypeRegistry } from '../../client/attachment_framework/external_reference_registry'; import { PersistableStateAttachmentTypeRegistry } from '../../client/attachment_framework/persistable_state_registry'; -import { basicCase, basicPush, getUserAction } from '../../containers/mock'; +import { getCaseConnectorsMockResponse } from '../../common/mock/connectors'; +import { basicCase, getUserAction } from '../../containers/mock'; import { userProfiles, userProfilesMap } from '../../containers/user_profiles/api.mock'; import type { UserActionBuilderArgs } from './types'; export const getMockBuilderArgs = (): UserActionBuilderArgs => { const userAction = getUserAction('title', Actions.update); const commentRefs = { current: {} }; - const caseServices = { - '123': { - ...basicPush, - firstPushIndex: 0, - lastPushIndex: 0, - commentsToUpdate: [], - hasDataToPush: true, - }, - }; const alertData = { 'alert-id-1': { @@ -51,6 +43,8 @@ export const getMockBuilderArgs = (): UserActionBuilderArgs => { }, }; + const caseConnectors = getCaseConnectorsMockResponse(); + const getRuleDetailsHref = jest.fn().mockReturnValue('https://example.com'); const onRuleDetailsClick = jest.fn(); const onShowAlertDetails = jest.fn(); @@ -70,7 +64,6 @@ export const getMockBuilderArgs = (): UserActionBuilderArgs => { persistableStateAttachmentTypeRegistry, caseData: basicCase, comments: basicCase.comments, - caseServices, index: 0, alertData, commentRefs, @@ -78,6 +71,7 @@ export const getMockBuilderArgs = (): UserActionBuilderArgs => { selectedOutlineCommentId: '', loadingCommentIds: [], loadingAlertData: false, + caseConnectors, getRuleDetailsHref, onRuleDetailsClick, onShowAlertDetails, diff --git a/x-pack/plugins/cases/public/components/user_actions/pushed.test.tsx b/x-pack/plugins/cases/public/components/user_actions/pushed.test.tsx index 219a7a6d2c7c8..14e7199047a7a 100644 --- a/x-pack/plugins/cases/public/components/user_actions/pushed.test.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/pushed.test.tsx @@ -14,13 +14,13 @@ import { getUserAction } from '../../containers/mock'; import { TestProviders } from '../../common/mock'; import { createPushedUserActionBuilder } from './pushed'; import { getMockBuilderArgs } from './mock'; +import { getCaseConnectorsMockResponse } from '../../common/mock/connectors'; jest.mock('../../common/lib/kibana'); jest.mock('../../common/navigation/hooks'); describe('createPushedUserActionBuilder ', () => { const builderArgs = getMockBuilderArgs(); - const caseServices = builderArgs.caseServices; beforeEach(() => { jest.clearAllMocks(); @@ -31,8 +31,6 @@ describe('createPushedUserActionBuilder ', () => { const builder = createPushedUserActionBuilder({ ...builderArgs, userAction, - caseServices, - index: 0, }); const createdUserAction = builder.build(); @@ -42,20 +40,22 @@ describe('createPushedUserActionBuilder ', () => { ); - expect(screen.getByText('pushed as new incident connector name')).toBeInTheDocument(); + expect(screen.getByText('pushed as new incident My SN connector')).toBeInTheDocument(); expect(screen.getByText('external title').closest('a')).toHaveAttribute( 'href', 'basicPush.com' ); }); - it('renders correctly when updating an external service', async () => { + it('renders correctly if oldestUserActionPushDate is not defined', async () => { const userAction = getUserAction('pushed', Actions.push_to_service); + const caseConnectors = getCaseConnectorsMockResponse({ + 'push.details.oldestUserActionPushDate': undefined, + }); const builder = createPushedUserActionBuilder({ ...builderArgs, + caseConnectors, userAction, - caseServices, - index: 1, }); const createdUserAction = builder.build(); @@ -65,22 +65,19 @@ describe('createPushedUserActionBuilder ', () => { ); - expect(screen.getByText('updated incident connector name')).toBeInTheDocument(); + expect(screen.getByText('pushed as new incident My SN connector')).toBeInTheDocument(); }); - it('renders the pushing indicators correctly', async () => { + it('renders correctly when updating an external service', async () => { const userAction = getUserAction('pushed', Actions.push_to_service); + const caseConnectors = getCaseConnectorsMockResponse({ + 'push.details.oldestUserActionPushDate': '2023-01-16T09:46:29.813Z', + }); + const builder = createPushedUserActionBuilder({ ...builderArgs, + caseConnectors, userAction, - caseServices: { - ...caseServices, - '123': { - ...caseServices['123'], - lastPushIndex: 1, - }, - }, - index: 1, }); const createdUserAction = builder.build(); @@ -90,24 +87,65 @@ describe('createPushedUserActionBuilder ', () => { ); - expect(screen.getByText('Already pushed to connector name incident')).toBeInTheDocument(); - expect(screen.getByText('Requires update to connector name incident')).toBeInTheDocument(); + expect(screen.getByText('updated incident My SN connector')).toBeInTheDocument(); }); - it('shows only the already pushed indicator if has no data to push', async () => { - const userAction = getUserAction('pushed', Actions.push_to_service); + it('shows only the top footer if it is the latest push and there is nothing to push', async () => { + const userAction = getUserAction('pushed', Actions.push_to_service, { + createdAt: '2023-01-17T09:46:29.813Z', + }); + + const builder = createPushedUserActionBuilder({ + ...builderArgs, + userAction, + }); + + const createdUserAction = builder.build(); + render( + + + + ); + + expect(screen.getByText('Already pushed to My SN connector incident')).toBeInTheDocument(); + expect( + screen.queryByText('Requires update to My SN connector incident') + ).not.toBeInTheDocument(); + }); + + it('shows both footers if the connectors needs to be pushed and is the latest push', async () => { + const caseConnectors = getCaseConnectorsMockResponse({ + 'push.needsToBePushed': true, + }); + + const userAction = getUserAction('pushed', Actions.push_to_service, { + createdAt: '2023-01-17T09:46:29.813Z', + }); + const builder = createPushedUserActionBuilder({ + ...builderArgs, + caseConnectors, + userAction, + }); + + const createdUserAction = builder.build(); + render( + + + + ); + + expect(screen.getByText('Already pushed to My SN connector incident')).toBeInTheDocument(); + expect(screen.getByText('Requires update to My SN connector incident')).toBeInTheDocument(); + }); + + it('does not show the footers if it is not the latest push', async () => { + const userAction = getUserAction('pushed', Actions.push_to_service, { + createdAt: '2020-01-17T09:46:29.813Z', + }); + const builder = createPushedUserActionBuilder({ ...builderArgs, userAction, - caseServices: { - ...caseServices, - '123': { - ...caseServices['123'], - lastPushIndex: 1, - hasDataToPush: false, - }, - }, - index: 1, }); const createdUserAction = builder.build(); @@ -117,14 +155,51 @@ describe('createPushedUserActionBuilder ', () => { ); - expect(screen.getByText('Already pushed to connector name incident')).toBeInTheDocument(); expect( - screen.queryByText('Requires update to connector name incident') + screen.queryByText('Already pushed to My SN connector incident') + ).not.toBeInTheDocument(); + + expect( + screen.queryByText('Requires update to My SN connector incident') + ).not.toBeInTheDocument(); + }); + + it('does not show the footers if latestUserActionPushDate is not defined', async () => { + const caseConnectors = getCaseConnectorsMockResponse({ + 'push.needsToBePushed': true, + 'push.details.latestUserActionPushDate': undefined, + }); + + const userAction = getUserAction('pushed', Actions.push_to_service, { + createdAt: '2023-01-17T09:46:29.813Z', + }); + + const builder = createPushedUserActionBuilder({ + ...builderArgs, + caseConnectors, + userAction, + }); + + const createdUserAction = builder.build(); + render( + + + + ); + + expect( + screen.queryByText('Already pushed to My SN connector incident') + ).not.toBeInTheDocument(); + + expect( + screen.queryByText('Requires update to My SN connector incident') ).not.toBeInTheDocument(); }); it('does not show the push information if the connector is none', async () => { + const caseConnectors = getCaseConnectorsMockResponse({ 'push.needsToBePushed': true }); const userAction = getUserAction('pushed', Actions.push_to_service, { + createdAt: '2023-01-17T09:46:29.813Z', payload: { externalService: { connectorId: NONE_CONNECTOR_ID, connectorName: 'none connector' }, }, @@ -132,15 +207,8 @@ describe('createPushedUserActionBuilder ', () => { const builder = createPushedUserActionBuilder({ ...builderArgs, + caseConnectors, userAction, - caseServices: { - ...caseServices, - '123': { - ...caseServices['123'], - lastPushIndex: 1, - }, - }, - index: 1, }); const createdUserAction = builder.build(); @@ -152,9 +220,11 @@ describe('createPushedUserActionBuilder ', () => { expect(screen.queryByText('pushed as new incident none connector')).not.toBeInTheDocument(); expect(screen.queryByText('updated incident none connector')).not.toBeInTheDocument(); - expect(screen.queryByText('Already pushed to connector name incident')).not.toBeInTheDocument(); expect( - screen.queryByText('Requires update to connector name incident') + screen.queryByText('Already pushed to My SN connector incident') + ).not.toBeInTheDocument(); + expect( + screen.queryByText('Requires update to My SN connector incident') ).not.toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/user_actions/pushed.tsx b/x-pack/plugins/cases/public/components/user_actions/pushed.tsx index e0352d7d96ccb..6386cade1eadd 100644 --- a/x-pack/plugins/cases/public/components/user_actions/pushed.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/pushed.tsx @@ -10,29 +10,52 @@ import type { EuiCommentProps } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; import type { PushedUserAction } from '../../../common/api'; -import { Actions, NONE_CONNECTOR_ID } from '../../../common/api'; +import { Actions } from '../../../common/api'; import type { UserActionBuilder, UserActionResponse } from './types'; import { createCommonUpdateUserActionBuilder } from './common'; import * as i18n from './translations'; -import type { CaseServices } from '../../containers/use_find_case_user_actions'; -import type { CaseExternalService } from '../../containers/types'; - -const getPushInfo = ( - caseServices: CaseServices, - externalService: CaseExternalService | undefined, - index: number -) => - externalService != null && externalService.connectorId !== NONE_CONNECTOR_ID - ? { - firstPush: caseServices[externalService.connectorId]?.firstPushIndex === index, - parsedConnectorId: externalService.connectorId, - parsedConnectorName: externalService.connectorName, - } - : { - firstPush: false, - parsedConnectorId: NONE_CONNECTOR_ID, - parsedConnectorName: NONE_CONNECTOR_ID, - }; +import type { CaseConnectors } from '../../containers/types'; + +const getPushDates = ( + userActionPushedAt: string, + connectorPushedAt: string | undefined +): { userActionDate: Date; connectorDate: Date } | undefined => { + if (!connectorPushedAt) { + return; + } + + const pushedDate = new Date(userActionPushedAt); + const connectorDate = new Date(connectorPushedAt); + + if (isNaN(pushedDate.getTime()) || isNaN(connectorDate.getTime())) { + return; + } + + return { + userActionDate: pushedDate, + connectorDate, + }; +}; + +const isLatestPush = (pushedAt: string, latestPush: string | undefined) => { + const dates = getPushDates(pushedAt, latestPush); + + if (!dates) { + return false; + } + + return dates.userActionDate.getTime() >= dates.connectorDate.getTime(); +}; + +const isFirstPush = (pushedAt: string, oldestPush: string | undefined) => { + const dates = getPushDates(pushedAt, oldestPush); + + if (!dates) { + return true; + } + + return dates.userActionDate.getTime() <= dates.connectorDate.getTime(); +}; const getLabelTitle = (action: UserActionResponse, firstPush: boolean) => { const externalService = action.payload.externalService; @@ -60,50 +83,36 @@ const getLabelTitle = (action: UserActionResponse, firstPush: const getFooters = ({ userAction, - caseServices, - connectorId, - connectorName, - index, + connectorInfo, }: { userAction: UserActionResponse; - caseServices: CaseServices; - connectorId: string; - connectorName: string; - index: number; + connectorInfo: CaseConnectors[string]; }): EuiCommentProps[] => { - const showTopFooter = - userAction.action === Actions.push_to_service && - index === caseServices[connectorId]?.lastPushIndex; - - const showBottomFooter = - userAction.action === Actions.push_to_service && - index === caseServices[connectorId]?.lastPushIndex && - caseServices[connectorId].hasDataToPush; + const footers: EuiCommentProps[] = []; + const latestPush = isLatestPush( + userAction.createdAt, + connectorInfo.push.details?.latestUserActionPushDate + ); - let footers: EuiCommentProps[] = []; + const showTopFooter = userAction.action === Actions.push_to_service && latestPush; + const showBottomFooter = showTopFooter && connectorInfo.push.needsToBePushed; if (showTopFooter) { - footers = [ - ...footers, - { - username: '', - event: i18n.ALREADY_PUSHED_TO_SERVICE(`${connectorName}`), - timelineAvatar: 'sortUp', - 'data-test-subj': 'top-footer', - }, - ]; + footers.push({ + username: '', + event: i18n.ALREADY_PUSHED_TO_SERVICE(`${connectorInfo.name}`), + timelineAvatar: 'sortUp', + 'data-test-subj': 'top-footer', + }); } if (showBottomFooter) { - footers = [ - ...footers, - { - username: '', - event: i18n.REQUIRED_UPDATE_TO_SERVICE(`${connectorName}`), - timelineAvatar: 'sortDown', - 'data-test-subj': 'bottom-footer', - }, - ]; + footers.push({ + username: '', + event: i18n.REQUIRED_UPDATE_TO_SERVICE(`${connectorInfo.name}`), + timelineAvatar: 'sortDown', + 'data-test-subj': 'bottom-footer', + }); } return footers; @@ -112,31 +121,25 @@ const getFooters = ({ export const createPushedUserActionBuilder: UserActionBuilder = ({ userAction, userProfiles, - caseServices, - index, + caseConnectors, handleOutlineComment, }) => ({ build: () => { const pushedUserAction = userAction as UserActionResponse; - const { firstPush, parsedConnectorId, parsedConnectorName } = getPushInfo( - caseServices, - pushedUserAction.payload.externalService, - index - ); + const connectorId = pushedUserAction.payload.externalService.connectorId; + const connectorInfo = caseConnectors[connectorId]; - if (parsedConnectorId === NONE_CONNECTOR_ID) { + if (!connectorInfo) { return []; } - const footers = getFooters({ - userAction: pushedUserAction, - caseServices, - connectorId: parsedConnectorId, - connectorName: parsedConnectorName, - index, - }); - + const firstPush = isFirstPush( + userAction.createdAt, + connectorInfo.push.details?.oldestUserActionPushDate + ); + const footers = getFooters({ userAction: pushedUserAction, connectorInfo }); const label = getLabelTitle(pushedUserAction, firstPush); + const commonBuilder = createCommonUpdateUserActionBuilder({ userProfiles, userAction, diff --git a/x-pack/plugins/cases/public/components/user_actions/types.ts b/x-pack/plugins/cases/public/components/user_actions/types.ts index 9bf750437d29d..92001d633e912 100644 --- a/x-pack/plugins/cases/public/components/user_actions/types.ts +++ b/x-pack/plugins/cases/public/components/user_actions/types.ts @@ -9,8 +9,13 @@ import type { EuiCommentProps } from '@elastic/eui'; import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; import type { SnakeToCamelCase } from '../../../common/types'; import type { ActionTypes, UserActionWithResponse } from '../../../common/api'; -import type { Case, CaseUserActions, Comment, UseFetchAlertData } from '../../containers/types'; -import type { CaseServices } from '../../containers/use_find_case_user_actions'; +import type { + Case, + CaseConnectors, + CaseUserActions, + Comment, + UseFetchAlertData, +} from '../../containers/types'; import type { AddCommentRefObject } from '../add_comment'; import type { UserActionMarkdownRefObject } from './markdown_form'; import type { CasesNavigation } from '../links'; @@ -21,7 +26,7 @@ import type { PersistableStateAttachmentTypeRegistry } from '../../client/attach import type { CurrentUserProfile } from '../types'; export interface UserActionTreeProps { - caseServices: CaseServices; + caseConnectors: CaseConnectors; caseUserActions: CaseUserActions[]; userProfiles: Map; currentUserProfile: CurrentUserProfile; @@ -47,8 +52,8 @@ export interface UserActionBuilderArgs { currentUserProfile: CurrentUserProfile; externalReferenceAttachmentTypeRegistry: ExternalReferenceAttachmentTypeRegistry; persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry; + caseConnectors: CaseConnectors; userAction: CaseUserActions; - caseServices: CaseServices; comments: Comment[]; index: number; commentRefs: React.MutableRefObject< diff --git a/x-pack/plugins/cases/public/containers/__mocks__/api.ts b/x-pack/plugins/cases/public/containers/__mocks__/api.ts index 431ce6626e2ea..ea329f67ad791 100644 --- a/x-pack/plugins/cases/public/containers/__mocks__/api.ts +++ b/x-pack/plugins/cases/public/containers/__mocks__/api.ts @@ -27,7 +27,7 @@ import { tags, findCaseUserActionsResponse, } from '../mock'; -import type { CaseUpdateRequest, ResolvedCase } from '../../../common/ui/types'; +import type { CaseConnectors, CaseUpdateRequest, ResolvedCase } from '../../../common/ui/types'; import { SeverityAll } from '../../../common/ui/types'; import type { CasePatchRequest, @@ -39,6 +39,7 @@ import { CaseStatuses } from '../../../common/api'; import type { ValidFeatureId } from '@kbn/rule-data-utils'; import type { UserProfile } from '@kbn/security-plugin/common'; import { userProfiles } from '../user_profiles/api.mock'; +import { getCaseConnectorsMockResponse } from '../../common/mock/connectors'; export const getCase = async ( caseId: string, @@ -140,3 +141,8 @@ export const getFeatureIds = async ( _query: { registrationContext: string[] }, _signal: AbortSignal ): Promise => Promise.resolve(['siem', 'observability']); + +export const getCaseConnectors = async ( + caseId: string, + signal: AbortSignal +): Promise => Promise.resolve(getCaseConnectorsMockResponse()); diff --git a/x-pack/plugins/cases/public/containers/api.test.tsx b/x-pack/plugins/cases/public/containers/api.test.tsx index c744fc36335cc..2f0c8645b3317 100644 --- a/x-pack/plugins/cases/public/containers/api.test.tsx +++ b/x-pack/plugins/cases/public/containers/api.test.tsx @@ -11,6 +11,7 @@ import { KibanaServices } from '../common/lib/kibana'; import { ConnectorTypes, CommentType, CaseStatuses, CaseSeverity } from '../../common/api'; import { + CASES_INTERNAL_URL, CASES_URL, INTERNAL_BULK_CREATE_ATTACHMENTS_URL, SECURITY_SOLUTION_OWNER, @@ -34,6 +35,7 @@ import { resolveCase, getFeatureIds, postComment, + getCaseConnectors, } from './api'; import { @@ -54,10 +56,13 @@ import { caseWithRegisteredAttachmentsSnake, caseWithRegisteredAttachments, caseUserActionsWithRegisteredAttachmentsSnake, + basicPushSnake, } from './mock'; import { DEFAULT_FILTER_OPTIONS, DEFAULT_QUERY_PARAMS } from './use_get_cases'; import { getCasesStatus } from '../api'; +import { getCaseConnectorsMockResponse } from '../common/mock/connectors'; +import { cloneDeep, set } from 'lodash'; const abortCtrl = new AbortController(); const mockKibanaServices = KibanaServices.get as jest.Mock; @@ -846,4 +851,31 @@ describe('Cases API', () => { expect(resp).toEqual(caseWithRegisteredAttachments); }); }); + + describe('getCaseConnectors', () => { + const caseConnectors = getCaseConnectorsMockResponse(); + const connectorCamelCase = caseConnectors['servicenow-1']; + + const snakeCaseConnector = cloneDeep(connectorCamelCase); + set(snakeCaseConnector, 'push.details.externalService', basicPushSnake); + + beforeEach(() => { + fetchMock.mockClear(); + fetchMock.mockResolvedValue({ 'servicenow-1': snakeCaseConnector }); + }); + + it('should be called with correct check url, method, signal', async () => { + await getCaseConnectors(basicCase.id, abortCtrl.signal); + + expect(fetchMock).toHaveBeenCalledWith(`${CASES_INTERNAL_URL}/${basicCase.id}/_connectors`, { + method: 'GET', + signal: abortCtrl.signal, + }); + }); + + it('should return correct response', async () => { + const resp = await getCaseConnectors(basicCase.id, abortCtrl.signal); + expect(resp).toEqual({ 'servicenow-1': connectorCamelCase }); + }); + }); }); diff --git a/x-pack/plugins/cases/public/containers/api.ts b/x-pack/plugins/cases/public/containers/api.ts index 9c4f1a07ee62f..aabe48638efa7 100644 --- a/x-pack/plugins/cases/public/containers/api.ts +++ b/x-pack/plugins/cases/public/containers/api.ts @@ -8,6 +8,7 @@ import type { ValidFeatureId } from '@kbn/rule-data-utils'; import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common/constants'; import type { + CaseConnectors, Cases, CaseUpdateRequest, FetchCasesProps, @@ -27,6 +28,7 @@ import type { User, SingleCaseMetricsResponse, CasesFindResponse, + GetCaseConnectorsResponse, } from '../../common/api'; import { CommentType, @@ -36,6 +38,7 @@ import { getCasePushUrl, getCaseFindUserActionsUrl, getCaseCommentDeleteUrl, + getCaseConnectorsUrl, } from '../../common/api'; import { CASE_REPORTERS_URL, @@ -378,3 +381,28 @@ export const getFeatureIds = async ( } ); }; + +export const getCaseConnectors = async ( + caseId: string, + signal: AbortSignal +): Promise => { + const res = await KibanaServices.get().http.fetch( + getCaseConnectorsUrl(caseId), + { + method: 'GET', + signal, + } + ); + + return Object.keys(res).reduce( + (acc, connectorId) => ({ + ...acc, + [connectorId]: { + ...convertToCamelCase( + res[connectorId] + ), + }, + }), + {} + ); +}; diff --git a/x-pack/plugins/cases/public/containers/configure/__mocks__/api.ts b/x-pack/plugins/cases/public/containers/configure/__mocks__/api.ts index 0d85e75478be3..607f6d01191ff 100644 --- a/x-pack/plugins/cases/public/containers/configure/__mocks__/api.ts +++ b/x-pack/plugins/cases/public/containers/configure/__mocks__/api.ts @@ -17,8 +17,9 @@ import type { CaseConfigure } from '../types'; import { caseConfigurationCamelCaseResponseMock } from '../mock'; import { actionTypesMock, connectorsMock } from '../../../common/mock/connectors'; -export const fetchConnectors = async ({ signal }: ApiProps): Promise => - Promise.resolve(connectorsMock); +export const getSupportedActionConnectors = async ({ + signal, +}: ApiProps): Promise => Promise.resolve(connectorsMock); export const getCaseConfigure = async ({ signal }: ApiProps): Promise => Promise.resolve(caseConfigurationCamelCaseResponseMock); diff --git a/x-pack/plugins/cases/public/containers/configure/api.test.ts b/x-pack/plugins/cases/public/containers/configure/api.test.ts index 0b1f0c8d172ea..9099f908a7871 100644 --- a/x-pack/plugins/cases/public/containers/configure/api.test.ts +++ b/x-pack/plugins/cases/public/containers/configure/api.test.ts @@ -6,7 +6,7 @@ */ import { - fetchConnectors, + getSupportedActionConnectors, getCaseConfigure, postCaseConfigure, patchCaseConfigure, @@ -37,7 +37,7 @@ describe('Case Configuration API', () => { }); test('check url, method, signal', async () => { - await fetchConnectors({ signal: abortCtrl.signal }); + await getSupportedActionConnectors({ signal: abortCtrl.signal }); expect(fetchMock).toHaveBeenCalledWith('/api/cases/configure/connectors/_find', { method: 'GET', signal: abortCtrl.signal, @@ -45,7 +45,7 @@ describe('Case Configuration API', () => { }); test('happy path', async () => { - const resp = await fetchConnectors({ signal: abortCtrl.signal }); + const resp = await getSupportedActionConnectors({ signal: abortCtrl.signal }); expect(resp).toEqual(connectorsMock); }); }); diff --git a/x-pack/plugins/cases/public/containers/configure/api.ts b/x-pack/plugins/cases/public/containers/configure/api.ts index 72702e27fbb56..b0f2b8df4a093 100644 --- a/x-pack/plugins/cases/public/containers/configure/api.ts +++ b/x-pack/plugins/cases/public/containers/configure/api.ts @@ -24,7 +24,9 @@ import type { ApiProps } from '../types'; import { decodeCaseConfigurationsResponse, decodeCaseConfigureResponse } from '../utils'; import type { CaseConfigure } from './types'; -export const fetchConnectors = async ({ signal }: ApiProps): Promise => { +export const getSupportedActionConnectors = async ({ + signal, +}: ApiProps): Promise => { const response = await KibanaServices.get().http.fetch( `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, { method: 'GET', signal } diff --git a/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx similarity index 85% rename from x-pack/plugins/cases/public/containers/configure/use_connectors.tsx rename to x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx index f9c19ee1776bd..0bba69ca47df1 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx @@ -6,13 +6,13 @@ */ import { useQuery } from '@tanstack/react-query'; -import { fetchConnectors } from './api'; +import { getSupportedActionConnectors } from './api'; import { useApplicationCapabilities, useToasts } from '../../common/lib/kibana'; import * as i18n from './translations'; import { casesQueriesKeys } from '../constants'; import type { ServerError } from '../../types'; -export function useGetConnectors() { +export function useGetSupportedActionConnectors() { const toasts = useToasts(); const { actions } = useApplicationCapabilities(); return useQuery( @@ -22,7 +22,7 @@ export function useGetConnectors() { return []; } const abortCtrl = new AbortController(); - return fetchConnectors({ signal: abortCtrl.signal }); + return getSupportedActionConnectors({ signal: abortCtrl.signal }); }, { onError: (error: ServerError) => { diff --git a/x-pack/plugins/cases/public/containers/configure/use_connectors.test.tsx b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx similarity index 78% rename from x-pack/plugins/cases/public/containers/configure/use_connectors.test.tsx rename to x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx index 076e1a8408482..36cbd9417e375 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_connectors.test.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_get_supported_action_connectors.tsx.test.tsx @@ -10,7 +10,7 @@ import { renderHook } from '@testing-library/react-hooks'; import * as api from './api'; import { TestProviders } from '../../common/mock'; import { useApplicationCapabilities, useToasts } from '../../common/lib/kibana'; -import { useGetConnectors } from './use_connectors'; +import { useGetSupportedActionConnectors } from './use_get_supported_action_connectors'; const useApplicationCapabilitiesMock = useApplicationCapabilities as jest.Mocked< typeof useApplicationCapabilities @@ -25,8 +25,8 @@ describe('useConnectors', () => { }); it('fetches connectors', async () => { - const spy = jest.spyOn(api, 'fetchConnectors'); - const { waitForNextUpdate } = renderHook(() => useGetConnectors(), { + const spy = jest.spyOn(api, 'getSupportedActionConnectors'); + const { waitForNextUpdate } = renderHook(() => useGetSupportedActionConnectors(), { wrapper: ({ children }) => {children}, }); @@ -39,12 +39,12 @@ describe('useConnectors', () => { const addError = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addError }); - const spyOnfetchConnectors = jest.spyOn(api, 'fetchConnectors'); + const spyOnfetchConnectors = jest.spyOn(api, 'getSupportedActionConnectors'); spyOnfetchConnectors.mockImplementation(() => { throw new Error('Something went wrong'); }); - const { waitForNextUpdate } = renderHook(() => useGetConnectors(), { + const { waitForNextUpdate } = renderHook(() => useGetSupportedActionConnectors(), { wrapper: ({ children }) => {children}, }); await waitForNextUpdate(); @@ -53,10 +53,10 @@ describe('useConnectors', () => { }); it('does not fetch connectors when the user does not has access to actions', async () => { - const spyOnFetchConnectors = jest.spyOn(api, 'fetchConnectors'); + const spyOnFetchConnectors = jest.spyOn(api, 'getSupportedActionConnectors'); useApplicationCapabilitiesMock().actions = { crud: false, read: false }; - const { result, waitForNextUpdate } = renderHook(() => useGetConnectors(), { + const { result, waitForNextUpdate } = renderHook(() => useGetSupportedActionConnectors(), { wrapper: ({ children }) => {children}, }); diff --git a/x-pack/plugins/cases/public/containers/constants.ts b/x-pack/plugins/cases/public/containers/constants.ts index 2e57a17be08ec..10d890629311e 100644 --- a/x-pack/plugins/cases/public/containers/constants.ts +++ b/x-pack/plugins/cases/public/containers/constants.ts @@ -24,8 +24,8 @@ export const casesQueriesKeys = { case: (id: string) => [...casesQueriesKeys.caseView(), id] as const, caseMetrics: (id: string, features: SingleCaseMetricsFeature[]) => [...casesQueriesKeys.case(id), 'metrics', features] as const, - userActions: (id: string, connectorId: string) => - [...casesQueriesKeys.case(id), 'user-actions', connectorId] as const, + caseConnectors: (id: string) => [...casesQueriesKeys.case(id), 'connectors'], + userActions: (id: string) => [...casesQueriesKeys.case(id), 'user-actions'] as const, userProfiles: () => [...casesQueriesKeys.users, 'user-profiles'] as const, userProfilesList: (ids: string[]) => [...casesQueriesKeys.userProfiles(), ids] as const, currentUser: () => [...casesQueriesKeys.users, 'current-user'] as const, diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index 5cadec4818457..4abe077f378db 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -53,12 +53,14 @@ export { connectorsMock } from '../common/mock/connectors'; export const basicCaseId = 'basic-case-id'; export const caseWithAlertsId = 'case-with-alerts-id'; export const caseWithAlertsSyncOffId = 'case-with-alerts-syncoff-id'; +export const pushConnectorId = 'servicenow-1'; const basicCommentId = 'basic-comment-id'; const basicCreatedAt = '2020-02-19T23:06:33.798Z'; const basicUpdatedAt = '2020-02-20T15:02:57.995Z'; const basicClosedAt = '2020-02-21T15:02:57.995Z'; -const laterTime = '2020-02-28T15:02:57.995Z'; +const basicPushedAt = '2023-01-17T09:46:29.813Z'; +const laterTime = '2023-01-18T09:46:29.813Z'; export const elasticUser = { fullName: 'Leslie Knope', @@ -370,21 +372,21 @@ export const casesMetrics: CasesMetrics = { }; export const basicPush = { - connectorId: '123', - connectorName: 'connector name', + connectorId: pushConnectorId, + connectorName: 'My SN connector', externalId: 'external_id', externalTitle: 'external title', externalUrl: 'basicPush.com', - pushedAt: basicUpdatedAt, + pushedAt: basicPushedAt, pushedBy: elasticUser, }; export const pushedCase: Case = { ...basicCase, connector: { - id: '123', - name: 'My Connector', - type: ConnectorTypes.jira, + id: pushConnectorId, + name: 'My SN connector', + type: ConnectorTypes.serviceNowITSM, fields: null, }, externalService: basicPush, @@ -539,10 +541,9 @@ export const casesStatusSnake: CasesStatusResponse = { count_open_cases: 20, }; -export const pushConnectorId = '123'; export const pushSnake = { connector_id: pushConnectorId, - connector_name: 'connector name', + connector_name: 'My SN connector', external_id: 'external_id', external_title: 'external title', external_url: 'basicPush.com', @@ -550,16 +551,16 @@ export const pushSnake = { export const basicPushSnake = { ...pushSnake, - pushed_at: basicUpdatedAt, + pushed_at: basicPushedAt, pushed_by: elasticUserSnake, }; export const pushedCaseSnake = { ...basicCaseSnake, connector: { - id: '123', - name: 'My Connector', - type: ConnectorTypes.jira, + id: pushConnectorId, + name: 'My SN connector', + type: ConnectorTypes.serviceNowITSM, fields: null, }, external_service: { ...basicPushSnake, connector_id: pushConnectorId }, @@ -605,11 +606,11 @@ export const getUserAction = ( const externalService = { connectorId: pushConnectorId, - connectorName: 'connector name', + connectorName: 'My SN connector', externalId: 'external_id', externalTitle: 'external title', externalUrl: 'basicPush.com', - pushedAt: basicUpdatedAt, + pushedAt: basicPushedAt, pushedBy: elasticUser, }; @@ -667,6 +668,7 @@ export const getUserAction = ( case ActionTypes.pushed: return { ...commonProperties, + createdAt: basicPushedAt, type: ActionTypes.pushed, payload: { externalService, diff --git a/x-pack/plugins/cases/public/containers/use_find_case_user_actions.test.tsx b/x-pack/plugins/cases/public/containers/use_find_case_user_actions.test.tsx index 5b98b71468c6f..4330323d7a8b2 100644 --- a/x-pack/plugins/cases/public/containers/use_find_case_user_actions.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_find_case_user_actions.test.tsx @@ -5,24 +5,20 @@ * 2.0. */ -import { renderHook, act } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; import type { UseFindCaseUserActions } from './use_find_case_user_actions'; -import { getPushedInfo, useFindCaseUserActions } from './use_find_case_user_actions'; +import { useFindCaseUserActions } from './use_find_case_user_actions'; import { basicCase, - basicPush, caseUserActions, elasticUser, - getJiraConnector, - getUserAction, - jiraFields, findCaseUserActionsResponse, + getUserAction, } from './mock'; import { Actions } from '../../common/api'; import React from 'react'; import { QueryClientProvider } from '@tanstack/react-query'; import { testQueryClient } from '../common/mock'; -import { waitFor } from '@testing-library/dom'; import * as api from './api'; import { useToasts } from '../common/lib/kibana'; @@ -39,36 +35,33 @@ const wrapper: React.FC = ({ children }) => ( {children} ); -describe('useFindCaseUserActions', () => { +describe('UseFindCaseUserActions', () => { beforeEach(() => { jest.clearAllMocks(); jest.restoreAllMocks(); }); it('returns proper state on findCaseUserActions', async () => { - await act(async () => { - const { result } = renderHook( - () => useFindCaseUserActions(basicCase.id, basicCase.connector.id), - { wrapper } - ); - await waitFor(() => { - expect(result.current).toEqual( - expect.objectContaining({ - ...initialData, - data: { - caseServices: {}, - caseUserActions: [...findCaseUserActionsResponse.userActions], - hasDataToPush: true, - participants: [elasticUser], - profileUids: new Set(), - }, - isError: false, - isLoading: false, - isFetching: false, - }) - ); - }); - }); + const { result, waitForNextUpdate } = renderHook( + () => useFindCaseUserActions(basicCase.id), + { wrapper } + ); + + await waitForNextUpdate(); + + expect(result.current).toEqual( + expect.objectContaining({ + ...initialData, + data: { + caseUserActions: [...findCaseUserActionsResponse.userActions], + participants: [elasticUser], + profileUids: new Set(), + }, + isError: false, + isLoading: false, + isFetching: false, + }) + ); }); it('shows a toast error when the API returns an error', async () => { @@ -78,10 +71,12 @@ describe('useFindCaseUserActions', () => { (useToasts as jest.Mock).mockReturnValue({ addError }); const { waitForNextUpdate } = renderHook( - () => useFindCaseUserActions(basicCase.id, basicCase.connector.id), + () => useFindCaseUserActions(basicCase.id), { wrapper } ); + await waitForNextUpdate(); + expect(spy).toHaveBeenCalledWith(basicCase.id, expect.any(AbortSignal)); expect(addError).toHaveBeenCalled(); }); @@ -97,20 +92,18 @@ describe('useFindCaseUserActions', () => { }) ); - await act(async () => { - const { result } = renderHook( - () => useFindCaseUserActions(basicCase.id, basicCase.connector.id), - { wrapper } - ); + const { result, waitForNextUpdate } = renderHook( + () => useFindCaseUserActions(basicCase.id), + { wrapper } + ); + + await waitForNextUpdate(); - await waitFor(() => { - expect(result.current.data?.profileUids).toMatchInlineSnapshot(` + expect(result.current.data?.profileUids).toMatchInlineSnapshot(` Set { "456", } `); - }); - }); }); it('aggregates the uids from a push', async () => { @@ -127,20 +120,18 @@ describe('useFindCaseUserActions', () => { }) ); - await act(async () => { - const { result } = renderHook( - () => useFindCaseUserActions(basicCase.id, basicCase.connector.id), - { wrapper } - ); + const { result, waitForNextUpdate } = renderHook( + () => useFindCaseUserActions(basicCase.id), + { wrapper } + ); + + await waitForNextUpdate(); - await waitFor(() => { - expect(result.current.data?.profileUids).toMatchInlineSnapshot(` + expect(result.current.data?.profileUids).toMatchInlineSnapshot(` Set { "123", } `); - }); - }); }); it('aggregates the uids from an assignment add user action', async () => { @@ -153,21 +144,19 @@ describe('useFindCaseUserActions', () => { }) ); - await act(async () => { - const { result } = renderHook( - () => useFindCaseUserActions(basicCase.id, basicCase.connector.id), - { wrapper } - ); + const { result, waitForNextUpdate } = renderHook( + () => useFindCaseUserActions(basicCase.id), + { wrapper } + ); + + await waitForNextUpdate(); - await waitFor(() => { - expect(result.current.data?.profileUids).toMatchInlineSnapshot(` + expect(result.current.data?.profileUids).toMatchInlineSnapshot(` Set { "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0", "u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0", } `); - }); - }); }); it('ignores duplicate uids', async () => { @@ -184,21 +173,19 @@ describe('useFindCaseUserActions', () => { }) ); - await act(async () => { - const { result } = renderHook( - () => useFindCaseUserActions(basicCase.id, basicCase.connector.id), - { wrapper } - ); + const { result, waitForNextUpdate } = renderHook( + () => useFindCaseUserActions(basicCase.id), + { wrapper } + ); + + await waitForNextUpdate(); - await waitFor(() => { - expect(result.current.data?.profileUids).toMatchInlineSnapshot(` + expect(result.current.data?.profileUids).toMatchInlineSnapshot(` Set { "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0", "u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0", } `); - }); - }); }); it('aggregates the uids from an assignment delete user action', async () => { @@ -211,562 +198,19 @@ describe('useFindCaseUserActions', () => { }) ); - await act(async () => { - const { result } = renderHook( - () => useFindCaseUserActions(basicCase.id, basicCase.connector.id), - { wrapper } - ); + const { result, waitForNextUpdate } = renderHook( + () => useFindCaseUserActions(basicCase.id), + { wrapper } + ); + + await waitForNextUpdate(); - await waitFor(() => { - expect(result.current.data?.profileUids).toMatchInlineSnapshot(` + expect(result.current.data?.profileUids).toMatchInlineSnapshot(` Set { "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0", "u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0", } `); - }); - }); - }); - }); - - describe('getPushedInfo', () => { - it('Correctly marks first/last index - hasDataToPush: false', () => { - const userActions = [...caseUserActions, getUserAction('pushed', Actions.push_to_service)]; - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: false, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 3, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); - }); - - it('Correctly marks first/last index and comment id - hasDataToPush: true', () => { - const userActions = [ - ...caseUserActions, - getUserAction('pushed', Actions.push_to_service), - getUserAction('comment', Actions.create), - ]; - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: true, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 3, - commentsToUpdate: [userActions[userActions.length - 1].commentId], - hasDataToPush: true, - }, - }, - }); - }); - - it('Correctly marks first/last index and multiple comment ids, both needs push', () => { - const userActions = [ - ...caseUserActions, - getUserAction('pushed', Actions.push_to_service), - getUserAction('comment', Actions.create), - { ...getUserAction('comment', Actions.create), commentId: 'muahaha' }, - ]; - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: true, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 3, - commentsToUpdate: [ - userActions[userActions.length - 2].commentId, - userActions[userActions.length - 1].commentId, - ], - hasDataToPush: true, - }, - }, - }); - }); - - it('Correctly marks first/last index and multiple comment ids, one needs push', () => { - const userActions = [ - ...caseUserActions, - getUserAction('pushed', Actions.push_to_service), - getUserAction('comment', Actions.create), - getUserAction('pushed', Actions.push_to_service), - { ...getUserAction('comment', Actions.create), commentId: 'muahaha' }, - ]; - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: true, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 5, - commentsToUpdate: [userActions[userActions.length - 1].commentId], - hasDataToPush: true, - }, - }, - }); - }); - - it('Correctly marks first/last index and multiple comment ids, one needs push and one needs update', () => { - const userActions = [ - ...caseUserActions, - getUserAction('pushed', Actions.push_to_service), - getUserAction('comment', Actions.create), - getUserAction('pushed', Actions.push_to_service), - { ...getUserAction('comment', Actions.create), commentId: 'muahaha' }, - getUserAction('comment', Actions.update), - getUserAction('comment', Actions.update), - ]; - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: true, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 5, - commentsToUpdate: [ - userActions[userActions.length - 3].commentId, - userActions[userActions.length - 1].commentId, - ], - hasDataToPush: true, - }, - }, - }); - }); - - it('Does not count connector update as a reason to push', () => { - const userActions = [ - ...caseUserActions, - getUserAction('pushed', Actions.push_to_service), - getUserAction('connector', Actions.update), - ]; - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: false, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 3, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); - }); - - it('Correctly handles multiple push actions', () => { - const userActions = [ - ...caseUserActions, - getUserAction('pushed', Actions.push_to_service), - getUserAction('comment', Actions.create), - getUserAction('pushed', Actions.push_to_service), - ]; - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: false, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 5, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); - }); - - it('Correctly handles comment update with multiple push actions', () => { - const userActions = [ - ...caseUserActions, - getUserAction('pushed', Actions.push_to_service), - getUserAction('comment', Actions.create), - getUserAction('pushed', Actions.push_to_service), - getUserAction('comment', Actions.update), - ]; - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: true, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 5, - commentsToUpdate: [userActions[userActions.length - 1].commentId], - hasDataToPush: true, - }, - }, - }); - }); - - it('Multiple connector tracking - hasDataToPush: true', () => { - const pushAction123 = getUserAction('pushed', Actions.push_to_service); - const push456 = { - ...basicPush, - connectorId: '456', - connectorName: 'other connector name', - externalId: 'other_external_id', - }; - - const pushAction456 = getUserAction('pushed', Actions.push_to_service, { - payload: { externalService: push456 }, - }); - - const userActions = [ - ...caseUserActions, - pushAction123, - getUserAction('comment', Actions.create), - pushAction456, - ]; - - const result = getPushedInfo(userActions, '123'); - - expect(result).toEqual({ - hasDataToPush: true, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 3, - commentsToUpdate: [userActions[userActions.length - 2].commentId], - hasDataToPush: true, - }, - '456': { - ...basicPush, - connectorId: '456', - connectorName: 'other connector name', - externalId: 'other_external_id', - firstPushIndex: 5, - lastPushIndex: 5, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); - }); - - it('Multiple connector tracking - hasDataToPush: false', () => { - const pushAction123 = getUserAction('pushed', Actions.push_to_service); - const push456 = { - ...basicPush, - connectorId: '456', - connectorName: 'other connector name', - externalId: 'other_external_id', - }; - - const pushAction456 = getUserAction('pushed', Actions.push_to_service, { - payload: { externalService: push456 }, - }); - - const userActions = [ - ...caseUserActions, - pushAction123, - getUserAction('comment', Actions.create), - pushAction456, - ]; - - const result = getPushedInfo(userActions, '456'); - expect(result).toEqual({ - hasDataToPush: false, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 3, - commentsToUpdate: [userActions[userActions.length - 2].commentId], - hasDataToPush: true, - }, - '456': { - ...basicPush, - connectorId: '456', - connectorName: 'other connector name', - externalId: 'other_external_id', - firstPushIndex: 5, - lastPushIndex: 5, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); - }); - - it('Change fields of current connector - hasDataToPush: true', () => { - const userActions = [ - ...caseUserActions, - createUpdate123HighPriorityConnector(), - getUserAction('pushed', Actions.push_to_service), - createUpdate123LowPriorityConnector(), - ]; - - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: true, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 4, - lastPushIndex: 4, - commentsToUpdate: [], - hasDataToPush: true, - }, - }, - }); - }); - - it('Change current connector - hasDataToPush: true', () => { - const userActions = [ - ...caseUserActions, - getUserAction('pushed', Actions.push_to_service), - createUpdate456HighPriorityConnector(), - ]; - - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: false, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 3, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); - }); - - it('Change connector and back - hasDataToPush: true', () => { - const userActions = [ - ...caseUserActions, - getUserAction('pushed', Actions.push_to_service), - createUpdate456HighPriorityConnector(), - createUpdate123HighPriorityConnector(), - ]; - - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: false, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 3, - lastPushIndex: 3, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); - }); - - it('Change fields and connector after push - hasDataToPush: true', () => { - const userActions = [ - ...caseUserActions, - createUpdate123HighPriorityConnector(), - getUserAction('pushed', Actions.push_to_service), - createUpdate456HighPriorityConnector(), - createUpdate123LowPriorityConnector(), - ]; - - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: true, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 4, - lastPushIndex: 4, - commentsToUpdate: [], - hasDataToPush: true, - }, - }, - }); - }); - - it('Change only connector after push - hasDataToPush: false', () => { - const userActions = [ - ...caseUserActions, - createUpdate123HighPriorityConnector(), - getUserAction('pushed', Actions.push_to_service), - createUpdate456HighPriorityConnector(), - createUpdate123HighPriorityConnector(), - ]; - - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: false, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 4, - lastPushIndex: 4, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); - }); - - it('Change connectors and fields - multiple pushes', () => { - const pushAction123 = getUserAction('pushed', Actions.push_to_service); - const push456 = { - ...basicPush, - connectorId: '456', - connectorName: 'other connector name', - externalId: 'other_external_id', - }; - - const pushAction456 = getUserAction('pushed', Actions.push_to_service, { - payload: { externalService: push456 }, - }); - - const userActions = [ - ...caseUserActions, - createUpdate123HighPriorityConnector(), - pushAction123, - createUpdate456HighPriorityConnector(), - pushAction456, - createUpdate123LowPriorityConnector(), - createUpdate456HighPriorityConnector(), - createUpdate123LowPriorityConnector(), - ]; - - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: true, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 4, - lastPushIndex: 4, - commentsToUpdate: [], - hasDataToPush: true, - }, - '456': { - ...basicPush, - connectorId: '456', - connectorName: 'other connector name', - externalId: 'other_external_id', - firstPushIndex: 6, - lastPushIndex: 6, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); - }); - - it('pushing other connectors does not count as an update', () => { - const pushAction123 = getUserAction('pushed', Actions.push_to_service); - const push456 = { - ...basicPush, - connectorId: '456', - connectorName: 'other connector name', - externalId: 'other_external_id', - }; - - const pushAction456 = getUserAction('pushed', Actions.push_to_service, { - payload: { externalService: push456 }, - }); - - const userActions = [ - ...caseUserActions, - createUpdate123HighPriorityConnector(), - pushAction123, - createUpdate456HighPriorityConnector(), - pushAction456, - createUpdate123HighPriorityConnector(), - ]; - - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: false, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 4, - lastPushIndex: 4, - commentsToUpdate: [], - hasDataToPush: false, - }, - '456': { - ...basicPush, - connectorId: '456', - connectorName: 'other connector name', - externalId: 'other_external_id', - firstPushIndex: 6, - lastPushIndex: 6, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); - }); - - it('Changing other connectors fields does not count as an update', () => { - const userActions = [ - ...caseUserActions, - createUpdate123HighPriorityConnector(), - getUserAction('pushed', Actions.push_to_service), - createUpdate456HighPriorityConnector(), - createUpdate456HighPriorityConnector(), - ]; - - const result = getPushedInfo(userActions, '123'); - expect(result).toEqual({ - hasDataToPush: false, - caseServices: { - '123': { - ...basicPush, - firstPushIndex: 4, - lastPushIndex: 4, - commentsToUpdate: [], - hasDataToPush: false, - }, - }, - }); }); }); }); - -const jira123HighPriorityFields = { - fields: { ...jiraFields.fields, priority: 'High' }, -}; - -const jira123LowPriorityFields = { - fields: { ...jiraFields.fields, priority: 'Low' }, -}; - -const jira456Fields = { - fields: { issueType: '10', parent: null, priority: null }, -}; - -const jira456HighPriorityFields = { - id: '456', - fields: { ...jira456Fields.fields, priority: 'High' }, -}; - -const createUpdate123HighPriorityConnector = () => - getUserAction('connector', Actions.update, { - payload: { connector: getJiraConnector(jira123HighPriorityFields) }, - }); - -const createUpdate123LowPriorityConnector = () => - getUserAction('connector', Actions.update, { - payload: { connector: getJiraConnector(jira123LowPriorityFields) }, - }); - -const createUpdate456HighPriorityConnector = () => - getUserAction('connector', Actions.update, { - payload: { connector: getJiraConnector(jira456HighPriorityFields) }, - }); diff --git a/x-pack/plugins/cases/public/containers/use_find_case_user_actions.tsx b/x-pack/plugins/cases/public/containers/use_find_case_user_actions.tsx index 46236ed36be3d..8470ea439d6fa 100644 --- a/x-pack/plugins/cases/public/containers/use_find_case_user_actions.tsx +++ b/x-pack/plugins/cases/public/containers/use_find_case_user_actions.tsx @@ -6,209 +6,17 @@ */ import { isEmpty, uniqBy } from 'lodash/fp'; -import deepEqual from 'fast-deep-equal'; import { useQuery } from '@tanstack/react-query'; -import type { CaseUserActions, CaseExternalService } from '../../common/ui/types'; -import type { CaseConnector } from '../../common/api'; -import { ActionTypes, NONE_CONNECTOR_ID } from '../../common/api'; +import type { CaseUserActions } from '../../common/ui/types'; +import { ActionTypes } from '../../common/api'; import { findCaseUserActions } from './api'; -import { - isPushedUserAction, - isConnectorUserAction, - isCreateCaseUserAction, -} from '../../common/utils/user_actions'; +import { isPushedUserAction } from '../../common/utils/user_actions'; import type { ServerError } from '../types'; import { useToasts } from '../common/lib/kibana'; import { ERROR_TITLE } from './translations'; import { casesQueriesKeys } from './constants'; -export interface CaseService extends CaseExternalService { - firstPushIndex: number; - lastPushIndex: number; - commentsToUpdate: string[]; - hasDataToPush: boolean; -} - -export interface CaseServices { - [key: string]: CaseService; -} - -const groupConnectorFields = ( - userActions: CaseUserActions[] -): Record> => - userActions.reduce((acc, mua) => { - if ( - (isConnectorUserAction(mua) || isCreateCaseUserAction(mua)) && - mua.payload?.connector?.id !== NONE_CONNECTOR_ID - ) { - const connector = mua.payload.connector; - - return { - ...acc, - [connector.id]: [...(acc[connector.id] || []), connector.fields], - }; - } - - return acc; - }, {} as Record>); - -const connectorHasChangedFields = ({ - connectorFieldsBeforePush, - connectorFieldsAfterPush, - connectorId, -}: { - connectorFieldsBeforePush: Record> | null; - connectorFieldsAfterPush: Record> | null; - connectorId: string; -}): boolean => { - if (connectorFieldsAfterPush == null || connectorFieldsAfterPush[connectorId] == null) { - return false; - } - - const fieldsAfterPush = connectorFieldsAfterPush[connectorId]; - - if (connectorFieldsBeforePush != null && connectorFieldsBeforePush[connectorId] != null) { - const fieldsBeforePush = connectorFieldsBeforePush[connectorId]; - return !deepEqual( - fieldsBeforePush[fieldsBeforePush.length - 1], - fieldsAfterPush[fieldsAfterPush.length - 1] - ); - } - - if (fieldsAfterPush.length >= 2) { - return !deepEqual( - fieldsAfterPush[fieldsAfterPush.length - 2], - fieldsAfterPush[fieldsAfterPush.length - 1] - ); - } - - return false; -}; - -interface CommentsAndIndex { - commentId: string; - commentIndex: number; -} - -export const getPushedInfo = ( - caseUserActions: CaseUserActions[], - caseConnectorId: string -): { - caseServices: CaseServices; - hasDataToPush: boolean; -} => { - const hasDataToPushForConnector = (connectorId: string): boolean => { - const caseUserActionsReversed = [...caseUserActions].reverse(); - const lastPushOfConnectorReversedIndex = caseUserActionsReversed.findIndex( - (mua) => - isPushedUserAction<'camelCase'>(mua) && - mua.payload.externalService.connectorId === connectorId - ); - - if (lastPushOfConnectorReversedIndex === -1) { - return true; - } - - const lastPushOfConnectorIndex = - caseUserActionsReversed.length - lastPushOfConnectorReversedIndex - 1; - - const actionsBeforePush = caseUserActions.slice(0, lastPushOfConnectorIndex); - const actionsAfterPush = caseUserActions.slice( - lastPushOfConnectorIndex + 1, - caseUserActionsReversed.length - ); - - const connectorFieldsBeforePush = groupConnectorFields(actionsBeforePush); - const connectorFieldsAfterPush = groupConnectorFields(actionsAfterPush); - - const connectorHasChanged = connectorHasChangedFields({ - connectorFieldsBeforePush, - connectorFieldsAfterPush, - connectorId, - }); - - return ( - actionsAfterPush.some( - (mua) => mua.type !== ActionTypes.connector && mua.type !== ActionTypes.pushed - ) || connectorHasChanged - ); - }; - - const commentsAndIndex = caseUserActions.reduce( - (bacc, mua, index) => - mua.type === ActionTypes.comment && mua.commentId != null - ? [ - ...bacc, - { - commentId: mua.commentId, - commentIndex: index, - }, - ] - : bacc, - [] - ); - - let caseServices = caseUserActions.reduce((acc, cua, i) => { - if (!isPushedUserAction<'camelCase'>(cua)) { - return acc; - } - - const externalService = cua.payload.externalService; - if (externalService === null) { - return acc; - } - - return { - ...acc, - ...(acc[externalService.connectorId] != null - ? { - [externalService.connectorId]: { - ...acc[externalService.connectorId], - ...externalService, - lastPushIndex: i, - commentsToUpdate: [], - }, - } - : { - [externalService.connectorId]: { - ...externalService, - firstPushIndex: i, - lastPushIndex: i, - hasDataToPush: hasDataToPushForConnector(externalService.connectorId), - commentsToUpdate: [], - }, - }), - }; - }, {}); - - caseServices = Object.keys(caseServices).reduce((acc, key) => { - return { - ...acc, - [key]: { - ...caseServices[key], - // if the comment happens after the lastUpdateToCaseIndex, it should be included in commentsToUpdate - commentsToUpdate: commentsAndIndex.reduce( - (bacc, currentComment) => - currentComment.commentIndex > caseServices[key].lastPushIndex - ? bacc.indexOf(currentComment.commentId) > -1 - ? [...bacc.filter((e) => e !== currentComment.commentId), currentComment.commentId] - : [...bacc, currentComment.commentId] - : bacc, - [] - ), - }, - }; - }, {}); - - const hasDataToPush = - caseServices[caseConnectorId] != null ? caseServices[caseConnectorId].hasDataToPush : true; - return { - hasDataToPush, - caseServices, - }; -}; - export const getProfileUids = (userActions: CaseUserActions[]) => { const uids = userActions.reduce>((acc, userAction) => { if (userAction.type === ActionTypes.assignees) { @@ -235,29 +43,25 @@ export const getProfileUids = (userActions: CaseUserActions[]) => { return uids; }; -export const useFindCaseUserActions = (caseId: string, caseConnectorId: string) => { +export const useFindCaseUserActions = (caseId: string) => { const toasts = useToasts(); const abortCtrlRef = new AbortController(); return useQuery( - casesQueriesKeys.userActions(caseId, caseConnectorId), + casesQueriesKeys.userActions(caseId), async () => { const response = await findCaseUserActions(caseId, abortCtrlRef.signal); const participants = !isEmpty(response.userActions) ? uniqBy('createdBy.username', response.userActions).map((cau) => cau.createdBy) : []; - const caseUserActions: CaseUserActions[] = !isEmpty(response.userActions) - ? response.userActions - : []; - const pushedInfo = getPushedInfo(caseUserActions, caseConnectorId); + const caseUserActions = !isEmpty(response.userActions) ? response.userActions : []; const profileUids = getProfileUids(caseUserActions); return { caseUserActions, participants, profileUids, - ...pushedInfo, }; }, { diff --git a/x-pack/plugins/cases/public/containers/use_get_case_connectors.test.tsx b/x-pack/plugins/cases/public/containers/use_get_case_connectors.test.tsx new file mode 100644 index 0000000000000..220d37deefb5b --- /dev/null +++ b/x-pack/plugins/cases/public/containers/use_get_case_connectors.test.tsx @@ -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 { renderHook } from '@testing-library/react-hooks'; +import * as api from './api'; +import type { AppMockRenderer } from '../common/mock'; +import { createAppMockRenderer } from '../common/mock'; +import { useToasts } from '../common/lib/kibana'; +import { useGetCaseConnectors } from './use_get_case_connectors'; + +jest.mock('./api'); +jest.mock('../common/lib/kibana'); + +describe('useGetCaseConnectors', () => { + const caseId = 'test-id'; + const abortCtrl = new AbortController(); + const addSuccess = jest.fn(); + (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError: jest.fn() }); + + let appMockRender: AppMockRenderer; + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + it('calls getCaseConnectors with correct arguments', async () => { + const spyOnGetCases = jest.spyOn(api, 'getCaseConnectors'); + const { waitForNextUpdate } = renderHook(() => useGetCaseConnectors(caseId), { + wrapper: appMockRender.AppWrapper, + }); + + await waitForNextUpdate(); + + expect(spyOnGetCases).toBeCalledWith('test-id', abortCtrl.signal); + }); + + it('shows a toast error message when an error occurs in the response', async () => { + const spyOnGetCases = jest.spyOn(api, 'getCaseConnectors'); + spyOnGetCases.mockImplementation(() => { + throw new Error('Something went wrong'); + }); + + const addError = jest.fn(); + (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); + + const { waitForNextUpdate } = renderHook(() => useGetCaseConnectors(caseId), { + wrapper: appMockRender.AppWrapper, + }); + + await waitForNextUpdate(); + expect(addError).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/cases/public/containers/use_get_case_connectors.tsx b/x-pack/plugins/cases/public/containers/use_get_case_connectors.tsx new file mode 100644 index 0000000000000..fa7920a8c3553 --- /dev/null +++ b/x-pack/plugins/cases/public/containers/use_get_case_connectors.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 { useQuery } from '@tanstack/react-query'; +import * as i18n from './translations'; +import { getCaseConnectors } from './api'; +import type { ServerError } from '../types'; +import { casesQueriesKeys } from './constants'; +import { useCasesToast } from '../common/use_cases_toast'; +import type { CaseConnectors } from './types'; + +// 30 seconds +const STALE_TIME = 1000 * 30; + +export const useGetCaseConnectors = (caseId: string) => { + const { showErrorToast } = useCasesToast(); + + return useQuery( + casesQueriesKeys.caseConnectors(caseId), + () => { + const abortCtrlRef = new AbortController(); + return getCaseConnectors(caseId, abortCtrlRef.signal); + }, + { + staleTime: STALE_TIME, + onError: (error: ServerError) => { + showErrorToast(error, { title: i18n.ERROR_TITLE }); + }, + } + ); +}; + +export type UseGetCaseConnectors = ReturnType; diff --git a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap index c373435795b17..193451e9d146a 100644 --- a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap +++ b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap @@ -84,6 +84,90 @@ Object { } `; +exports[`audit_logger log function event structure creates the correct audit event for operation: "bulkGetAttachments" with an error and entity 1`] = ` +Object { + "error": Object { + "code": "Error", + "message": "an error", + }, + "event": Object { + "action": "case_comment_bulk_get", + "category": Array [ + "database", + ], + "outcome": "failure", + "type": Array [ + "access", + ], + }, + "kibana": Object { + "saved_object": Object { + "id": "1", + "type": "cases-comments", + }, + }, + "message": "Failed attempt to access cases-comments [id=1] as owner \\"awesome\\"", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "bulkGetAttachments" with an error but no entity 1`] = ` +Object { + "error": Object { + "code": "Error", + "message": "an error", + }, + "event": Object { + "action": "case_comment_bulk_get", + "category": Array [ + "database", + ], + "outcome": "failure", + "type": Array [ + "access", + ], + }, + "message": "Failed attempt to access a comments as any owners", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "bulkGetAttachments" without an error but with an entity 1`] = ` +Object { + "event": Object { + "action": "case_comment_bulk_get", + "category": Array [ + "database", + ], + "outcome": "success", + "type": Array [ + "access", + ], + }, + "kibana": Object { + "saved_object": Object { + "id": "5", + "type": "cases-comments", + }, + }, + "message": "User has accessed cases-comments [id=5] as owner \\"super\\"", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "bulkGetAttachments" without an error or entity 1`] = ` +Object { + "event": Object { + "action": "case_comment_bulk_get", + "category": Array [ + "database", + ], + "outcome": "success", + "type": Array [ + "access", + ], + }, + "message": "User has accessed a comments as any owners", +} +`; + exports[`audit_logger log function event structure creates the correct audit event for operation: "bulkGetCases" with an error and entity 1`] = ` Object { "error": Object { @@ -2100,6 +2184,90 @@ Object { } `; +exports[`audit_logger log function event structure creates the correct audit event for operation: "getUserActionStats" with an error and entity 1`] = ` +Object { + "error": Object { + "code": "Error", + "message": "an error", + }, + "event": Object { + "action": "case_user_action_get_stats", + "category": Array [ + "database", + ], + "outcome": "failure", + "type": Array [ + "access", + ], + }, + "kibana": Object { + "saved_object": Object { + "id": "1", + "type": "cases-user-actions", + }, + }, + "message": "Failed attempt to access cases-user-actions [id=1] as owner \\"awesome\\"", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getUserActionStats" with an error but no entity 1`] = ` +Object { + "error": Object { + "code": "Error", + "message": "an error", + }, + "event": Object { + "action": "case_user_action_get_stats", + "category": Array [ + "database", + ], + "outcome": "failure", + "type": Array [ + "access", + ], + }, + "message": "Failed attempt to access a user actions as any owners", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getUserActionStats" without an error but with an entity 1`] = ` +Object { + "event": Object { + "action": "case_user_action_get_stats", + "category": Array [ + "database", + ], + "outcome": "success", + "type": Array [ + "access", + ], + }, + "kibana": Object { + "saved_object": Object { + "id": "5", + "type": "cases-user-actions", + }, + }, + "message": "User has accessed cases-user-actions [id=5] as owner \\"super\\"", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getUserActionStats" without an error or entity 1`] = ` +Object { + "event": Object { + "action": "case_user_action_get_stats", + "category": Array [ + "database", + ], + "outcome": "success", + "type": Array [ + "access", + ], + }, + "message": "User has accessed a user actions as any owners", +} +`; + exports[`audit_logger log function event structure creates the correct audit event for operation: "getUserActions" with an error and entity 1`] = ` Object { "error": Object { diff --git a/x-pack/plugins/cases/server/authorization/authorization.test.ts b/x-pack/plugins/cases/server/authorization/authorization.test.ts index 612f521128685..0945555689fbf 100644 --- a/x-pack/plugins/cases/server/authorization/authorization.test.ts +++ b/x-pack/plugins/cases/server/authorization/authorization.test.ts @@ -91,6 +91,7 @@ describe('authorization', () => { describe('ensureAuthorized', () => { const feature = { id: '1', cases: ['a'] }; + const checkRequestReturningHasAllAsTrue = jest.fn(async () => ({ hasAllRequested: true })); let securityStart: ReturnType; let featuresStart: jest.Mocked; @@ -101,7 +102,7 @@ describe('authorization', () => { securityStart = securityMock.createStart(); securityStart.authz.mode.useRbacForRequest.mockReturnValue(true); securityStart.authz.checkPrivilegesDynamicallyWithRequest.mockReturnValue( - jest.fn(async () => ({ hasAllRequested: true })) + checkRequestReturningHasAllAsTrue ); featuresStart = featuresPluginMock.createStart(); @@ -119,6 +120,34 @@ describe('authorization', () => { }); }); + it('calls checkRequest with no repeated owners', async () => { + expect.assertions(2); + + const casesGet = securityStart.authz.actions.cases.get as jest.Mock; + casesGet.mockImplementation((owner, op) => `${owner}/${op}`); + + try { + await auth.ensureAuthorized({ + entities: [ + { id: '1', owner: 'b' }, + { id: '2', owner: 'b' }, + ], + operation: Operations.createCase, + }); + } catch (error) { + expect(checkRequestReturningHasAllAsTrue).toBeCalledTimes(1); + expect(checkRequestReturningHasAllAsTrue.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "kibana": Array [ + "b/createCase", + ], + }, + ] + `); + } + }); + it('throws an error when the owner passed in is not included in the features when security is disabled', async () => { expect.assertions(1); securityStart.authz.mode.useRbacForRequest.mockReturnValue(false); @@ -133,6 +162,23 @@ describe('authorization', () => { } }); + it('throws an error with a single owner when the repeated owners passed in are not included in the features when security is disabled', async () => { + expect.assertions(1); + securityStart.authz.mode.useRbacForRequest.mockReturnValue(false); + + try { + await auth.ensureAuthorized({ + entities: [ + { id: '1', owner: 'b' }, + { id: '2', owner: 'b' }, + ], + operation: Operations.createCase, + }); + } catch (error) { + expect(error.message).toBe('Unauthorized to create case with owners: "b"'); + } + }); + it('throws an error when the owner passed in is not included in the features when security undefined', async () => { expect.assertions(1); @@ -154,6 +200,30 @@ describe('authorization', () => { } }); + it('throws an error with a single owner when the repeated owners passed in are not included in the features when security undefined', async () => { + expect.assertions(1); + + auth = await Authorization.create({ + request, + spaces: spacesStart, + features: featuresStart, + auditLogger: new AuthorizationAuditLogger(mockLogger), + logger: loggingSystemMock.createLogger(), + }); + + try { + await auth.ensureAuthorized({ + entities: [ + { id: '1', owner: 'b' }, + { id: '1', owner: 'b' }, + ], + operation: Operations.createCase, + }); + } catch (error) { + expect(error.message).toBe('Unauthorized to create case with owners: "b"'); + } + }); + it('throws an error when the owner passed in is not included in the features when security is enabled', async () => { expect.assertions(1); @@ -167,6 +237,22 @@ describe('authorization', () => { } }); + it('throws an error with a single owner when the repeated owners passed in are not included in the features when security is enabled', async () => { + expect.assertions(1); + + try { + await auth.ensureAuthorized({ + entities: [ + { id: '1', owner: 'b' }, + { id: '2', owner: 'b' }, + ], + operation: Operations.createCase, + }); + } catch (error) { + expect(error.message).toBe('Unauthorized to create case with owners: "b"'); + } + }); + it('logs the error thrown when the passed in owner is not one of the features', async () => { expect.assertions(2); @@ -254,6 +340,26 @@ describe('authorization', () => { } }); + it('throws an error with a single owner listed when the user does not have all the requested privileges', async () => { + expect.assertions(1); + + securityStart.authz.checkPrivilegesDynamicallyWithRequest.mockReturnValue( + jest.fn(async () => ({ hasAllRequested: false })) + ); + + try { + await auth.ensureAuthorized({ + entities: [ + { id: '1', owner: 'a' }, + { id: '2', owner: 'a' }, + ], + operation: Operations.createCase, + }); + } catch (error) { + expect(error.message).toBe('Unauthorized to create case with owners: "a"'); + } + }); + it('throws an error when owner does not exist because it was from a disabled plugin', async () => { expect.assertions(1); @@ -1038,6 +1144,7 @@ describe('authorization', () => { { id: '1', attributes: { owner: 'b' }, type: 'test', references: [] }, { id: '2', attributes: { owner: 'c' }, type: 'test', references: [] }, ], + operation: Operations.bulkGetCases, }); } catch (error) { expect(error.message).toBe('Unauthorized to access cases of any owner'); @@ -1115,6 +1222,7 @@ describe('authorization', () => { { id: '1', attributes: { owner: 'a' }, type: 'test', references: [] }, { id: '2', attributes: { owner: 'b' }, type: 'test', references: [] }, ], + operation: Operations.bulkGetCases, }); await expect(helpersPromise).resolves.not.toThrow(); @@ -1183,6 +1291,7 @@ describe('authorization', () => { { id: '2', attributes: { owner: 'b' }, type: 'test', references: [] }, { id: '3', attributes: { owner: 'c' }, type: 'test', references: [] }, ], + operation: Operations.bulkGetCases, }); expect(res).toEqual({ @@ -1305,6 +1414,7 @@ describe('authorization', () => { { id: '2', attributes: { owner: 'b' }, type: 'test', references: [] }, { id: '3', attributes: { owner: 'c' }, type: 'test', references: [] }, ], + operation: Operations.bulkGetCases, }); expect(res).toEqual({ diff --git a/x-pack/plugins/cases/server/authorization/authorization.ts b/x-pack/plugins/cases/server/authorization/authorization.ts index 884ae817a7a95..14ecaa6111f8c 100644 --- a/x-pack/plugins/cases/server/authorization/authorization.ts +++ b/x-pack/plugins/cases/server/authorization/authorization.ts @@ -14,7 +14,7 @@ import type { Space, SpacesPluginStart } from '@kbn/spaces-plugin/server'; import type { AuthFilterHelpers, OwnerEntity } from './types'; import { getOwnersFilter, groupByAuthorization } from './utils'; import type { OperationDetails } from '.'; -import { AuthorizationAuditLogger, Operations } from '.'; +import { AuthorizationAuditLogger } from '.'; import { createCaseError } from '../common/error'; /** @@ -109,10 +109,9 @@ export class Authorization { operation: OperationDetails; }) { try { - await this._ensureAuthorized( - entities.map((entity) => entity.owner), - operation - ); + const uniqueOwners = Array.from(new Set(entities.map((entity) => entity.owner))); + + await this._ensureAuthorized(uniqueOwners, operation); } catch (error) { this.logSavedObjects({ entities, operation, error }); throw error; @@ -128,13 +127,15 @@ export class Authorization { * * @param savedObjects an array of saved objects to be authorized. Each saved objects should contain * an ID and an owner + * @param operation the operation that should be authorized */ public async getAndEnsureAuthorizedEntities({ savedObjects, + operation, }: { savedObjects: Array>; + operation: OperationDetails; }): Promise<{ authorized: Array>; unauthorized: Array> }> { - const operation = Operations.bulkGetCases; const entities = savedObjects.map((so) => ({ id: so.id, owner: so.attributes.owner, diff --git a/x-pack/plugins/cases/server/authorization/index.ts b/x-pack/plugins/cases/server/authorization/index.ts index 9a4c3cdcdf9aa..cbfef6f4713e3 100644 --- a/x-pack/plugins/cases/server/authorization/index.ts +++ b/x-pack/plugins/cases/server/authorization/index.ts @@ -230,6 +230,14 @@ const AttachmentOperations = { docType: 'comments', savedObjectType: CASE_COMMENT_SAVED_OBJECT, }, + [ReadOperations.BulkGetAttachments]: { + ecsType: EVENT_TYPES.access, + name: ACCESS_COMMENT_OPERATION, + action: 'case_comment_bulk_get', + verbs: accessVerbs, + docType: 'comments', + savedObjectType: CASE_COMMENT_SAVED_OBJECT, + }, [ReadOperations.GetAllComments]: { ecsType: EVENT_TYPES.access, name: ACCESS_COMMENT_OPERATION, @@ -351,4 +359,12 @@ export const Operations: Record; + +type AttachmentSavedObject = SavedObject; + +/** + * Retrieves multiple attachments by id. + */ +export async function bulkGet( + { attachmentIDs, caseID }: BulkGetArgs, + clientArgs: CasesClientArgs, + casesClient: CasesClient +): Promise { + const { + services: { attachmentService }, + logger, + authorization, + } = clientArgs; + + try { + const request = pipe( + excess(BulkGetAttachmentsRequestRt).decode({ ids: attachmentIDs }), + fold(throwErrors(Boom.badRequest), identity) + ); + + throwErrorIfIdsExceedTheLimit(request.ids); + + // perform an authorization check for the case + await casesClient.cases.resolve({ id: caseID }); + + const attachments = await attachmentService.getter.bulkGet(request.ids); + + const { validAttachments, attachmentsWithErrors, invalidAssociationAttachments } = + partitionAttachments(caseID, attachments); + + const { authorized: authorizedAttachments, unauthorized: unauthorizedAttachments } = + await authorization.getAndEnsureAuthorizedEntities({ + savedObjects: validAttachments, + operation: Operations.bulkGetAttachments, + }); + + const errors = constructErrors({ + associationErrors: invalidAssociationAttachments, + unauthorizedAttachments, + soBulkGetErrors: attachmentsWithErrors, + caseId: caseID, + }); + + return BulkGetAttachmentsResponseRt.encode({ + attachments: flattenCommentSavedObjects(authorizedAttachments), + errors, + }); + } catch (error) { + throw createCaseError({ + message: `Failed to bulk get attachments for case id: ${caseID}: ${error}`, + error, + logger, + }); + } +} + +const throwErrorIfIdsExceedTheLimit = (ids: string[]) => { + if (ids.length > MAX_BULK_GET_ATTACHMENTS) { + throw Boom.badRequest( + `Maximum request limit of ${MAX_BULK_GET_ATTACHMENTS} attachments reached` + ); + } +}; + +interface PartitionedAttachments { + validAttachments: AttachmentSavedObject[]; + attachmentsWithErrors: AttachmentSavedObjectWithErrors; + invalidAssociationAttachments: AttachmentSavedObject[]; +} + +const partitionAttachments = ( + caseId: string, + attachments: BulkOptionalAttributes +): PartitionedAttachments => { + const [attachmentsWithoutErrors, errors] = partitionBySOError(attachments.saved_objects); + const [caseAttachments, invalidAssociationAttachments] = partitionByCaseAssociation( + caseId, + attachmentsWithoutErrors + ); + + return { + validAttachments: caseAttachments, + attachmentsWithErrors: errors, + invalidAssociationAttachments, + }; +}; + +const partitionBySOError = (attachments: Array>) => + partition( + attachments, + (attachment) => attachment.error == null && attachment.attributes != null + ) as [AttachmentSavedObject[], AttachmentSavedObjectWithErrors]; + +const partitionByCaseAssociation = (caseId: string, attachments: AttachmentSavedObject[]) => + partition(attachments, (attachment) => { + const ref = getCaseReference(attachment.references); + + return caseId === ref?.id; + }); + +const getCaseReference = (references: SavedObjectReference[]): SavedObjectReference | undefined => { + return references.find((ref) => ref.name === CASE_REF_NAME && ref.type === CASE_SAVED_OBJECT); +}; + +const constructErrors = ({ + caseId, + soBulkGetErrors, + associationErrors, + unauthorizedAttachments, +}: { + caseId: string; + soBulkGetErrors: AttachmentSavedObjectWithErrors; + associationErrors: AttachmentSavedObject[]; + unauthorizedAttachments: AttachmentSavedObject[]; +}): BulkGetAttachmentsResponse['errors'] => { + const errors: BulkGetAttachmentsResponse['errors'] = []; + + for (const soError of soBulkGetErrors) { + errors.push({ + error: soError.error.error, + message: soError.error.message, + status: soError.error.statusCode, + attachmentId: soError.id, + }); + } + + for (const attachment of associationErrors) { + errors.push({ + error: 'Bad Request', + message: `Attachment is not attached to case id=${caseId}`, + status: 400, + attachmentId: attachment.id, + }); + } + + for (const unauthorizedAttachment of unauthorizedAttachments) { + errors.push({ + error: 'Forbidden', + message: `Unauthorized to access attachment with owner: "${unauthorizedAttachment.attributes.owner}"`, + status: 403, + attachmentId: unauthorizedAttachment.id, + }); + } + + return errors; +}; diff --git a/x-pack/plugins/cases/server/client/attachments/client.ts b/x-pack/plugins/cases/server/client/attachments/client.ts index c8cb26a886acf..5647427f76cf4 100644 --- a/x-pack/plugins/cases/server/client/attachments/client.ts +++ b/x-pack/plugins/cases/server/client/attachments/client.ts @@ -5,21 +5,35 @@ * 2.0. */ -import type { AlertResponse, CommentResponse } from '../../../common/api'; +import type { + AlertResponse, + AllCommentsResponse, + BulkGetAttachmentsResponse, + CaseResponse, + CommentResponse, + CommentsResponse, +} from '../../../common/api'; import type { CasesClient } from '../client'; import type { CasesClientInternal } from '../client_internal'; -import type { IAllCommentsResponse, ICaseResponse, ICommentsResponse } from '../typedoc_interfaces'; import type { CasesClientArgs } from '../types'; -import type { AddArgs } from './add'; import { addComment } from './add'; -import type { BulkCreateArgs } from './bulk_create'; +import type { + BulkCreateArgs, + AddArgs, + DeleteAllArgs, + DeleteArgs, + FindArgs, + GetAllAlertsAttachToCase, + GetAllArgs, + GetArgs, + UpdateArgs, + BulkGetArgs, +} from './types'; import { bulkCreate } from './bulk_create'; -import type { DeleteAllArgs, DeleteArgs } from './delete'; import { deleteAll, deleteComment } from './delete'; -import type { FindArgs, GetAllAlertsAttachToCase, GetAllArgs, GetArgs } from './get'; import { find, get, getAll, getAllAlertsAttachToCase } from './get'; -import type { UpdateArgs } from './update'; +import { bulkGet } from './bulk_get'; import { update } from './update'; /** @@ -29,8 +43,9 @@ export interface AttachmentsSubClient { /** * Adds an attachment to a case. */ - add(params: AddArgs): Promise; - bulkCreate(params: BulkCreateArgs): Promise; + add(params: AddArgs): Promise; + bulkCreate(params: BulkCreateArgs): Promise; + bulkGet(params: BulkGetArgs): Promise; /** * Deletes all attachments associated with a single case. */ @@ -42,7 +57,7 @@ export interface AttachmentsSubClient { /** * Retrieves all comments matching the search criteria. */ - find(findArgs: FindArgs): Promise; + find(findArgs: FindArgs): Promise; /** * Retrieves all alerts attach to a case given a single case ID */ @@ -50,7 +65,7 @@ export interface AttachmentsSubClient { /** * Gets all attachments for a single case. */ - getAll(getAllArgs: GetAllArgs): Promise; + getAll(getAllArgs: GetAllArgs): Promise; /** * Retrieves a single attachment for a case. */ @@ -60,7 +75,7 @@ export interface AttachmentsSubClient { * * The request must include all fields for the attachment. Even the fields that are not changing. */ - update(updateArgs: UpdateArgs): Promise; + update(updateArgs: UpdateArgs): Promise; } /** @@ -76,6 +91,7 @@ export const createAttachmentsSubClient = ( const attachmentSubClient: AttachmentsSubClient = { add: (params: AddArgs) => addComment(params, clientArgs), bulkCreate: (params: BulkCreateArgs) => bulkCreate(params, clientArgs), + bulkGet: (params) => bulkGet(params, clientArgs, casesClient), deleteAll: (deleteAllArgs: DeleteAllArgs) => deleteAll(deleteAllArgs, clientArgs), delete: (deleteArgs: DeleteArgs) => deleteComment(deleteArgs, clientArgs), find: (findArgs: FindArgs) => find(findArgs, clientArgs), diff --git a/x-pack/plugins/cases/server/client/attachments/delete.ts b/x-pack/plugins/cases/server/client/attachments/delete.ts index 22d739a3d9379..c1a8e019ff0f8 100644 --- a/x-pack/plugins/cases/server/client/attachments/delete.ts +++ b/x-pack/plugins/cases/server/client/attachments/delete.ts @@ -15,30 +15,7 @@ import { CASE_SAVED_OBJECT, MAX_CONCURRENT_SEARCHES } from '../../../common/cons import type { CasesClientArgs } from '../types'; import { createCaseError } from '../../common/error'; import { Operations } from '../../authorization'; - -/** - * Parameters for deleting all comments of a case. - */ -export interface DeleteAllArgs { - /** - * The case ID to delete all attachments for - */ - caseID: string; -} - -/** - * Parameters for deleting a single attachment of a case. - */ -export interface DeleteArgs { - /** - * The case ID to delete an attachment from - */ - caseID: string; - /** - * The attachment ID to delete - */ - attachmentID: string; -} +import type { DeleteAllArgs, DeleteArgs } from './types'; /** * Delete all comments for a case. @@ -51,7 +28,6 @@ export async function deleteAll( ): Promise { const { user, - unsecuredSavedObjectsClient, services: { caseService, attachmentService, userActionService }, logger, authorization, @@ -76,7 +52,6 @@ export async function deleteAll( const mapper = async (comment: SavedObject) => attachmentService.delete({ - unsecuredSavedObjectsClient, attachmentId: comment.id, refresh: false, }); @@ -115,15 +90,13 @@ export async function deleteComment( ) { const { user, - unsecuredSavedObjectsClient, services: { attachmentService, userActionService }, logger, authorization, } = clientArgs; try { - const myComment = await attachmentService.get({ - unsecuredSavedObjectsClient, + const myComment = await attachmentService.getter.get({ attachmentId: attachmentID, }); @@ -145,7 +118,6 @@ export async function deleteComment( } await attachmentService.delete({ - unsecuredSavedObjectsClient, attachmentId: attachmentID, refresh: false, }); diff --git a/x-pack/plugins/cases/server/client/attachments/get.ts b/x-pack/plugins/cases/server/client/attachments/get.ts index adbdd3abf2448..91160e857c5cf 100644 --- a/x-pack/plugins/cases/server/client/attachments/get.ts +++ b/x-pack/plugins/cases/server/client/attachments/get.ts @@ -12,7 +12,6 @@ import type { AttributesTypeAlerts, CommentResponse, CommentsResponse, - FindQueryParams, } from '../../../common/api'; import { AllCommentsResponseRt, CommentResponseRt, CommentsResponseRt } from '../../../common/api'; import { @@ -29,48 +28,7 @@ import { combineFilters, stringToKueryNode } from '../utils'; import { Operations } from '../../authorization'; import { includeFieldsRequiredForAuthentication } from '../../authorization/utils'; import type { CasesClient } from '../client'; - -/** - * Parameters for finding attachments of a case - */ -export interface FindArgs { - /** - * The case ID for finding associated attachments - */ - caseID: string; - /** - * Optional parameters for filtering the returned attachments - */ - queryParams?: FindQueryParams; -} - -/** - * Parameters for retrieving all attachments of a case - */ -export interface GetAllArgs { - /** - * The case ID to retrieve all attachments for - */ - caseID: string; -} - -export interface GetArgs { - /** - * The ID of the case to retrieve an attachment from - */ - caseID: string; - /** - * The ID of the attachment to retrieve - */ - attachmentID: string; -} - -export interface GetAllAlertsAttachToCase { - /** - * The ID of the case to retrieve the alerts from - */ - caseId: string; -} +import type { FindArgs, GetAllAlertsAttachToCase, GetAllArgs, GetArgs } from './types'; const normalizeAlertResponse = (alerts: Array>): AlertResponse => alerts.reduce((acc: AlertResponse, alert) => { @@ -92,8 +50,6 @@ const normalizeAlertResponse = (alerts: Array> /** * Retrieves all alerts attached to a specific case. - * - * @ignore */ export const getAllAlertsAttachToCase = async ( { caseId }: GetAllAlertsAttachToCase, @@ -101,7 +57,6 @@ export const getAllAlertsAttachToCase = async ( casesClient: CasesClient ): Promise => { const { - unsecuredSavedObjectsClient, authorization, services: { attachmentService }, logger, @@ -117,8 +72,7 @@ export const getAllAlertsAttachToCase = async ( const { filter: authorizationFilter, ensureSavedObjectsAreAuthorized } = await authorization.getAuthorizationFilter(Operations.getAlertsAttachedToCase); - const alerts = await attachmentService.getAllAlertsAttachToCase({ - unsecuredSavedObjectsClient, + const alerts = await attachmentService.getter.getAllAlertsAttachToCase({ caseId: theCase.id, filter: authorizationFilter, }); @@ -142,8 +96,6 @@ export const getAllAlertsAttachToCase = async ( /** * Retrieves the attachments for a case entity. This support pagination. - * - * @ignore */ export async function find( { caseID, queryParams }: FindArgs, @@ -219,8 +171,6 @@ export async function find( /** * Retrieves a single attachment by its ID. - * - * @ignore */ export async function get( { attachmentID, caseID }: GetArgs, @@ -228,14 +178,12 @@ export async function get( ): Promise { const { services: { attachmentService }, - unsecuredSavedObjectsClient, logger, authorization, } = clientArgs; try { - const comment = await attachmentService.get({ - unsecuredSavedObjectsClient, + const comment = await attachmentService.getter.get({ attachmentId: attachmentID, }); @@ -256,8 +204,6 @@ export async function get( /** * Retrieves all the attachments for a case. - * - * @ignore */ export async function getAll( { caseID }: GetAllArgs, diff --git a/x-pack/plugins/cases/server/client/attachments/types.ts b/x-pack/plugins/cases/server/client/attachments/types.ts new file mode 100644 index 0000000000000..51c1de4fe3877 --- /dev/null +++ b/x-pack/plugins/cases/server/client/attachments/types.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 type { + BulkCreateCommentRequest, + CommentPatchRequest, + CommentRequest, + FindQueryParams, +} from '../../../common/api'; + +/** + * The arguments needed for creating a new attachment to a case. + */ +export interface AddArgs { + /** + * The case ID that this attachment will be associated with + */ + caseId: string; + /** + * The attachment values. + */ + comment: CommentRequest; +} + +export interface BulkCreateArgs { + caseId: string; + attachments: BulkCreateCommentRequest; +} + +/** + * Parameters for deleting all comments of a case. + */ +export interface DeleteAllArgs { + /** + * The case ID to delete all attachments for + */ + caseID: string; +} + +/** + * Parameters for deleting a single attachment of a case. + */ +export interface DeleteArgs { + /** + * The case ID to delete an attachment from + */ + caseID: string; + /** + * The attachment ID to delete + */ + attachmentID: string; +} + +/** + * Parameters for finding attachments of a case + */ +export interface FindArgs { + /** + * The case ID for finding associated attachments + */ + caseID: string; + /** + * Optional parameters for filtering the returned attachments + */ + queryParams?: FindQueryParams; +} + +/** + * Parameters for retrieving all attachments of a case + */ +export interface GetAllArgs { + /** + * The case ID to retrieve all attachments for + */ + caseID: string; +} + +export interface GetArgs { + /** + * The ID of the case to retrieve an attachment from + */ + caseID: string; + /** + * The ID of the attachment to retrieve + */ + attachmentID: string; +} + +export interface BulkGetArgs { + caseID: string; + /** + * The ids of the attachments + */ + attachmentIDs: string[]; +} + +export interface GetAllAlertsAttachToCase { + /** + * The ID of the case to retrieve the alerts from + */ + caseId: string; +} + +/** + * Parameters for updating a single attachment + */ +export interface UpdateArgs { + /** + * The ID of the case that is associated with this attachment + */ + caseID: string; + /** + * The full attachment request with the fields updated with appropriate values + */ + updateRequest: CommentPatchRequest; +} diff --git a/x-pack/plugins/cases/server/client/attachments/update.ts b/x-pack/plugins/cases/server/client/attachments/update.ts index f1ce5568c78bb..f2ed100243b08 100644 --- a/x-pack/plugins/cases/server/client/attachments/update.ts +++ b/x-pack/plugins/cases/server/client/attachments/update.ts @@ -10,25 +10,12 @@ import Boom from '@hapi/boom'; import { CaseCommentModel } from '../../common/models'; import { createCaseError } from '../../common/error'; import { isCommentRequestTypeExternalReference } from '../../../common/utils/attachments'; -import type { CaseResponse, CommentPatchRequest } from '../../../common/api'; +import type { CaseResponse } from '../../../common/api'; import { CASE_SAVED_OBJECT } from '../../../common/constants'; import type { CasesClientArgs } from '..'; import { decodeCommentRequest } from '../utils'; import { Operations } from '../../authorization'; - -/** - * Parameters for updating a single attachment - */ -export interface UpdateArgs { - /** - * The ID of the case that is associated with this attachment - */ - caseID: string; - /** - * The full attachment request with the fields updated with appropriate values - */ - updateRequest: CommentPatchRequest; -} +import type { UpdateArgs } from './types'; /** * Update an attachment. @@ -41,7 +28,6 @@ export async function update( ): Promise { const { services: { attachmentService }, - unsecuredSavedObjectsClient, logger, authorization, } = clientArgs; @@ -55,8 +41,7 @@ export async function update( decodeCommentRequest(queryRestAttributes); - const myComment = await attachmentService.get({ - unsecuredSavedObjectsClient, + const myComment = await attachmentService.getter.get({ attachmentId: queryCommentId, }); diff --git a/x-pack/plugins/cases/server/client/cases/bulk_get.test.ts b/x-pack/plugins/cases/server/client/cases/bulk_get.test.ts index f08f79a653c4f..8628fa64056ad 100644 --- a/x-pack/plugins/cases/server/client/cases/bulk_get.test.ts +++ b/x-pack/plugins/cases/server/client/cases/bulk_get.test.ts @@ -32,7 +32,7 @@ describe('bulkGet', () => { const caseSO = mockCases[0]; const clientArgs = createCasesClientMockArgs(); clientArgs.services.caseService.getCases.mockResolvedValue({ saved_objects: [caseSO] }); - clientArgs.services.attachmentService.getCaseCommentStats.mockResolvedValue(new Map()); + clientArgs.services.attachmentService.getter.getCaseCommentStats.mockResolvedValue(new Map()); clientArgs.authorization.getAndEnsureAuthorizedEntities.mockResolvedValue({ authorized: [caseSO], diff --git a/x-pack/plugins/cases/server/client/cases/bulk_get.ts b/x-pack/plugins/cases/server/client/cases/bulk_get.ts index c5f1c4541c283..85952b84bef91 100644 --- a/x-pack/plugins/cases/server/client/cases/bulk_get.ts +++ b/x-pack/plugins/cases/server/client/cases/bulk_get.ts @@ -11,13 +11,13 @@ import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pick, partition } from 'lodash'; -import type { SavedObjectError } from '@kbn/core-saved-objects-common'; import { MAX_BULK_GET_CASES } from '../../../common/constants'; import type { CasesBulkGetResponse, CasesBulkGetResponseCertainFields, CasesBulkGetRequestCertainFields, CaseResponse, + CaseAttributes, } from '../../../common/api'; import { CasesBulkGetRequestRt, @@ -30,11 +30,12 @@ import { import { getTypeProps } from '../../../common/api/runtime_types'; import { createCaseError } from '../../common/error'; import { asArray, flattenCaseSavedObject } from '../../common/utils'; -import type { CasesClientArgs } from '../types'; +import type { CasesClientArgs, SOWithErrors } from '../types'; import { includeFieldsRequiredForAuthentication } from '../../authorization/utils'; import type { CaseSavedObject } from '../../common/types'; +import { Operations } from '../../authorization'; -type SOWithErrors = Array; +type CaseSavedObjectWithErrors = SOWithErrors; /** * Retrieves multiple cases by ids. @@ -47,7 +48,6 @@ export const bulkGet = async caseInfo.error === undefined - ) as [CaseSavedObject[], SOWithErrors]; + ) as [CaseSavedObject[], CaseSavedObjectWithErrors]; const { authorized: authorizedCases, unauthorized: unauthorizedCases } = - await authorization.getAndEnsureAuthorizedEntities({ savedObjects: validCases }); + await authorization.getAndEnsureAuthorizedEntities({ + savedObjects: validCases, + operation: Operations.bulkGetCases, + }); const requestForTotals = ['totalComment', 'totalAlerts'].some( (totalKey) => !fields || fields.includes(totalKey) ); const commentTotals = requestForTotals - ? await attachmentService.getCaseCommentStats({ - unsecuredSavedObjectsClient, + ? await attachmentService.getter.getCaseCommentStats({ caseIds: authorizedCases.map((theCase) => theCase.id), }) : new Map(); @@ -139,7 +141,7 @@ const throwErrorIfCaseIdsReachTheLimit = (ids: string[]) => { }; const constructErrors = ( - soBulkGetErrors: SOWithErrors, + soBulkGetErrors: CaseSavedObjectWithErrors, unauthorizedCases: CaseSavedObject[] ): CasesBulkGetResponse['errors'] => { const errors: CasesBulkGetResponse['errors'] = []; diff --git a/x-pack/plugins/cases/server/client/cases/delete.ts b/x-pack/plugins/cases/server/client/cases/delete.ts index 81f5df0d2d2b3..d9e0b6383af1b 100644 --- a/x-pack/plugins/cases/server/client/cases/delete.ts +++ b/x-pack/plugins/cases/server/client/cases/delete.ts @@ -24,7 +24,6 @@ import { Operations } from '../../authorization'; */ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): Promise { const { - unsecuredSavedObjectsClient, services: { caseService, attachmentService, userActionService }, logger, authorization, @@ -50,9 +49,8 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P entities: Array.from(entities.values()), }); - const attachmentIds = await attachmentService.getAttachmentIdsForCases({ + const attachmentIds = await attachmentService.getter.getAttachmentIdsForCases({ caseIds: ids, - unsecuredSavedObjectsClient, }); const userActionIds = await userActionService.getUserActionIdsForCases(ids); diff --git a/x-pack/plugins/cases/server/client/cases/get.ts b/x-pack/plugins/cases/server/client/cases/get.ts index db836786b6db3..669efc96c960c 100644 --- a/x-pack/plugins/cases/server/client/cases/get.ts +++ b/x-pack/plugins/cases/server/client/cases/get.ts @@ -67,7 +67,6 @@ export const getCasesByAlertID = async ( services: { caseService, attachmentService }, logger, authorization, - unsecuredSavedObjectsClient, } = clientArgs; try { @@ -107,8 +106,7 @@ export const getCasesByAlertID = async ( return []; } - const commentStats = await attachmentService.getCaseCommentStats({ - unsecuredSavedObjectsClient, + const commentStats = await attachmentService.getter.getCaseCommentStats({ caseIds, }); diff --git a/x-pack/plugins/cases/server/client/cases/push.ts b/x-pack/plugins/cases/server/client/cases/push.ts index 340a93dee2d4f..0cbeb7358646c 100644 --- a/x-pack/plugins/cases/server/client/cases/push.ts +++ b/x-pack/plugins/cases/server/client/cases/push.ts @@ -241,7 +241,6 @@ export const push = async ( }), attachmentService.bulkUpdate({ - unsecuredSavedObjectsClient, comments: comments.saved_objects .filter((comment) => comment.attributes.pushed_at == null) .map((comment) => ({ diff --git a/x-pack/plugins/cases/server/client/factory.ts b/x-pack/plugins/cases/server/client/factory.ts index 57f54a745cf2d..b214b40e34701 100644 --- a/x-pack/plugins/cases/server/client/factory.ts +++ b/x-pack/plugins/cases/server/client/factory.ts @@ -170,10 +170,11 @@ export class CasesClientFactory { }): CasesServices { this.validateInitialization(); - const attachmentService = new AttachmentService( - this.logger, - this.options.persistableStateAttachmentTypeRegistry - ); + const attachmentService = new AttachmentService({ + log: this.logger, + persistableStateAttachmentTypeRegistry: this.options.persistableStateAttachmentTypeRegistry, + unsecuredSavedObjectsClient, + }); const caseService = new CasesService({ log: this.logger, diff --git a/x-pack/plugins/cases/server/client/metrics/actions/actions.ts b/x-pack/plugins/cases/server/client/metrics/actions/actions.ts index ce2f71553f274..fe56ac1f289c5 100644 --- a/x-pack/plugins/cases/server/client/metrics/actions/actions.ts +++ b/x-pack/plugins/cases/server/client/metrics/actions/actions.ts @@ -25,7 +25,6 @@ export class Actions extends SingleCaseAggregationHandler { public async compute(): Promise { const { - unsecuredSavedObjectsClient, authorization, services: { attachmentService }, logger, @@ -48,7 +47,6 @@ export class Actions extends SingleCaseAggregationHandler { }, {}); const response = await attachmentService.executeCaseActionsAggregations({ - unsecuredSavedObjectsClient, caseId: theCase.id, filter: authorizationFilter, aggregations, diff --git a/x-pack/plugins/cases/server/client/metrics/alerts/count.ts b/x-pack/plugins/cases/server/client/metrics/alerts/count.ts index f5f7e0d2be560..70344016910b7 100644 --- a/x-pack/plugins/cases/server/client/metrics/alerts/count.ts +++ b/x-pack/plugins/cases/server/client/metrics/alerts/count.ts @@ -18,7 +18,6 @@ export class AlertsCount extends SingleCaseBaseHandler { public async compute(): Promise { const { - unsecuredSavedObjectsClient, authorization, services: { attachmentService }, logger, @@ -38,7 +37,6 @@ export class AlertsCount extends SingleCaseBaseHandler { ); const alertsCount = await attachmentService.countAlertsAttachedToCase({ - unsecuredSavedObjectsClient, caseId: theCase.id, filter: authorizationFilter, }); diff --git a/x-pack/plugins/cases/server/client/mocks.ts b/x-pack/plugins/cases/server/client/mocks.ts index 0c2c57253ab9f..2c17182f49a07 100644 --- a/x-pack/plugins/cases/server/client/mocks.ts +++ b/x-pack/plugins/cases/server/client/mocks.ts @@ -74,6 +74,7 @@ type AttachmentsSubClientMock = jest.Mocked; const createAttachmentsSubClientMock = (): AttachmentsSubClientMock => { return { + bulkGet: jest.fn(), add: jest.fn(), bulkCreate: jest.fn(), deleteAll: jest.fn(), @@ -90,9 +91,10 @@ type UserActionsSubClientMock = jest.Mocked; const createUserActionsSubClientMock = (): UserActionsSubClientMock => { return { + find: jest.fn(), getAll: jest.fn(), getConnectors: jest.fn(), - find: jest.fn(), + stats: jest.fn(), }; }; diff --git a/x-pack/plugins/cases/server/client/types.ts b/x-pack/plugins/cases/server/client/types.ts index 1ceca25fb2871..e052bb7f58550 100644 --- a/x-pack/plugins/cases/server/client/types.ts +++ b/x-pack/plugins/cases/server/client/types.ts @@ -6,13 +6,14 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { SavedObjectsClientContract, Logger } from '@kbn/core/server'; +import type { SavedObjectsClientContract, Logger, SavedObject } from '@kbn/core/server'; import type { ActionsClient } from '@kbn/actions-plugin/server'; import type { LensServerPluginSetup } from '@kbn/lens-plugin/server'; import type { SecurityPluginStart } from '@kbn/security-plugin/server'; import type { IBasePath } from '@kbn/core-http-browser'; import type { ISavedObjectsSerializer } from '@kbn/core-saved-objects-server'; import type { KueryNode } from '@kbn/es-query'; +import type { SavedObjectError } from '@kbn/core-saved-objects-common'; import type { CasesFindRequest, User } from '../../common/api'; import type { Authorization } from '../authorization/authorization'; import type { @@ -64,3 +65,5 @@ export type CasesFindQueryParams = Partial< 'tags' | 'reporters' | 'status' | 'severity' | 'owner' | 'from' | 'to' | 'assignees' > & { sortByField?: string; authorizationFilter?: KueryNode } >; + +export type SOWithErrors = Array & { error: SavedObjectError }>; diff --git a/x-pack/plugins/cases/server/client/user_actions/client.ts b/x-pack/plugins/cases/server/client/user_actions/client.ts index 7a091cb43a0d2..2676955719adb 100644 --- a/x-pack/plugins/cases/server/client/user_actions/client.ts +++ b/x-pack/plugins/cases/server/client/user_actions/client.ts @@ -7,12 +7,14 @@ import type { GetCaseConnectorsResponse, + CaseUserActionStatsResponse, UserActionFindResponse, CaseUserActionsDeprecatedResponse, } from '../../../common/api'; import type { CasesClientArgs } from '../types'; import { get } from './get'; import { getConnectors } from './connectors'; +import { getStats } from './stats'; import type { GetConnectorsRequest, UserActionFind, UserActionGet } from './types'; import { find } from './find'; import type { CasesClient } from '../client'; @@ -30,6 +32,11 @@ export interface UserActionsSubClient { * Retrieves all the connectors used within a given case */ getConnectors(params: GetConnectorsRequest): Promise; + + /** + * Retrieves the total of comments and user actions in a given case + */ + stats(params: UserActionGet): Promise; } /** @@ -43,6 +50,7 @@ export const createUserActionsSubClient = ( find: (params) => find(params, casesClient, clientArgs), getAll: (params) => get(params, clientArgs), getConnectors: (params) => getConnectors(params, clientArgs), + stats: (params) => getStats(params, casesClient, clientArgs), }; return Object.freeze(attachmentSubClient); diff --git a/x-pack/plugins/cases/server/client/user_actions/connectors.ts b/x-pack/plugins/cases/server/client/user_actions/connectors.ts index d3079d466e662..da949996d0fd7 100644 --- a/x-pack/plugins/cases/server/client/user_actions/connectors.ts +++ b/x-pack/plugins/cases/server/client/user_actions/connectors.ts @@ -15,6 +15,7 @@ import type { CaseConnector, CaseUserActionInjectedAttributes, CaseExternalServiceBasic, + GetCaseConnectorsPushDetails, } from '../../../common/api'; import { GetCaseConnectorsResponseRt } from '../../../common/api'; import { @@ -55,6 +56,7 @@ export const getConnectors = async ( connectors, latestUserAction, userActionService, + logger, }); return GetCaseConnectorsResponseRt.encode(results); @@ -121,23 +123,39 @@ const getConnectorsInfo = async ({ latestUserAction, actionsClient, userActionService, + logger, }: { caseId: string; connectors: CaseConnectorActivity[]; latestUserAction?: SavedObject; actionsClient: PublicMethodsOf; userActionService: CaseUserActionService; + logger: CasesClientArgs['logger']; }): Promise => { const connectorIds = connectors.map((connector) => connector.connectorId); const [pushInfo, actionConnectors] = await Promise.all([ getEnrichedPushInfo({ caseId, activity: connectors, userActionService }), - actionsClient.getBulk(connectorIds), + await getActionConnectors(actionsClient, logger, connectorIds), ]); return createConnectorInfoResult({ actionConnectors, connectors, pushInfo, latestUserAction }); }; +const getActionConnectors = async ( + actionsClient: PublicMethodsOf, + logger: CasesClientArgs['logger'], + ids: string[] +): Promise => { + try { + return await actionsClient.getBulk(ids); + } catch (error) { + // silent error and log it + logger.error(`Failed to retrieve action connectors in the get case connectors route: ${error}`); + return []; + } +}; + interface PushDetails { connectorId: string; externalService: CaseExternalServiceBasic; @@ -253,10 +271,12 @@ const createConnectorInfoResult = ({ latestUserAction?: SavedObject; }) => { const results: GetCaseConnectorsResponse = {}; + const actionConnectorsMap = new Map( + actionConnectors.map((actionConnector) => [actionConnector.id, { ...actionConnector }]) + ); - for (let i = 0; i < connectors.length; i++) { - const connectorDetails = actionConnectors[i]; - const aggregationConnector = connectors[i]; + for (const aggregationConnector of connectors) { + const connectorDetails = actionConnectorsMap.get(aggregationConnector.connectorId); const connector = getConnectorInfoFromSavedObject(aggregationConnector.fields); const latestUserActionCreatedAt = getDate(latestUserAction?.attributes.created_at); @@ -269,15 +289,15 @@ const createConnectorInfoResult = ({ latestUserActionDate: latestUserActionCreatedAt, }); + const pushDetails = convertEnrichedPushInfoToDetails(enrichedPushInfo); + results[connector.id] = { ...connector, - name: connectorDetails.name, + name: connectorDetails?.name ?? connector.name, push: { needsToBePushed, hasBeenPushed: hasBeenPushed(enrichedPushInfo), - externalService: enrichedPushInfo?.externalService, - latestUserActionPushDate: enrichedPushInfo?.latestPushDate.toISOString(), - oldestUserActionPushDate: enrichedPushInfo?.oldestPushDate.toISOString(), + ...(pushDetails && { details: pushDetails }), }, }; } @@ -319,3 +339,17 @@ const hasDataToPush = ({ const hasBeenPushed = (pushInfo: EnrichedPushInfo | undefined): boolean => { return pushInfo != null; }; + +const convertEnrichedPushInfoToDetails = ( + info: EnrichedPushInfo | undefined +): GetCaseConnectorsPushDetails | undefined => { + if (info == null) { + return; + } + + return { + latestUserActionPushDate: info.latestPushDate.toISOString(), + oldestUserActionPushDate: info.oldestPushDate.toISOString(), + externalService: info.externalService, + }; +}; diff --git a/x-pack/plugins/cases/server/client/user_actions/stats.ts b/x-pack/plugins/cases/server/client/user_actions/stats.ts new file mode 100644 index 0000000000000..b93f4a19cda20 --- /dev/null +++ b/x-pack/plugins/cases/server/client/user_actions/stats.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 { CaseUserActionStatsResponse } from '../../../common/api'; +import { CaseUserActionStatsResponseRt } from '../../../common/api'; +import { createCaseError } from '../../common/error'; +import type { CasesClientArgs } from '..'; +import type { UserActionGet } from './types'; +import type { CasesClient } from '../client'; + +export const getStats = async ( + { caseId }: UserActionGet, + casesClient: CasesClient, + clientArgs: CasesClientArgs +): Promise => { + const { + services: { userActionService }, + logger, + } = clientArgs; + + try { + await casesClient.cases.resolve({ id: caseId, includeComments: false }); + const totals = await userActionService.getCaseUserActionStats({ + caseId, + }); + + return CaseUserActionStatsResponseRt.encode(totals); + } catch (error) { + throw createCaseError({ + message: `Failed to retrieve user action stats for case id: ${caseId}: ${error}`, + error, + logger, + }); + } +}; diff --git a/x-pack/plugins/cases/server/common/models/case_with_comments.ts b/x-pack/plugins/cases/server/common/models/case_with_comments.ts index 62d6b44b613f8..4e134d873cbaf 100644 --- a/x-pack/plugins/cases/server/common/models/case_with_comments.ts +++ b/x-pack/plugins/cases/server/common/models/case_with_comments.ts @@ -95,8 +95,7 @@ export class CaseCommentModel { }; if (queryRestAttributes.type === CommentType.user && queryRestAttributes?.comment) { - const currentComment = (await this.params.services.attachmentService.get({ - unsecuredSavedObjectsClient: this.params.unsecuredSavedObjectsClient, + const currentComment = (await this.params.services.attachmentService.getter.get({ attachmentId: id, })) as SavedObject; @@ -110,7 +109,6 @@ export class CaseCommentModel { const [comment, commentableCase] = await Promise.all([ this.params.services.attachmentService.update({ - unsecuredSavedObjectsClient: this.params.unsecuredSavedObjectsClient, attachmentId: id, updatedAttributes: { ...queryRestAttributes, @@ -212,7 +210,6 @@ export class CaseCommentModel { const [comment, commentableCase] = await Promise.all([ this.params.services.attachmentService.create({ - unsecuredSavedObjectsClient: this.params.unsecuredSavedObjectsClient, attributes: transformNewComment({ createdDate, ...commentReq, @@ -276,7 +273,6 @@ export class CaseCommentModel { private async validateAlertsLimitOnCase(totalAlertsInReq: number) { const alertsValueCount = await this.params.services.attachmentService.valueCountAlertsAttachedToCase({ - unsecuredSavedObjectsClient: this.params.unsecuredSavedObjectsClient, caseId: this.caseInfo.id, }); @@ -410,7 +406,6 @@ export class CaseCommentModel { const [newlyCreatedAttachments, commentableCase] = await Promise.all([ this.params.services.attachmentService.bulkCreate({ - unsecuredSavedObjectsClient: this.params.unsecuredSavedObjectsClient, attachments: attachments.map(({ id, ...attachment }) => { return { attributes: transformNewComment({ diff --git a/x-pack/plugins/cases/server/routes/api/get_internal_routes.ts b/x-pack/plugins/cases/server/routes/api/get_internal_routes.ts index ca1f44706caf9..41d9904de01bd 100644 --- a/x-pack/plugins/cases/server/routes/api/get_internal_routes.ts +++ b/x-pack/plugins/cases/server/routes/api/get_internal_routes.ts @@ -7,10 +7,12 @@ import type { UserProfileService } from '../../services'; import { getConnectorsRoute } from './internal/get_connectors'; +import { getCaseUserActionStatsRoute } from './internal/get_case_user_actions_stats'; import { bulkCreateAttachmentsRoute } from './internal/bulk_create_attachments'; import { bulkGetCasesRoute } from './internal/bulk_get_cases'; import { suggestUserProfilesRoute } from './internal/suggest_user_profiles'; import type { CaseRoute } from './types'; +import { bulkGetAttachmentsRoute } from './internal/bulk_get_attachments'; export const getInternalRoutes = (userProfileService: UserProfileService) => [ @@ -18,4 +20,6 @@ export const getInternalRoutes = (userProfileService: UserProfileService) => suggestUserProfilesRoute(userProfileService), getConnectorsRoute, bulkGetCasesRoute, + getCaseUserActionStatsRoute, + bulkGetAttachmentsRoute, ] as CaseRoute[]; diff --git a/x-pack/plugins/cases/server/routes/api/internal/bulk_get_attachments.ts b/x-pack/plugins/cases/server/routes/api/internal/bulk_get_attachments.ts new file mode 100644 index 0000000000000..8f72a42b91b41 --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/internal/bulk_get_attachments.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 { schema } from '@kbn/config-schema'; +import type { BulkGetAttachmentsRequest } from '../../../../common/api'; + +import { INTERNAL_BULK_GET_ATTACHMENTS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; +import { escapeHatch } from '../utils'; + +export const bulkGetAttachmentsRoute = createCasesRoute({ + method: 'post', + path: INTERNAL_BULK_GET_ATTACHMENTS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + }), + body: escapeHatch, + }, + handler: async ({ context, request, response }) => { + try { + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); + const body = request.body as BulkGetAttachmentsRequest; + + return response.ok({ + body: await client.attachments.bulkGet({ + caseID: request.params.case_id, + attachmentIDs: body.ids, + }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to bulk get attachments in route case id: ${request.params.case_id}: ${error}`, + error, + }); + } + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/internal/get_case_user_actions_stats.ts b/x-pack/plugins/cases/server/routes/api/internal/get_case_user_actions_stats.ts new file mode 100644 index 0000000000000..58d92c5f9d729 --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/internal/get_case_user_actions_stats.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 { schema } from '@kbn/config-schema'; +import { INTERNAL_GET_CASE_USER_ACTIONS_STATS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; + +export const getCaseUserActionStatsRoute = createCasesRoute({ + method: 'get', + path: INTERNAL_GET_CASE_USER_ACTIONS_STATS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + }), + }, + handler: async ({ context, request, response }) => { + try { + const casesContext = await context.cases; + const casesClient = await casesContext.getCasesClient(); + const caseId = request.params.case_id; + + return response.ok({ + body: await casesClient.userActions.stats({ caseId }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to retrieve stats in route case id: ${request.params.case_id}: ${error}`, + error, + }); + } + }, +}); diff --git a/x-pack/plugins/cases/server/saved_object_types/import_export/export.ts b/x-pack/plugins/cases/server/saved_object_types/import_export/export.ts index 2e898c23d94b7..0fe7b51385981 100644 --- a/x-pack/plugins/cases/server/saved_object_types/import_export/export.ts +++ b/x-pack/plugins/cases/server/saved_object_types/import_export/export.ts @@ -12,7 +12,10 @@ import type { SavedObjectsClientContract, SavedObjectsExportTransformContext, } from '@kbn/core/server'; -import type { CaseUserActionAttributes, CommentAttributes } from '../../../common/api'; +import type { + CaseUserActionAttributesWithoutConnectorId, + CommentAttributesWithoutRefs, +} from '../../../common/api'; import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT, @@ -34,7 +37,13 @@ export async function handleExport({ objects: Array>; coreSetup: CoreSetup; logger: Logger; -}): Promise>> { +}): Promise< + Array< + SavedObject< + ESCaseAttributes | CommentAttributesWithoutRefs | CaseUserActionAttributesWithoutConnectorId + > + > +> { try { if (objects.length <= 0) { return []; @@ -64,15 +73,17 @@ export async function handleExport({ async function getAttachmentsAndUserActionsForCases( savedObjectsClient: SavedObjectsClientContract, caseIds: string[] -): Promise>> { +): Promise< + Array> +> { const [attachments, userActions] = await Promise.all([ - getAssociatedObjects({ + getAssociatedObjects({ savedObjectsClient, caseIds, sortField: defaultSortField, type: CASE_COMMENT_SAVED_OBJECT, }), - getAssociatedObjects({ + getAssociatedObjects({ savedObjectsClient, caseIds, sortField: defaultSortField, diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts index e42d75817c65b..9d46e90bca17f 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts @@ -5,15 +5,17 @@ * 2.0. */ -import type { SavedObjectSanitizedDoc } from '@kbn/core/server'; +import type { SavedObjectSanitizedDoc, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; import { ACTION_SAVED_OBJECT_TYPE } from '@kbn/actions-plugin/server'; +import type { CasesConfigureAttributes } from '../../../common/api'; import { ConnectorTypes } from '../../../common/api'; import { CASE_CONFIGURE_SAVED_OBJECT, SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { CONNECTOR_ID_REFERENCE_NAME } from '../../common/constants'; import { getNoneCaseConnector } from '../../common/utils'; import type { ESCaseConnectorWithId } from '../../services/test_utils'; import type { ESCasesConfigureAttributes } from '../../services/configure/types'; -import { configureConnectorIdMigration } from './configuration'; +import type { UnsanitizedConfigureConnector } from './configuration'; +import { createConnectorAttributeMigration, configureConnectorIdMigration } from './configuration'; // eslint-disable-next-line @typescript-eslint/naming-convention const create_7_14_0_configSchema = (connector?: ESCaseConnectorWithId) => ({ @@ -38,7 +40,95 @@ const create_7_14_0_configSchema = (connector?: ESCaseConnectorWithId) => ({ }, }); +// eslint-disable-next-line @typescript-eslint/naming-convention +const create_7_9_0_configSchema = ( + overrides?: object +): SavedObjectUnsanitizedDoc => { + return { + attributes: { + connector_id: 'b35c26a9-4d0d-4ffb-be85-4e9b96266204', + connector_name: '', + closure_type: 'close-by-user', + created_at: '2023-01-31T22:11:49.480Z', + created_by: { + email: 'test@test.com', + full_name: 'tester', + username: 'tester_user', + }, + updated_at: null, + updated_by: null, + ...overrides, + }, + id: '1', + type: 'cases-configure', + references: [], + updated_at: '2023-01-31T22:00:50.003Z', + } as SavedObjectUnsanitizedDoc; +}; + describe('configuration migrations', () => { + describe('7.10.0 connector migration', () => { + it('creates the connector field with the connector id and name nested under it', () => { + const config = createConnectorAttributeMigration(create_7_9_0_configSchema()); + + expect(config.attributes.connector.id).toEqual('b35c26a9-4d0d-4ffb-be85-4e9b96266204'); + expect(config.attributes.connector.name).toEqual(''); + }); + + it('sets the connector.name field to the existing name', () => { + const config = createConnectorAttributeMigration( + create_7_9_0_configSchema({ connector_name: 'connector-name' }) + ); + + expect(config.attributes.connector.name).toEqual('connector-name'); + }); + + it('sets the connector.fields to null and connector.type to .none', () => { + const config = createConnectorAttributeMigration(create_7_9_0_configSchema()); + + expect(config.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "id": "b35c26a9-4d0d-4ffb-be85-4e9b96266204", + "name": "", + "type": ".none", + } + `); + }); + + it('does not modify the other attributes of the saved object', () => { + const config = createConnectorAttributeMigration(create_7_9_0_configSchema()); + + const configAttributes = config as SavedObjectSanitizedDoc; + expect(configAttributes.attributes.created_by.email).toEqual('test@test.com'); + }); + + it('sets name and id to none when they are undefined', () => { + const config = createConnectorAttributeMigration( + create_7_9_0_configSchema({ connector_name: undefined, connector_id: undefined }) + ); + + expect(config.attributes.connector.id).toEqual('none'); + expect(config.attributes.connector.name).toEqual('none'); + }); + + it('removes the connector_id and connector_name fields', () => { + const config = createConnectorAttributeMigration( + create_7_9_0_configSchema({ connector_name: 'name', connector_id: 'id' }) + ); + + expect(config.attributes).not.toHaveProperty('connector_id'); + expect(config.attributes).not.toHaveProperty('connector_name'); + }); + + it('sets the references to an empty array when it is initially undefined', () => { + const docWithoutRefs = { ...create_7_9_0_configSchema(), references: undefined }; + const config = createConnectorAttributeMigration(docWithoutRefs); + + expect(config.references).toEqual([]); + }); + }); + describe('7.15.0 connector ID migration', () => { it('does not create a reference when the connector ID is none', () => { const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts index 84d1164ac23f1..b758d7ca0e7d3 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts @@ -14,7 +14,7 @@ import { addOwnerToSO } from '.'; import { CONNECTOR_ID_REFERENCE_NAME } from '../../common/constants'; import { transformConnectorIdToReference } from './user_actions/connector_id'; -interface UnsanitizedConfigureConnector { +export interface UnsanitizedConfigureConnector { connector_id: string; connector_name: string; } @@ -28,6 +28,26 @@ interface SanitizedConfigureConnector { }; } +export const createConnectorAttributeMigration = ( + doc: SavedObjectUnsanitizedDoc +): SavedObjectSanitizedDoc => { + const { connector_id, connector_name, ...restAttributes } = doc.attributes; + + return { + ...doc, + attributes: { + ...restAttributes, + connector: { + id: connector_id ?? 'none', + name: connector_name ?? 'none', + type: ConnectorTypes.none, + fields: null, + }, + }, + references: doc.references || [], + }; +}; + export const configureConnectorIdMigration = ( doc: SavedObjectUnsanitizedDoc<{ connector?: { id: string } }> ): SavedObjectSanitizedDoc => { @@ -50,25 +70,7 @@ export const configureConnectorIdMigration = ( }; export const configureMigrations = { - '7.10.0': ( - doc: SavedObjectUnsanitizedDoc - ): SavedObjectSanitizedDoc => { - const { connector_id, connector_name, ...restAttributes } = doc.attributes; - - return { - ...doc, - attributes: { - ...restAttributes, - connector: { - id: connector_id ?? 'none', - name: connector_name ?? 'none', - type: ConnectorTypes.none, - fields: null, - }, - }, - references: doc.references || [], - }; - }, + '7.10.0': createConnectorAttributeMigration, '7.14.0': ( doc: SavedObjectUnsanitizedDoc> ): SavedObjectSanitizedDoc => { diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/index.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/index.ts index e152cb6d386b0..db14d05f05762 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/index.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions/index.ts @@ -18,7 +18,7 @@ import { mergeSavedObjectMigrationMaps } from '@kbn/core/server'; import type { MigrateFunctionsObject, MigrateFunction } from '@kbn/kibana-utils-plugin/common'; import { mapValues } from 'lodash'; import type { PersistableStateAttachmentState } from '../../../attachment_framework/types'; -import type { CaseUserActionAttributes } from '../../../../common/api'; +import type { CaseUserActionAttributesWithoutConnectorId } from '../../../../common/api'; import { ActionTypes, CommentType, ConnectorTypes } from '../../../../common/api'; import type { PersistableStateAttachmentTypeRegistry } from '../../../attachment_framework/persistable_state_registry'; import type { SanitizedCaseOwner } from '..'; @@ -40,7 +40,7 @@ export const createUserActionsMigrations = ( ): SavedObjectMigrationMap => { const persistableStateAttachmentMigrations = mapValues< MigrateFunctionsObject, - SavedObjectMigrationFn + SavedObjectMigrationFn >( getAllPersistableAttachmentMigrations(deps.persistableStateAttachmentTypeRegistry), migratePersistableStateAttachments @@ -105,8 +105,11 @@ export const createUserActionsMigrations = ( export const migratePersistableStateAttachments = ( migrate: MigrateFunction - ): SavedObjectMigrationFn => - (doc: SavedObjectUnsanitizedDoc) => { + ): SavedObjectMigrationFn< + CaseUserActionAttributesWithoutConnectorId, + CaseUserActionAttributesWithoutConnectorId + > => + (doc: SavedObjectUnsanitizedDoc) => { if ( doc.attributes.type !== ActionTypes.comment || doc.attributes.payload.comment.type !== CommentType.persistableState diff --git a/x-pack/plugins/cases/server/services/attachments/index.test.ts b/x-pack/plugins/cases/server/services/attachments/index.test.ts index e1dce7877a58d..17fa8baac72e5 100644 --- a/x-pack/plugins/cases/server/services/attachments/index.test.ts +++ b/x-pack/plugins/cases/server/services/attachments/index.test.ts @@ -28,7 +28,11 @@ describe('CasesService', () => { beforeEach(() => { jest.clearAllMocks(); - service = new AttachmentService(mockLogger, persistableStateAttachmentTypeRegistry); + service = new AttachmentService({ + log: mockLogger, + persistableStateAttachmentTypeRegistry, + unsecuredSavedObjectsClient, + }); }); describe('update', () => { @@ -44,7 +48,6 @@ describe('CasesService', () => { unsecuredSavedObjectsClient.update.mockResolvedValue(soClientRes); const res = await service.update({ - unsecuredSavedObjectsClient, attachmentId: '1', updatedAttributes: persistableStateAttachment, options: { references: [] }, @@ -60,7 +63,6 @@ describe('CasesService', () => { }); const res = await service.update({ - unsecuredSavedObjectsClient, attachmentId: '1', updatedAttributes: externalReferenceAttachmentSO, options: { references: [] }, @@ -76,7 +78,6 @@ describe('CasesService', () => { }); const res = await service.update({ - unsecuredSavedObjectsClient, attachmentId: '1', updatedAttributes: externalReferenceAttachmentES, options: { references: [] }, @@ -111,7 +112,6 @@ describe('CasesService', () => { }); const res = await service.bulkUpdate({ - unsecuredSavedObjectsClient, comments: [ { attachmentId: '1', diff --git a/x-pack/plugins/cases/server/services/attachments/index.ts b/x-pack/plugins/cases/server/services/attachments/index.ts index bda8ab9333b1c..b0192e8b1b8ee 100644 --- a/x-pack/plugins/cases/server/services/attachments/index.ts +++ b/x-pack/plugins/cases/server/services/attachments/index.ts @@ -6,33 +6,23 @@ */ import type { - Logger, SavedObject, SavedObjectReference, SavedObjectsBulkResponse, SavedObjectsBulkUpdateResponse, - SavedObjectsClientContract, SavedObjectsFindResponse, SavedObjectsUpdateOptions, SavedObjectsUpdateResponse, } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { KueryNode } from '@kbn/es-query'; import type { - AttachmentTotals, - AttributesTypeAlerts, CommentAttributes as AttachmentAttributes, CommentAttributesWithoutRefs as AttachmentAttributesWithoutRefs, CommentPatchAttributes as AttachmentPatchAttributes, } from '../../../common/api'; import { CommentType } from '../../../common/api'; -import { - CASE_COMMENT_SAVED_OBJECT, - CASE_SAVED_OBJECT, - MAX_DOCS_PER_PAGE, -} from '../../../common/constants'; -import type { ClientArgs } from '..'; +import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT } from '../../../common/constants'; import { buildFilter, combineFilters } from '../../client/utils'; import { defaultSortField } from '../../common/utils'; import type { AggregationResponse } from '../../client/metrics/types'; @@ -42,15 +32,10 @@ import { injectAttachmentSOAttributesFromRefsForPatch, } from '../so_references'; import type { SavedObjectFindOptionsKueryNode } from '../../common/types'; -import type { PersistableStateAttachmentTypeRegistry } from '../../attachment_framework/persistable_state_registry'; import type { IndexRefresh } from '../types'; +import type { AttachedToCaseArgs, GetAttachmentArgs, ServiceContext } from './types'; +import { AttachmentGetter } from './operations/get'; -interface AttachedToCaseArgs extends ClientArgs { - caseId: string; - filter?: KueryNode; -} - -type GetAllAlertsAttachToCaseArgs = AttachedToCaseArgs; type AlertsAttachedToCaseArgs = AttachedToCaseArgs; interface AttachmentsAttachedToCaseArgs extends AttachedToCaseArgs { @@ -62,19 +47,15 @@ interface CountActionsAttachedToCaseArgs extends AttachedToCaseArgs { aggregations: Record; } -interface GetAttachmentArgs extends ClientArgs { - attachmentId: string; -} - interface DeleteAttachmentArgs extends GetAttachmentArgs, IndexRefresh {} -interface CreateAttachmentArgs extends ClientArgs, IndexRefresh { +interface CreateAttachmentArgs extends IndexRefresh { attributes: AttachmentAttributes; references: SavedObjectReference[]; id: string; } -interface BulkCreateAttachments extends ClientArgs, IndexRefresh { +interface BulkCreateAttachments extends IndexRefresh { attachments: Array<{ attributes: AttachmentAttributes; references: SavedObjectReference[]; @@ -88,60 +69,28 @@ interface UpdateArgs { options?: Omit, 'upsert'>; } -export type UpdateAttachmentArgs = UpdateArgs & ClientArgs; +export type UpdateAttachmentArgs = UpdateArgs; -interface BulkUpdateAttachmentArgs extends ClientArgs, IndexRefresh { +interface BulkUpdateAttachmentArgs extends IndexRefresh { comments: UpdateArgs[]; } export class AttachmentService { - constructor( - private readonly log: Logger, - private readonly persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry - ) {} - - public async getAttachmentIdsForCases({ - caseIds, - unsecuredSavedObjectsClient, - }: { - caseIds: string[]; - unsecuredSavedObjectsClient: SavedObjectsClientContract; - }) { - try { - this.log.debug(`Attempting to retrieve attachments associated with cases: [${caseIds}]`); + private readonly _getter: AttachmentGetter; - const finder = unsecuredSavedObjectsClient.createPointInTimeFinder({ - type: CASE_COMMENT_SAVED_OBJECT, - hasReference: caseIds.map((id) => ({ id, type: CASE_SAVED_OBJECT })), - sortField: 'created_at', - sortOrder: 'asc', - /** - * We only care about the ids so to reduce the data returned we should limit the fields in the response. Core - * doesn't support retrieving no fields (id would always be returned anyway) so to limit it we'll only request - * the owner even though we don't need it. - */ - fields: ['owner'], - perPage: MAX_DOCS_PER_PAGE, - }); - - const ids: string[] = []; - - for await (const attachmentSavedObject of finder.find()) { - ids.push(...attachmentSavedObject.saved_objects.map((attachment) => attachment.id)); - } + constructor(private readonly context: ServiceContext) { + this._getter = new AttachmentGetter(context); + } - return ids; - } catch (error) { - this.log.error(`Error retrieving attachments associated with cases: [${caseIds}]: ${error}`); - throw error; - } + public get getter() { + return this._getter; } public async countAlertsAttachedToCase( params: AlertsAttachedToCaseArgs ): Promise { try { - this.log.debug(`Attempting to count alerts for case id ${params.caseId}`); + this.context.log.debug(`Attempting to count alerts for case id ${params.caseId}`); const res = await this.executeCaseAggregations<{ alerts: { value: number } }>({ ...params, attachmentType: CommentType.alert, @@ -150,7 +99,7 @@ export class AttachmentService { return res?.alerts?.value; } catch (error) { - this.log.error(`Error while counting alerts for case id ${params.caseId}: ${error}`); + this.context.log.error(`Error while counting alerts for case id ${params.caseId}: ${error}`); throw error; } } @@ -167,7 +116,7 @@ export class AttachmentService { public async valueCountAlertsAttachedToCase(params: AlertsAttachedToCaseArgs): Promise { try { - this.log.debug(`Attempting to value count alerts for case id ${params.caseId}`); + this.context.log.debug(`Attempting to value count alerts for case id ${params.caseId}`); const res = await this.executeCaseAggregations<{ alerts: { value: number } }>({ ...params, attachmentType: CommentType.alert, @@ -176,47 +125,9 @@ export class AttachmentService { return res?.alerts?.value ?? 0; } catch (error) { - this.log.error(`Error while value counting alerts for case id ${params.caseId}: ${error}`); - throw error; - } - } - - /** - * Retrieves all the alerts attached to a case. - */ - public async getAllAlertsAttachToCase({ - unsecuredSavedObjectsClient, - caseId, - filter, - }: GetAllAlertsAttachToCaseArgs): Promise>> { - try { - this.log.debug(`Attempting to GET all alerts for case id ${caseId}`); - const alertsFilter = buildFilter({ - filters: [CommentType.alert], - field: 'type', - operator: 'or', - type: CASE_COMMENT_SAVED_OBJECT, - }); - - const combinedFilter = combineFilters([alertsFilter, filter]); - - const finder = unsecuredSavedObjectsClient.createPointInTimeFinder({ - type: CASE_COMMENT_SAVED_OBJECT, - hasReference: { type: CASE_SAVED_OBJECT, id: caseId }, - sortField: 'created_at', - sortOrder: 'asc', - filter: combinedFilter, - perPage: MAX_DOCS_PER_PAGE, - }); - - let result: Array> = []; - for await (const userActionSavedObject of finder.find()) { - result = result.concat(userActionSavedObject.saved_objects); - } - - return result; - } catch (error) { - this.log.error(`Error on GET all alerts for case id ${caseId}: ${error}`); + this.context.log.error( + `Error while value counting alerts for case id ${params.caseId}: ${error}` + ); throw error; } } @@ -225,14 +136,13 @@ export class AttachmentService { * Executes the aggregations against a type of attachment attached to a case. */ public async executeCaseAggregations({ - unsecuredSavedObjectsClient, caseId, filter, aggregations, attachmentType, }: AttachmentsAttachedToCaseArgs): Promise { try { - this.log.debug(`Attempting to aggregate for case id ${caseId}`); + this.context.log.debug(`Attempting to aggregate for case id ${caseId}`); const attachmentFilter = buildFilter({ filters: attachmentType, field: 'type', @@ -242,7 +152,10 @@ export class AttachmentService { const combinedFilter = combineFilters([attachmentFilter, filter]); - const response = await unsecuredSavedObjectsClient.find({ + const response = await this.context.unsecuredSavedObjectsClient.find< + AttachmentAttributes, + Agg + >({ type: CASE_COMMENT_SAVED_OBJECT, hasReference: { type: CASE_SAVED_OBJECT, id: caseId }, page: 1, @@ -254,7 +167,7 @@ export class AttachmentService { return response.aggregations; } catch (error) { - this.log.error(`Error while executing aggregation for case id ${caseId}: ${error}`); + this.context.log.error(`Error while executing aggregation for case id ${caseId}: ${error}`); throw error; } } @@ -266,133 +179,114 @@ export class AttachmentService { params: CountActionsAttachedToCaseArgs ): Promise { try { - this.log.debug(`Attempting to count actions for case id ${params.caseId}`); + this.context.log.debug(`Attempting to count actions for case id ${params.caseId}`); return await this.executeCaseAggregations({ ...params, attachmentType: CommentType.actions }); } catch (error) { - this.log.error(`Error while counting actions for case id ${params.caseId}: ${error}`); + this.context.log.error(`Error while counting actions for case id ${params.caseId}: ${error}`); throw error; } } - public async get({ - unsecuredSavedObjectsClient, - attachmentId, - }: GetAttachmentArgs): Promise> { + public async delete({ attachmentId, refresh }: DeleteAttachmentArgs) { try { - this.log.debug(`Attempting to GET attachment ${attachmentId}`); - const res = await unsecuredSavedObjectsClient.get( + this.context.log.debug(`Attempting to DELETE attachment ${attachmentId}`); + return await this.context.unsecuredSavedObjectsClient.delete( CASE_COMMENT_SAVED_OBJECT, - attachmentId + attachmentId, + { + refresh, + } ); - - return injectAttachmentSOAttributesFromRefs(res, this.persistableStateAttachmentTypeRegistry); } catch (error) { - this.log.error(`Error on GET attachment ${attachmentId}: ${error}`); - throw error; - } - } - - public async delete({ - unsecuredSavedObjectsClient, - attachmentId, - refresh, - }: DeleteAttachmentArgs) { - try { - this.log.debug(`Attempting to DELETE attachment ${attachmentId}`); - return await unsecuredSavedObjectsClient.delete(CASE_COMMENT_SAVED_OBJECT, attachmentId, { - refresh, - }); - } catch (error) { - this.log.error(`Error on DELETE attachment ${attachmentId}: ${error}`); + this.context.log.error(`Error on DELETE attachment ${attachmentId}: ${error}`); throw error; } } public async create({ - unsecuredSavedObjectsClient, attributes, references, id, refresh, }: CreateAttachmentArgs): Promise> { try { - this.log.debug(`Attempting to POST a new comment`); + this.context.log.debug(`Attempting to POST a new comment`); const { attributes: extractedAttributes, references: extractedReferences } = extractAttachmentSORefsFromAttributes( attributes, references, - this.persistableStateAttachmentTypeRegistry + this.context.persistableStateAttachmentTypeRegistry ); - const attachment = await unsecuredSavedObjectsClient.create( - CASE_COMMENT_SAVED_OBJECT, - extractedAttributes, - { - references: extractedReferences, - id, - refresh, - } - ); + const attachment = + await this.context.unsecuredSavedObjectsClient.create( + CASE_COMMENT_SAVED_OBJECT, + extractedAttributes, + { + references: extractedReferences, + id, + refresh, + } + ); return injectAttachmentSOAttributesFromRefs( attachment, - this.persistableStateAttachmentTypeRegistry + this.context.persistableStateAttachmentTypeRegistry ); } catch (error) { - this.log.error(`Error on POST a new comment: ${error}`); + this.context.log.error(`Error on POST a new comment: ${error}`); throw error; } } public async bulkCreate({ - unsecuredSavedObjectsClient, attachments, refresh, }: BulkCreateAttachments): Promise> { try { - this.log.debug(`Attempting to bulk create attachments`); - const res = await unsecuredSavedObjectsClient.bulkCreate( - attachments.map((attachment) => { - const { attributes: extractedAttributes, references: extractedReferences } = - extractAttachmentSORefsFromAttributes( - attachment.attributes, - attachment.references, - this.persistableStateAttachmentTypeRegistry - ); - - return { - type: CASE_COMMENT_SAVED_OBJECT, - ...attachment, - attributes: extractedAttributes, - references: extractedReferences, - }; - }), - { refresh } - ); + this.context.log.debug(`Attempting to bulk create attachments`); + const res = + await this.context.unsecuredSavedObjectsClient.bulkCreate( + attachments.map((attachment) => { + const { attributes: extractedAttributes, references: extractedReferences } = + extractAttachmentSORefsFromAttributes( + attachment.attributes, + attachment.references, + this.context.persistableStateAttachmentTypeRegistry + ); + + return { + type: CASE_COMMENT_SAVED_OBJECT, + ...attachment, + attributes: extractedAttributes, + references: extractedReferences, + }; + }), + { refresh } + ); return { saved_objects: res.saved_objects.map((so) => { return injectAttachmentSOAttributesFromRefs( so, - this.persistableStateAttachmentTypeRegistry + this.context.persistableStateAttachmentTypeRegistry ); }), }; } catch (error) { - this.log.error(`Error on bulk create attachments: ${error}`); + this.context.log.error(`Error on bulk create attachments: ${error}`); throw error; } } public async update({ - unsecuredSavedObjectsClient, attachmentId, updatedAttributes, options, }: UpdateAttachmentArgs): Promise> { try { - this.log.debug(`Attempting to UPDATE comment ${attachmentId}`); + this.context.log.debug(`Attempting to UPDATE comment ${attachmentId}`); const { attributes: extractedAttributes, @@ -401,165 +295,116 @@ export class AttachmentService { } = extractAttachmentSORefsFromAttributes( updatedAttributes, options?.references ?? [], - this.persistableStateAttachmentTypeRegistry + this.context.persistableStateAttachmentTypeRegistry ); const shouldUpdateRefs = extractedReferences.length > 0 || didDeleteOperation; - const res = await unsecuredSavedObjectsClient.update( - CASE_COMMENT_SAVED_OBJECT, - attachmentId, - extractedAttributes, - { - ...options, - /** - * If options?.references are undefined and there is no field to move to the refs - * then the extractedReferences will be an empty array. If we pass the empty array - * on the update then all previously refs will be removed. The check below is needed - * to prevent this. - */ - references: shouldUpdateRefs ? extractedReferences : undefined, - } - ); + const res = + await this.context.unsecuredSavedObjectsClient.update( + CASE_COMMENT_SAVED_OBJECT, + attachmentId, + extractedAttributes, + { + ...options, + /** + * If options?.references are undefined and there is no field to move to the refs + * then the extractedReferences will be an empty array. If we pass the empty array + * on the update then all previously refs will be removed. The check below is needed + * to prevent this. + */ + references: shouldUpdateRefs ? extractedReferences : undefined, + } + ); return injectAttachmentSOAttributesFromRefsForPatch( updatedAttributes, res, - this.persistableStateAttachmentTypeRegistry + this.context.persistableStateAttachmentTypeRegistry ); } catch (error) { - this.log.error(`Error on UPDATE comment ${attachmentId}: ${error}`); + this.context.log.error(`Error on UPDATE comment ${attachmentId}: ${error}`); throw error; } } public async bulkUpdate({ - unsecuredSavedObjectsClient, comments, refresh, }: BulkUpdateAttachmentArgs): Promise> { try { - this.log.debug( + this.context.log.debug( `Attempting to UPDATE comments ${comments.map((c) => c.attachmentId).join(', ')}` ); - const res = await unsecuredSavedObjectsClient.bulkUpdate( - comments.map((c) => { - const { - attributes: extractedAttributes, - references: extractedReferences, - didDeleteOperation, - } = extractAttachmentSORefsFromAttributes( - c.updatedAttributes, - c.options?.references ?? [], - this.persistableStateAttachmentTypeRegistry - ); - - const shouldUpdateRefs = extractedReferences.length > 0 || didDeleteOperation; + const res = + await this.context.unsecuredSavedObjectsClient.bulkUpdate( + comments.map((c) => { + const { + attributes: extractedAttributes, + references: extractedReferences, + didDeleteOperation, + } = extractAttachmentSORefsFromAttributes( + c.updatedAttributes, + c.options?.references ?? [], + this.context.persistableStateAttachmentTypeRegistry + ); - return { - ...c.options, - type: CASE_COMMENT_SAVED_OBJECT, - id: c.attachmentId, - attributes: extractedAttributes, - /* If c.options?.references are undefined and there is no field to move to the refs - * then the extractedAttributes will be an empty array. If we pass the empty array - * on the update then all previously refs will be removed. The check below is needed - * to prevent this. - */ - references: shouldUpdateRefs ? extractedReferences : undefined, - }; - }), - { refresh } - ); + const shouldUpdateRefs = extractedReferences.length > 0 || didDeleteOperation; + + return { + ...c.options, + type: CASE_COMMENT_SAVED_OBJECT, + id: c.attachmentId, + attributes: extractedAttributes, + /* If c.options?.references are undefined and there is no field to move to the refs + * then the extractedAttributes will be an empty array. If we pass the empty array + * on the update then all previously refs will be removed. The check below is needed + * to prevent this. + */ + references: shouldUpdateRefs ? extractedReferences : undefined, + }; + }), + { refresh } + ); return { saved_objects: res.saved_objects.map((so, index) => { return injectAttachmentSOAttributesFromRefsForPatch( comments[index].updatedAttributes, so, - this.persistableStateAttachmentTypeRegistry + this.context.persistableStateAttachmentTypeRegistry ); }), }; } catch (error) { - this.log.error( + this.context.log.error( `Error on UPDATE comments ${comments.map((c) => c.attachmentId).join(', ')}: ${error}` ); throw error; } } - public async getCaseCommentStats({ - unsecuredSavedObjectsClient, - caseIds, - }: { - unsecuredSavedObjectsClient: SavedObjectsClientContract; - caseIds: string[]; - }): Promise> { - if (caseIds.length <= 0) { - return new Map(); - } - - interface AggsResult { - references: { - caseIds: { - buckets: Array<{ - key: string; - doc_count: number; - reverse: { - alerts: { - value: number; - }; - comments: { - doc_count: number; - }; - }; - }>; - }; - }; - } - - const res = await unsecuredSavedObjectsClient.find({ - hasReference: caseIds.map((id) => ({ type: CASE_SAVED_OBJECT, id })), - hasReferenceOperator: 'OR', - type: CASE_COMMENT_SAVED_OBJECT, - perPage: 0, - aggs: AttachmentService.buildCommentStatsAggs(caseIds), - }); - - return ( - res.aggregations?.references.caseIds.buckets.reduce((acc, idBucket) => { - acc.set(idBucket.key, { - userComments: idBucket.reverse.comments.doc_count, - alerts: idBucket.reverse.alerts.value, - }); - return acc; - }, new Map()) ?? new Map() - ); - } - public async find({ - unsecuredSavedObjectsClient, options, }: { - unsecuredSavedObjectsClient: SavedObjectsClientContract; options?: SavedObjectFindOptionsKueryNode; }): Promise> { try { - this.log.debug(`Attempting to find comments`); - const res = await unsecuredSavedObjectsClient.find({ - sortField: defaultSortField, - ...options, - type: CASE_COMMENT_SAVED_OBJECT, - }); + this.context.log.debug(`Attempting to find comments`); + const res = + await this.context.unsecuredSavedObjectsClient.find({ + sortField: defaultSortField, + ...options, + type: CASE_COMMENT_SAVED_OBJECT, + }); return { ...res, saved_objects: res.saved_objects.map((so) => { const injectedSO = injectAttachmentSOAttributesFromRefs( so, - this.persistableStateAttachmentTypeRegistry + this.context.persistableStateAttachmentTypeRegistry ); return { @@ -569,47 +414,8 @@ export class AttachmentService { }), }; } catch (error) { - this.log.error(`Error on find comments: ${error}`); + this.context.log.error(`Error on find comments: ${error}`); throw error; } } - - private static buildCommentStatsAggs( - ids: string[] - ): Record { - return { - references: { - nested: { - path: `${CASE_COMMENT_SAVED_OBJECT}.references`, - }, - aggregations: { - caseIds: { - terms: { - field: `${CASE_COMMENT_SAVED_OBJECT}.references.id`, - size: ids.length, - }, - aggregations: { - reverse: { - reverse_nested: {}, - aggregations: { - alerts: { - cardinality: { - field: `${CASE_COMMENT_SAVED_OBJECT}.attributes.alertId`, - }, - }, - comments: { - filter: { - term: { - [`${CASE_COMMENT_SAVED_OBJECT}.attributes.type`]: CommentType.user, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }; - } } diff --git a/x-pack/plugins/cases/server/services/attachments/operations/get.ts b/x-pack/plugins/cases/server/services/attachments/operations/get.ts new file mode 100644 index 0000000000000..89fa8bbb12277 --- /dev/null +++ b/x-pack/plugins/cases/server/services/attachments/operations/get.ts @@ -0,0 +1,250 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { SavedObject } from '@kbn/core/server'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { + CASE_COMMENT_SAVED_OBJECT, + CASE_SAVED_OBJECT, + MAX_DOCS_PER_PAGE, +} from '../../../../common/constants'; +import { buildFilter, combineFilters } from '../../../client/utils'; +import type { + AttachmentTotals, + AttributesTypeAlerts, + CommentAttributes as AttachmentAttributes, + CommentAttributesWithoutRefs as AttachmentAttributesWithoutRefs, + CommentAttributes, +} from '../../../../common/api'; +import { CommentType } from '../../../../common/api'; +import type { + AttachedToCaseArgs, + BulkOptionalAttributes, + GetAttachmentArgs, + ServiceContext, +} from '../types'; +import { + injectAttachmentAttributesAndHandleErrors, + injectAttachmentSOAttributesFromRefs, +} from '../../so_references'; + +type GetAllAlertsAttachToCaseArgs = AttachedToCaseArgs; + +export class AttachmentGetter { + constructor(private readonly context: ServiceContext) {} + + public async bulkGet( + attachmentIds: string[] + ): Promise> { + try { + this.context.log.debug( + `Attempting to retrieve attachments with ids: ${attachmentIds.join()}` + ); + + const response = + await this.context.unsecuredSavedObjectsClient.bulkGet( + attachmentIds.map((id) => ({ id, type: CASE_COMMENT_SAVED_OBJECT })) + ); + + return { + saved_objects: response.saved_objects.map((so) => + injectAttachmentAttributesAndHandleErrors( + so, + this.context.persistableStateAttachmentTypeRegistry + ) + ), + }; + } catch (error) { + this.context.log.error( + `Error retrieving attachments with ids ${attachmentIds.join()}: ${error}` + ); + throw error; + } + } + + public async getAttachmentIdsForCases({ caseIds }: { caseIds: string[] }) { + try { + this.context.log.debug( + `Attempting to retrieve attachments associated with cases: [${caseIds}]` + ); + + const finder = this.context.unsecuredSavedObjectsClient.createPointInTimeFinder({ + type: CASE_COMMENT_SAVED_OBJECT, + hasReference: caseIds.map((id) => ({ id, type: CASE_SAVED_OBJECT })), + sortField: 'created_at', + sortOrder: 'asc', + /** + * We only care about the ids so to reduce the data returned we should limit the fields in the response. Core + * doesn't support retrieving no fields (id would always be returned anyway) so to limit it we'll only request + * the owner even though we don't need it. + */ + fields: ['owner'], + perPage: MAX_DOCS_PER_PAGE, + }); + + const ids: string[] = []; + + for await (const attachmentSavedObject of finder.find()) { + ids.push(...attachmentSavedObject.saved_objects.map((attachment) => attachment.id)); + } + + return ids; + } catch (error) { + this.context.log.error( + `Error retrieving attachments associated with cases: [${caseIds}]: ${error}` + ); + throw error; + } + } + + /** + * Retrieves all the alerts attached to a case. + */ + public async getAllAlertsAttachToCase({ + caseId, + filter, + }: GetAllAlertsAttachToCaseArgs): Promise>> { + try { + this.context.log.debug(`Attempting to GET all alerts for case id ${caseId}`); + const alertsFilter = buildFilter({ + filters: [CommentType.alert], + field: 'type', + operator: 'or', + type: CASE_COMMENT_SAVED_OBJECT, + }); + + const combinedFilter = combineFilters([alertsFilter, filter]); + + const finder = + this.context.unsecuredSavedObjectsClient.createPointInTimeFinder({ + type: CASE_COMMENT_SAVED_OBJECT, + hasReference: { type: CASE_SAVED_OBJECT, id: caseId }, + sortField: 'created_at', + sortOrder: 'asc', + filter: combinedFilter, + perPage: MAX_DOCS_PER_PAGE, + }); + + let result: Array> = []; + for await (const userActionSavedObject of finder.find()) { + result = result.concat(userActionSavedObject.saved_objects); + } + + return result; + } catch (error) { + this.context.log.error(`Error on GET all alerts for case id ${caseId}: ${error}`); + throw error; + } + } + + public async get({ + attachmentId, + }: GetAttachmentArgs): Promise> { + try { + this.context.log.debug(`Attempting to GET attachment ${attachmentId}`); + const res = + await this.context.unsecuredSavedObjectsClient.get( + CASE_COMMENT_SAVED_OBJECT, + attachmentId + ); + + return injectAttachmentSOAttributesFromRefs( + res, + this.context.persistableStateAttachmentTypeRegistry + ); + } catch (error) { + this.context.log.error(`Error on GET attachment ${attachmentId}: ${error}`); + throw error; + } + } + + public async getCaseCommentStats({ + caseIds, + }: { + caseIds: string[]; + }): Promise> { + if (caseIds.length <= 0) { + return new Map(); + } + + interface AggsResult { + references: { + caseIds: { + buckets: Array<{ + key: string; + doc_count: number; + reverse: { + alerts: { + value: number; + }; + comments: { + doc_count: number; + }; + }; + }>; + }; + }; + } + + const res = await this.context.unsecuredSavedObjectsClient.find({ + hasReference: caseIds.map((id) => ({ type: CASE_SAVED_OBJECT, id })), + hasReferenceOperator: 'OR', + type: CASE_COMMENT_SAVED_OBJECT, + perPage: 0, + aggs: AttachmentGetter.buildCommentStatsAggs(caseIds), + }); + + return ( + res.aggregations?.references.caseIds.buckets.reduce((acc, idBucket) => { + acc.set(idBucket.key, { + userComments: idBucket.reverse.comments.doc_count, + alerts: idBucket.reverse.alerts.value, + }); + return acc; + }, new Map()) ?? new Map() + ); + } + + private static buildCommentStatsAggs( + ids: string[] + ): Record { + return { + references: { + nested: { + path: `${CASE_COMMENT_SAVED_OBJECT}.references`, + }, + aggregations: { + caseIds: { + terms: { + field: `${CASE_COMMENT_SAVED_OBJECT}.references.id`, + size: ids.length, + }, + aggregations: { + reverse: { + reverse_nested: {}, + aggregations: { + alerts: { + cardinality: { + field: `${CASE_COMMENT_SAVED_OBJECT}.attributes.alertId`, + }, + }, + comments: { + filter: { + term: { + [`${CASE_COMMENT_SAVED_OBJECT}.attributes.type`]: CommentType.user, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }; + } +} diff --git a/x-pack/plugins/cases/server/services/attachments/types.ts b/x-pack/plugins/cases/server/services/attachments/types.ts new file mode 100644 index 0000000000000..554ba8afb049b --- /dev/null +++ b/x-pack/plugins/cases/server/services/attachments/types.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 type { + Logger, + SavedObject, + SavedObjectsBulkResponse, + SavedObjectsClientContract, +} from '@kbn/core/server'; +import type { KueryNode } from '@kbn/es-query'; +import type { PersistableStateAttachmentTypeRegistry } from '../../attachment_framework/persistable_state_registry'; +import type { PartialField } from '../../types'; + +export interface ServiceContext { + log: Logger; + persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry; + unsecuredSavedObjectsClient: SavedObjectsClientContract; +} + +export interface AttachedToCaseArgs { + caseId: string; + filter?: KueryNode; +} + +export interface GetAttachmentArgs { + attachmentId: string; +} + +export type OptionalAttributes = PartialField, 'attributes'>; + +export interface BulkOptionalAttributes + extends Omit, 'saved_objects'> { + saved_objects: Array>; +} diff --git a/x-pack/plugins/cases/server/services/cases/index.test.ts b/x-pack/plugins/cases/server/services/cases/index.test.ts index c00407ec1a965..c84d4df964157 100644 --- a/x-pack/plugins/cases/server/services/cases/index.test.ts +++ b/x-pack/plugins/cases/server/services/cases/index.test.ts @@ -153,10 +153,11 @@ describe('CasesService', () => { const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); const mockLogger = loggerMock.create(); const persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry(); - const attachmentService = new AttachmentService( - mockLogger, - persistableStateAttachmentTypeRegistry - ); + const attachmentService = new AttachmentService({ + log: mockLogger, + persistableStateAttachmentTypeRegistry, + unsecuredSavedObjectsClient, + }); let service: CasesService; diff --git a/x-pack/plugins/cases/server/services/cases/index.ts b/x-pack/plugins/cases/server/services/cases/index.ts index 661f4cda00d7a..bc694a44d93eb 100644 --- a/x-pack/plugins/cases/server/services/cases/index.ts +++ b/x-pack/plugins/cases/server/services/cases/index.ts @@ -226,8 +226,7 @@ export class CasesService { return accMap; }, new Map>()); - const commentTotals = await this.attachmentService.getCaseCommentStats({ - unsecuredSavedObjectsClient: this.unsecuredSavedObjectsClient, + const commentTotals = await this.attachmentService.getter.getCaseCommentStats({ caseIds: Array.from(casesMap.keys()), }); @@ -411,7 +410,6 @@ export class CasesService { this.log.debug(`Attempting to GET all comments internal for id ${JSON.stringify(id)}`); if (options?.page !== undefined || options?.perPage !== undefined) { return this.attachmentService.find({ - unsecuredSavedObjectsClient: this.unsecuredSavedObjectsClient, options: { sortField: defaultSortField, ...options, @@ -420,7 +418,6 @@ export class CasesService { } return this.attachmentService.find({ - unsecuredSavedObjectsClient: this.unsecuredSavedObjectsClient, options: { page: 1, perPage: MAX_DOCS_PER_PAGE, @@ -521,7 +518,6 @@ export class CasesService { username, full_name: user.full_name ?? null, email: user.email ?? null, - // TODO: verify that adding a new field is ok, shouldn't be a breaking change profile_uid: user.profile_uid, }; }) ?? [] diff --git a/x-pack/plugins/cases/server/services/mocks.ts b/x-pack/plugins/cases/server/services/mocks.ts index 7b2adbd1094f3..ee65ce172c596 100644 --- a/x-pack/plugins/cases/server/services/mocks.ts +++ b/x-pack/plugins/cases/server/services/mocks.ts @@ -14,6 +14,7 @@ import type { ConnectorMappingsService, AttachmentService, } from '.'; +import type { AttachmentGetter } from './attachments/operations/get'; import type { LicensingService } from './licensing'; import type { EmailNotificationService } from './notifications/email_notification_service'; import type { UserActionPersister } from './user_actions/operations/create'; @@ -24,6 +25,12 @@ interface UserActionServiceOperations { finder: CaseUserActionFinderServiceMock; } +interface AttachmentServiceOperations { + getter: AttachmentGetterServiceMock; +} + +export type AttachmentGetterServiceMock = jest.Mocked; + export type CaseServiceMock = jest.Mocked; export type CaseConfigureServiceMock = jest.Mocked; export type ConnectorMappingsServiceMock = jest.Mocked; @@ -33,7 +40,7 @@ export type CaseUserActionServiceMock = jest.Mocked< export type CaseUserActionPersisterServiceMock = jest.Mocked; export type CaseUserActionFinderServiceMock = jest.Mocked; export type AlertServiceMock = jest.Mocked; -export type AttachmentServiceMock = jest.Mocked; +export type AttachmentServiceMock = jest.Mocked; export type LicensingServiceMock = jest.Mocked; export type NotificationServiceMock = jest.Mocked; @@ -117,6 +124,7 @@ export const createUserActionServiceMock = (): CaseUserActionServiceMock => { getAll: jest.fn(), getUniqueConnectors: jest.fn(), getUserActionIdsForCases: jest.fn(), + getCaseUserActionStats: jest.fn(), }; // the cast here is required because jest.Mocked tries to include private members and would throw an error @@ -134,22 +142,33 @@ export const createAlertServiceMock = (): AlertServiceMock => { return service as unknown as AlertServiceMock; }; -export const createAttachmentServiceMock = (): AttachmentServiceMock => { - const service: PublicMethodsOf = { +const createAttachmentGetterServiceMock = (): AttachmentGetterServiceMock => { + const service: PublicMethodsOf = { get: jest.fn(), + bulkGet: jest.fn(), + getAllAlertsAttachToCase: jest.fn(), + getCaseCommentStats: jest.fn(), + getAttachmentIdsForCases: jest.fn(), + }; + + return service as unknown as AttachmentGetterServiceMock; +}; + +type FakeAttachmentService = PublicMethodsOf & AttachmentServiceOperations; + +export const createAttachmentServiceMock = (): AttachmentServiceMock => { + const service: FakeAttachmentService = { + getter: createAttachmentGetterServiceMock(), delete: jest.fn(), create: jest.fn(), bulkCreate: jest.fn(), update: jest.fn(), bulkUpdate: jest.fn(), find: jest.fn(), - getAllAlertsAttachToCase: jest.fn(), countAlertsAttachedToCase: jest.fn(), executeCaseActionsAggregations: jest.fn(), - getCaseCommentStats: jest.fn(), valueCountAlertsAttachedToCase: jest.fn(), executeCaseAggregations: jest.fn(), - getAttachmentIdsForCases: jest.fn(), }; // the cast here is required because jest.Mocked tries to include private members and would throw an error diff --git a/x-pack/plugins/cases/server/services/so_references.ts b/x-pack/plugins/cases/server/services/so_references.ts index d5dcc50da03e3..f85c4cab1829d 100644 --- a/x-pack/plugins/cases/server/services/so_references.ts +++ b/x-pack/plugins/cases/server/services/so_references.ts @@ -22,6 +22,7 @@ import { } from '../attachment_framework/so_references'; import { EXTERNAL_REFERENCE_REF_NAME } from '../common/constants'; import { isCommentRequestTypeExternalReferenceSO } from '../common/utils'; +import type { PartialField } from '../types'; import { SOReferenceExtractor } from './so_reference_extractor'; export const getAttachmentSOExtractor = (attachment: Partial) => { @@ -38,6 +39,30 @@ export const getAttachmentSOExtractor = (attachment: Partial) => return new SOReferenceExtractor(fieldsToExtract); }; +type OptionalAttributes = PartialField, 'attributes'>; + +/** + * This function should be used when the attributes field could be undefined. Specifically when + * performing a bulkGet within the core saved object library. If one of the requested ids does not exist in elasticsearch + * then the error field will be set and attributes will be undefined. + */ +export const injectAttachmentAttributesAndHandleErrors = ( + savedObject: OptionalAttributes, + persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry +): OptionalAttributes => { + if (!hasAttributes(savedObject)) { + // we don't actually have an attributes field here so the type doesn't matter, this cast is to get the types to stop + // complaining though + return savedObject as OptionalAttributes; + } + + return injectAttachmentSOAttributesFromRefs(savedObject, persistableStateAttachmentTypeRegistry); +}; + +const hasAttributes = (savedObject: OptionalAttributes): savedObject is SavedObject => { + return savedObject.error == null && savedObject.attributes != null; +}; + export const injectAttachmentSOAttributesFromRefs = ( savedObject: SavedObject, persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts index a96f113305444..159cb40a5f1f8 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.ts @@ -88,6 +88,16 @@ interface ConnectorFieldsBeforePushAggsResult { }; } +interface UserActionsStatsAggsResult { + total: number; + totals: { + buckets: Array<{ + key: string; + doc_count: number; + }>; + }; +} + export class CaseUserActionService { private readonly _creator: UserActionPersister; private readonly _finder: UserActionFinder; @@ -661,4 +671,48 @@ export class CaseUserActionService { }, }; } + + public async getCaseUserActionStats({ caseId }: { caseId: string }) { + const response = await this.context.unsecuredSavedObjectsClient.find< + CaseUserActionAttributesWithoutConnectorId, + UserActionsStatsAggsResult + >({ + type: CASE_USER_ACTION_SAVED_OBJECT, + hasReference: { type: CASE_SAVED_OBJECT, id: caseId }, + page: 1, + perPage: 1, + sortField: defaultSortField, + aggs: CaseUserActionService.buildUserActionStatsAgg(), + }); + + const result = { + total: response.total, + total_comments: 0, + total_other_actions: 0, + }; + + response.aggregations?.totals.buckets.forEach(({ key, doc_count: docCount }) => { + if (key === 'user') { + result.total_comments = docCount; + } + }); + + result.total_other_actions = result.total - result.total_comments; + + return result; + } + + private static buildUserActionStatsAgg(): Record< + string, + estypes.AggregationsAggregationContainer + > { + return { + totals: { + terms: { + field: `${CASE_USER_ACTION_SAVED_OBJECT}.attributes.payload.comment.type`, + size: 100, + }, + }, + }; + } } diff --git a/x-pack/plugins/cases/server/services/user_actions/test_utils.ts b/x-pack/plugins/cases/server/services/user_actions/test_utils.ts index 335905cb116ce..49d47b13f8d04 100644 --- a/x-pack/plugins/cases/server/services/user_actions/test_utils.ts +++ b/x-pack/plugins/cases/server/services/user_actions/test_utils.ts @@ -19,7 +19,7 @@ import { SECURITY_SOLUTION_OWNER, } from '../../../common/constants'; import type { - CaseUserActionAttributes, + CaseUserActionAttributesWithoutConnectorId, ConnectorUserAction, UserAction, } from '../../../common/api'; @@ -45,15 +45,15 @@ import type { PersistableStateAttachmentTypeRegistry } from '../../attachment_fr import { transformFindResponseToExternalModel } from './transform'; export const createUserActionFindSO = ( - userAction: SavedObject -): SavedObjectsFindResult => ({ + userAction: SavedObject +): SavedObjectsFindResult => ({ ...userAction, score: 0, }); export const createConnectorUserAction = ( - overrides?: Partial -): SavedObject => { + overrides?: Partial +): SavedObject => { const { id, ...restConnector } = createConnectorObject().connector; return { ...createUserActionSO({ @@ -79,12 +79,12 @@ export const createUserActionSO = ({ action: UserAction; type?: string; payload?: Record; - attributesOverrides?: Partial; + attributesOverrides?: Partial; commentId?: string; connectorId?: string; pushedConnectorId?: string; references?: SavedObjectReference[]; -}): SavedObject => { +}): SavedObject => { const defaultParams = { action, created_at: 'abc', @@ -140,14 +140,14 @@ export const createUserActionSO = ({ ] : []), ], - } as SavedObject; + } as SavedObject; }; export const updateConnectorUserAction = ({ overrides, }: { - overrides?: Partial; -} = {}): SavedObject => { + overrides?: Partial; +} = {}): SavedObject => { const { id, ...restConnector } = createJiraConnector(); return { ...createUserActionSO({ @@ -163,8 +163,8 @@ export const updateConnectorUserAction = ({ export const pushConnectorUserAction = ({ overrides, }: { - overrides?: Partial; -} = {}): SavedObject => { + overrides?: Partial; +} = {}): SavedObject => { const { connector_id: connectorId, ...restExternalService } = createExternalService(); return { ...createUserActionSO({ @@ -177,7 +177,7 @@ export const pushConnectorUserAction = ({ }; }; -export const createCaseUserAction = (): SavedObject => { +export const createCaseUserAction = (): SavedObject => { const { id, ...restConnector } = createJiraConnector(); return { ...createUserActionSO({ @@ -231,7 +231,7 @@ export const createExternalReferenceUserAction = () => { export const testConnectorId = ( persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry, - userAction: SavedObject, + userAction: SavedObject, path: string, expectedConnectorId = '1' ) => { @@ -252,7 +252,9 @@ export const testConnectorId = ( }; const transformed = transformFindResponseToExternalModel( createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), + createUserActionFindSO( + invalidUserAction as SavedObject + ), ]), persistableStateAttachmentTypeRegistry ); @@ -267,7 +269,9 @@ export const testConnectorId = ( }; const transformed = transformFindResponseToExternalModel( createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), + createUserActionFindSO( + invalidUserAction as SavedObject + ), ]), persistableStateAttachmentTypeRegistry ) as SavedObjectsFindResponse; diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.ts b/x-pack/plugins/cases/server/services/user_actions/transform.ts index 4af9873c5cc5b..9d7225660fbe4 100644 --- a/x-pack/plugins/cases/server/services/user_actions/transform.ts +++ b/x-pack/plugins/cases/server/services/user_actions/transform.ts @@ -113,7 +113,7 @@ function legacyTransformToExternalModel( } const addReferenceIdToPayload = ( - userAction: SavedObject, + userAction: SavedObject, persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry ): CaseUserActionAttributes['payload'] => { const connectorId = getConnectorIdFromReferences(userAction); @@ -175,7 +175,7 @@ const addReferenceIdToPayload = ( }; function getConnectorIdFromReferences( - userAction: SavedObject + userAction: SavedObject ): string | null { const { references } = userAction; diff --git a/x-pack/plugins/cases/server/types.ts b/x-pack/plugins/cases/server/types.ts index 3c92a9a16cdbc..ba6d64dc88668 100644 --- a/x-pack/plugins/cases/server/types.ts +++ b/x-pack/plugins/cases/server/types.ts @@ -56,3 +56,5 @@ export interface PluginStartContract { export interface PluginSetupContract { attachmentFramework: AttachmentFramework; } + +export type PartialField = Omit & Partial>; diff --git a/x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts b/x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts index 53e619b0d50be..15bcfdc53512f 100644 --- a/x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts +++ b/x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @@ -18,7 +18,11 @@ const configSchema = schema.object({ schema.maybe(schema.string()) ), eventTypesAllowlist: schema.arrayOf(schema.string(), { - defaultValue: ['Loaded Kibana'], + defaultValue: [ + 'Loaded Kibana', // Sent once per page refresh (potentially, once per session) + 'Hosts View Query Submitted', // Worst-case scenario 1 every 2 seconds + 'Host Entry Clicked', // Worst-case scenario once per second - AT RISK, + ], }), }); diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts index 0711bd371828a..567c763f5d845 100644 --- a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts +++ b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts @@ -35,6 +35,7 @@ export const cspRuleTemplateMetadataSchemaV870 = rt.object({ id: rt.string(), version: rt.string(), rule_number: rt.maybe(rt.string()), + posture_type: rt.maybe(rt.string()), }), default_value: rt.maybe(rt.string()), description: rt.string(), diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.test.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.test.ts new file mode 100644 index 0000000000000..616fa533e7c52 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.test.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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-hooks/dom'; +import { useNavigateFindings, useNavigateFindingsByResource } from './use_navigate_findings'; +import { useHistory } from 'react-router-dom'; + +jest.mock('react-router-dom', () => ({ + useHistory: jest.fn().mockReturnValue({ push: jest.fn() }), +})); + +jest.mock('./use_kibana', () => ({ + useKibana: jest.fn().mockReturnValue({ + services: { + data: { + query: { + queryString: { + getDefaultQuery: jest.fn().mockReturnValue({ + language: 'kuery', + query: '', + }), + }, + }, + }, + }, + }), +})); + +describe('useNavigateFindings', () => { + it('creates a URL to findings page with correct path and filter', () => { + const push = jest.fn(); + (useHistory as jest.Mock).mockReturnValueOnce({ push }); + + const filter = { foo: 1 }; + + const { result } = renderHook(() => useNavigateFindings()); + + act(() => { + result.current({ filter }); + }); + + expect(push).toHaveBeenCalledWith({ + pathname: '/cloud_security_posture/findings/default', + search: + "cspq=(filters:!((meta:(alias:!n,disabled:!f,key:filter,negate:!f,params:(query:(foo:1)),type:phrase),query:(match_phrase:(filter:(foo:1))))),query:(language:kuery,query:''))", + }); + expect(push).toHaveBeenCalledTimes(1); + }); + it('creates a URL to findings resource page with correct path and filter', () => { + const push = jest.fn(); + (useHistory as jest.Mock).mockReturnValueOnce({ push }); + + const filter = { foo: 1 }; + + const { result } = renderHook(() => useNavigateFindingsByResource()); + + act(() => { + result.current({ filter }); + }); + + expect(push).toHaveBeenCalledWith({ + pathname: '/cloud_security_posture/findings/resource', + search: + "cspq=(filters:!((meta:(alias:!n,disabled:!f,key:filter,negate:!f,params:(query:(foo:1)),type:phrase),query:(match_phrase:(filter:(foo:1))))),query:(language:kuery,query:''))", + }); + expect(push).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts index b9a408116b0b6..946941b181292 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts @@ -5,61 +5,46 @@ * 2.0. */ +import { useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import { Query } from '@kbn/es-query'; +import { Filter } from '@kbn/es-query'; import { findingsNavigation } from '../navigation/constants'; import { encodeQuery } from '../navigation/query_utils'; -import { FindingsBaseURLQuery } from '../../pages/findings/types'; +import { useKibana } from './use_kibana'; -const getFindingsQuery = (queryValue: Query['query']): Pick => { - const query = - typeof queryValue === 'string' - ? queryValue - : // TODO: use a tested query builder instead ASAP - Object.entries(queryValue) - .reduce((a, [key, value]) => { - a.push(`${key}: "${value}"`); - return a; - }, []) - .join(' and '); +const createFilter = (key: string, value: string, negate = false): Filter => ({ + meta: { + alias: null, + negate, + disabled: false, + type: 'phrase', + key, + params: { query: value }, + }, + query: { match_phrase: { [key]: value } }, +}); - return { - query: { - language: 'kuery', - // NOTE: a query object is valid TS but throws on runtime - query, - }, - }!; -}; - -export const useNavigateFindings = () => { +const useNavigate = (pathname: string) => { const history = useHistory(); + const { services } = useKibana(); - return (query: Query['query'] = {}) => { - history.push({ - pathname: findingsNavigation.findings_default.path, - ...(query && { + return useCallback( + (filterParams: Record = {}) => { + const filters = Object.entries(filterParams).map(([key, value]) => createFilter(key, value)); + history.push({ + pathname, search: encodeQuery({ - ...getFindingsQuery(query), - filters: [], + // Set query language from user's preference + query: services.data.query.queryString.getDefaultQuery(), + filters, }), - }), - }); - }; + }); + }, + [pathname, history, services.data.query.queryString] + ); }; -export const useNavigateFindingsByResource = () => { - const history = useHistory(); +export const useNavigateFindings = () => useNavigate(findingsNavigation.findings_default.path); - return (query: Query['query'] = {}) => { - history.push({ - pathname: findingsNavigation.findings_by_resource.path, - ...(query && { - search: encodeQuery({ - ...getFindingsQuery(query), - filters: [], - }), - }), - }); - }; -}; +export const useNavigateFindingsByResource = () => + useNavigate(findingsNavigation.findings_by_resource.path); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form.tsx index 3585e63cf5143..ca96f0010448c 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form.tsx @@ -12,6 +12,7 @@ import { EuiLink, EuiSpacer, EuiText, + EuiTitle, } from '@elastic/eui'; import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; import { NewPackagePolicyInput } from '@kbn/fleet-plugin/common'; @@ -20,6 +21,29 @@ import { i18n } from '@kbn/i18n'; import { RadioGroup } from './csp_boxed_radio_group'; import { getPosturePolicy, NewPackagePolicyPostureInput } from './utils'; +const AWSSetupInfoContent = () => ( + <> + + +

      + +

      +
      + + + + + +); + const DocsLink = ( -
      ); @@ -61,7 +84,6 @@ const DirectAccessKeysDescription = ( defaultMessage="Access keys are long-term credentials for an IAM user or the AWS account root user." /> -

); @@ -75,7 +97,6 @@ const TemporaryKeysDescription = ( found using GetSessionToken." /> -
); @@ -88,7 +109,6 @@ const SharedCredentialsDescription = ( to define multiple access keys in the same configuration file." /> - ); @@ -170,6 +190,7 @@ const options: AwsOptions = { }; export type AwsCredentialsType = keyof typeof options; +export const DEFAULT_AWS_VARS_GROUP: AwsCredentialsType = 'assume_role'; const AWS_CREDENTIALS_OPTIONS = Object.keys(options).map((value) => ({ id: value as AwsCredentialsType, label: options[value as keyof typeof options].label, @@ -209,6 +230,7 @@ export const AwsCredentialsForm = ({ input, newPolicy, updatePolicy }: Props) => return ( <> + /> {group.info} + {DocsLink} onChange(id as AwsCredentialsType)} /> ); + const AwsInputVarFields = ({ fields, onChange, diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts index ecc435aa5961d..ceefc2a2b6333 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts @@ -5,153 +5,19 @@ * 2.0. */ import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; -import type { PackagePolicy } from '@kbn/fleet-plugin/common'; import { createNewPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; -import { BenchmarkId } from '../../../common/types'; -import { CLOUDBEAT_EKS, CLOUDBEAT_VANILLA } from '../../../common/constants'; +import { + CLOUDBEAT_GCP, + CLOUDBEAT_AZURE, + CLOUDBEAT_EKS, + CLOUDBEAT_VANILLA, + CLOUDBEAT_AWS, +} from '../../../common/constants'; import type { PostureInput } from '../../../common/types'; -export const getCspNewPolicyMock = (type: BenchmarkId = 'cis_k8s'): NewPackagePolicy => ({ - name: 'some-cloud_security_posture-policy', - description: '', - namespace: 'default', - policy_id: '', - enabled: true, - inputs: [ - { - type: CLOUDBEAT_VANILLA, - policy_template: 'kspm', - enabled: type === 'cis_k8s', - streams: [ - { - enabled: type === 'cis_k8s', - data_stream: { - type: 'logs', - dataset: 'cloud_security_posture.findings', - }, - }, - ], - }, - { - type: CLOUDBEAT_EKS, - policy_template: 'kspm', - enabled: type === 'cis_eks', - streams: [ - { - enabled: type === 'cis_eks', - data_stream: { - type: 'logs', - dataset: 'cloud_security_posture.findings', - }, - vars: { - access_key_id: { - type: 'text', - }, - secret_access_key: { - type: 'text', - }, - session_token: { - type: 'text', - }, - shared_credential_file: { - type: 'text', - }, - credential_profile_name: { - type: 'text', - }, - role_arn: { - type: 'text', - }, - }, - }, - ], - }, - ], - package: { - name: 'cloud_security_posture', - title: 'Kubernetes Security Posture Management', - version: '0.0.21', - }, - vars: { - runtimeCfg: { - type: 'yaml', - }, - }, -}); - -export const getCspPolicyMock = (type: BenchmarkId = 'cis_k8s'): PackagePolicy => ({ - ...getCspNewPolicyMock(type), - id: 'c6d16e42-c32d-4dce-8a88-113cfe276ad1', - version: 'abcd', - revision: 1, - updated_at: '2020-06-25T16:03:38.159292', - updated_by: 'kibana', - created_at: '2020-06-25T16:03:38.159292', - created_by: 'kibana', - inputs: [ - { - policy_template: 'kspm', - streams: [ - { - compiled_stream: { - data_yaml: { - activated_rules: { - cis_k8s: [], - cis_eks: ['cis_3_1_4'], - }, - }, - name: 'Findings', - processors: [{ add_cluster_id: null }], - }, - data_stream: { - type: 'logs', - dataset: 'cloud_security_posture.findings', - }, - id: 'cloudbeat/vanilla-cloud_security_posture.findings-de97ed6f-5024-46af-a4f9-9acd7bd012d8', - enabled: true, - }, - ], - type: CLOUDBEAT_VANILLA, - enabled: type === 'cis_k8s', - }, - { - policy_template: 'kspm', - streams: [ - { - data_stream: { - type: 'logs', - dataset: 'cloud_security_posture.findings', - }, - vars: { - access_key_id: { - type: 'text', - }, - session_token: { - type: 'text', - }, - secret_access_key: { - type: 'text', - }, - }, - id: 'cloudbeat/eks-cloud_security_posture.findings-de97ed6f-5024-46af-a4f9-9acd7bd012d8', - enabled: false, - }, - ], - type: CLOUDBEAT_EKS, - enabled: type === 'cis_eks', - }, - ], - vars: { - dataYaml: { - type: 'yaml', - value: 'data_yaml:\n activated_rules:\n cis_k8s: []\n cis_eks:\n - cis_3_1_4\n ', - }, - }, -}); - -export const getMockPolicyAWS = () => getPolicyMock('cloudbeat/cis_aws'); -export const getMockPolicyK8s = () => getPolicyMock('cloudbeat/cis_k8s'); -export const getMockPolicyEKS = () => getPolicyMock('cloudbeat/cis_eks'); +export const getMockPolicyAWS = () => getPolicyMock(CLOUDBEAT_AWS); +export const getMockPolicyK8s = () => getPolicyMock(CLOUDBEAT_VANILLA); +export const getMockPolicyEKS = () => getPolicyMock(CLOUDBEAT_EKS); const getPolicyMock = (type: PostureInput): NewPackagePolicy => { const mockPackagePolicy = createNewPackagePolicyMock(); @@ -178,42 +44,38 @@ const getPolicyMock = (type: PostureInput): NewPackagePolicy => { }, vars: { posture: { - value: type === 'cloudbeat/cis_k8s' || type === 'cloudbeat/cis_eks' ? 'kspm' : 'cspm', + value: type === CLOUDBEAT_VANILLA || type === CLOUDBEAT_EKS ? 'kspm' : 'cspm', type: 'text', }, deployment: { value: type, type: 'text' }, }, inputs: [ { - type: 'cloudbeat/cis_k8s', + type: CLOUDBEAT_VANILLA, policy_template: 'kspm', - enabled: type === 'cloudbeat/cis_k8s', - streams: [{ enabled: type === 'cloudbeat/cis_k8s', data_stream: dataStream }], + enabled: type === CLOUDBEAT_VANILLA, + streams: [{ enabled: type === CLOUDBEAT_VANILLA, data_stream: dataStream }], }, { - type: 'cloudbeat/cis_eks', + type: CLOUDBEAT_EKS, policy_template: 'kspm', - enabled: type === 'cloudbeat/cis_eks', - streams: [ - { enabled: type === 'cloudbeat/cis_eks', data_stream: dataStream, vars: awsVarsMock }, - ], + enabled: type === CLOUDBEAT_EKS, + streams: [{ enabled: type === CLOUDBEAT_EKS, data_stream: dataStream, vars: awsVarsMock }], }, { - type: 'cloudbeat/cis_aws', + type: CLOUDBEAT_AWS, policy_template: 'cspm', - enabled: type === 'cloudbeat/cis_aws', - streams: [ - { enabled: type === 'cloudbeat/cis_aws', data_stream: dataStream, vars: awsVarsMock }, - ], + enabled: type === CLOUDBEAT_AWS, + streams: [{ enabled: type === CLOUDBEAT_AWS, data_stream: dataStream, vars: awsVarsMock }], }, { - type: 'cloudbeat/cis_gcp', + type: CLOUDBEAT_GCP, policy_template: 'cspm', enabled: false, streams: [{ enabled: false, data_stream: dataStream }], }, { - type: 'cloudbeat/cis_azure', + type: CLOUDBEAT_AZURE, policy_template: 'cspm', enabled: false, streams: [{ enabled: false, data_stream: dataStream }], diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_extension_create.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_extension_create.tsx deleted file mode 100644 index b4d3828dd97b8..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_extension_create.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React, { memo } from 'react'; -import type { PackagePolicyCreateExtensionComponentProps } from '@kbn/fleet-plugin/public'; -import { CspPolicyTemplateForm } from './policy_template_form'; - -export const CspCreatePolicyExtension = memo( - ({ newPolicy, onChange }) => ( - - ) -); - -CspCreatePolicyExtension.displayName = 'CspCreatePolicyExtension'; - -// eslint-disable-next-line import/no-default-export -export { CspCreatePolicyExtension as default }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_extension_edit.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_extension_edit.tsx deleted file mode 100644 index b2be1b0c0d7cc..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_extension_edit.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React, { memo } from 'react'; -import type { PackagePolicyEditExtensionComponentProps } from '@kbn/fleet-plugin/public'; -import { CspPolicyTemplateForm } from './policy_template_form'; - -export const CspEditPolicyExtension = memo( - ({ newPolicy, onChange }) => ( - - ) -); - -CspEditPolicyExtension.displayName = 'CspEditPolicyExtension'; - -// eslint-disable-next-line import/no-default-export -export { CspEditPolicyExtension as default }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx index 0d4ab59bd31b9..37d4a13e8a532 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx @@ -9,7 +9,7 @@ import { render } from '@testing-library/react'; import { CspPolicyTemplateForm } from './policy_template_form'; import { TestProvider } from '../../test/test_provider'; import { getMockPolicyAWS, getMockPolicyEKS, getMockPolicyK8s } from './mocks'; -import type { NewPackagePolicy } from '@kbn/fleet-plugin/common'; +import type { NewPackagePolicy, PackageInfo, PackagePolicy } from '@kbn/fleet-plugin/common'; import userEvent from '@testing-library/user-event'; import { getPosturePolicy } from './utils'; import { CLOUDBEAT_AWS, CLOUDBEAT_EKS } from '../../../common/constants'; @@ -25,7 +25,23 @@ describe('', () => { newPolicy: NewPackagePolicy; }) => ( - + {edit && ( + + )} + {!edit && ( + + )} ); @@ -33,6 +49,38 @@ describe('', () => { onChange.mockClear(); }); + it('renders and updates name field', () => { + const policy = getMockPolicyK8s(); + const { getByLabelText } = render(); + const name = getByLabelText('Name'); + expect(name).toBeInTheDocument(); + + userEvent.type(name, '1'); + + // Listen to the 2nd triggered by the test. + // The 1st is done on mount to ensure initial state is valid. + expect(onChange).toHaveBeenNthCalledWith(2, { + isValid: true, + updatedPolicy: { ...policy, name: `${policy.name}1` }, + }); + }); + + it('renders and updates description field', () => { + const policy = getMockPolicyK8s(); + const { getByLabelText } = render(); + const description = getByLabelText('Description'); + expect(description).toBeInTheDocument(); + + userEvent.type(description, '1'); + + // Listen to the 2nd triggered by the test. + // The 1st is done on mount to ensure initial state is valid. + expect(onChange).toHaveBeenNthCalledWith(2, { + isValid: true, + updatedPolicy: { ...policy, description: `${policy.description}1` }, + }); + }); + it('renders KSPM input selector', () => { const { getByLabelText } = render(); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index eee8f86ef6b48..da77b3965ed19 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -5,87 +5,157 @@ * 2.0. */ import React, { memo, useEffect } from 'react'; -import { EuiSpacer } from '@elastic/eui'; -import type { - NewPackagePolicy, - PackagePolicyCreateExtensionComponentProps, -} from '@kbn/fleet-plugin/public'; -import type { PostureInput } from '../../../common/types'; +import { EuiFieldText, EuiFormRow, EuiSpacer, EuiTitle } from '@elastic/eui'; +import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { PackagePolicyReplaceDefineStepExtensionComponentProps } from '@kbn/fleet-plugin/public/types'; +import { useParams } from 'react-router-dom'; +import type { PostureInput, PosturePolicyTemplate } from '../../../common/types'; import { CLOUDBEAT_AWS, CLOUDBEAT_VANILLA } from '../../../common/constants'; +import { getPosturePolicy, getEnabledPostureInput, getPostureInputHiddenVars } from './utils'; import { - getPosturePolicy, - INPUTS_WITH_AWS_VARS, - getEnabledPostureInput, - type NewPackagePolicyPostureInput, -} from './utils'; -import { AwsCredentialsForm, type AwsCredentialsType } from './aws_credentials_form'; -import { PolicyInputSelector } from './policy_template_input_selector'; + PolicyTemplateInfo, + PolicyTemplateInputSelector, + PolicyTemplateSelector, + PolicyTemplateVarsForm, +} from './policy_template_selectors'; const DEFAULT_INPUT_TYPE = { kspm: CLOUDBEAT_VANILLA, cspm: CLOUDBEAT_AWS, } as const; -const DEFAULT_AWS_VARS_GROUP: AwsCredentialsType = 'assume_role'; +const EditScreenStepTitle = () => ( + <> + +

+ +

+
+ + +); -interface Props extends PackagePolicyCreateExtensionComponentProps { - edit?: boolean; +interface IntegrationInfoFieldsProps { + fields: Array<{ id: string; value: string; label: React.ReactNode; error: string[] | null }>; + onChange(field: string, value: string): void; } -interface PolicyVarsFormProps { - newPolicy: NewPackagePolicy; - input: NewPackagePolicyPostureInput; - updatePolicy(updatedPolicy: NewPackagePolicy): void; -} +const IntegrationSettings = ({ onChange, fields }: IntegrationInfoFieldsProps) => ( +
+ {fields.map(({ value, id, label, error }) => ( + + onChange(id, event.target.value)} + /> + + ))} +
+); -const PolicyVarsForm = ({ input, ...props }: PolicyVarsFormProps) => { - switch (input.type) { - case 'cloudbeat/cis_aws': - case 'cloudbeat/cis_eks': - return ; - default: - return null; - } -}; - -export const CspPolicyTemplateForm = memo(({ newPolicy, onChange, edit }) => { - const input = getEnabledPostureInput(newPolicy); - - const updatePolicy = (updatedPolicy: NewPackagePolicy) => - onChange({ - isValid: true, - updatedPolicy, - }); - - /** - * - Updates policy inputs by user selection - * - Updates hidden policy vars - */ - const setEnabledPolicyInput = (inputType: PostureInput) => - updatePolicy( - getPosturePolicy( - newPolicy, - inputType, - INPUTS_WITH_AWS_VARS.includes(inputType) - ? { 'aws.credentials.type': { value: DEFAULT_AWS_VARS_GROUP } } - : undefined - ) +export const CspPolicyTemplateForm = memo( + ({ newPolicy, onChange, validationResults, isEditPage }) => { + const { integration } = useParams<{ integration: PosturePolicyTemplate }>(); + const input = getEnabledPostureInput(newPolicy); + + const updatePolicy = (updatedPolicy: NewPackagePolicy) => + onChange({ isValid: true, updatedPolicy }); + + /** + * - Updates policy inputs by user selection + * - Updates hidden policy vars + */ + const setEnabledPolicyInput = (inputType: PostureInput) => { + const inputVars = getPostureInputHiddenVars(inputType); + const policy = getPosturePolicy(newPolicy, inputType, inputVars); + updatePolicy(policy); + }; + + const integrationFields = [ + { + id: 'name', + value: newPolicy.name, + error: validationResults?.name || null, + label: ( + + ), + }, + { + id: 'description', + value: newPolicy.description || '', + error: validationResults?.description || null, + label: ( + + ), + }, + ]; + + useEffect(() => { + if (isEditPage) return; + + // Pick default input type for policy template. + // Only 1 enabled input is supported when all inputs are initially enabled. + setEnabledPolicyInput(DEFAULT_INPUT_TYPE[input.policy_template]); + + // Required for mount only to ensure a single input type is selected + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isEditPage]); + + return ( +
+ {isEditPage && } + + {/* Defines the enabled policy template */} + {!integration && ( + <> + setEnabledPolicyInput(DEFAULT_INPUT_TYPE[template])} + disabled={isEditPage} + /> + + + )} + + {/* Shows info on the active policy template */} + + + + {/* Defines the single enabled input of the active policy template */} + + + + {/* Defines the name/description */} + updatePolicy({ ...newPolicy, [field]: value })} + /> + {/* Defines the vars of the enabled input of the active policy template */} + + +
); + } +); + +CspPolicyTemplateForm.displayName = 'CspPolicyTemplateForm'; - useEffect(() => { - // Pick default input type for policy template. - // Only 1 enabled input is supported when all inputs are initially enabled. - if (!edit) setEnabledPolicyInput(DEFAULT_INPUT_TYPE[input.policy_template]); - - // Required for mount only to ensure a single input type is selected - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [edit]); - - return ( -
- - - -
- ); -}); +// eslint-disable-next-line import/no-default-export +export { CspPolicyTemplateForm as default }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_input_selector.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_input_selector.tsx deleted file mode 100644 index 34d00a95d6899..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_input_selector.tsx +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React from 'react'; -import { EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { PostureInput, PosturePolicyTemplate } from '../../../common/types'; -import { getPolicyTemplateInputOptions, type NewPackagePolicyPostureInput } from './utils'; -import { RadioGroup } from './csp_boxed_radio_group'; - -interface Props { - disabled: boolean; - input: NewPackagePolicyPostureInput; - setInput: (inputType: PostureInput) => void; -} - -export const PolicyInputSelector = ({ input, disabled, setInput }: Props) => { - const baseOptions = getPolicyTemplateInputOptions(input.policy_template); - const options = baseOptions.map((option) => ({ - ...option, - disabled: option.disabled || disabled, - label: option.label, - icon: option.icon, - })); - - return ( -
- - setInput(inputType as PostureInput)} - size="m" - /> - -
- ); -}; - -const PolicyInputInfo = ({ type }: { type: PostureInput }) => { - switch (type) { - case 'cloudbeat/cis_aws': - case 'cloudbeat/cis_eks': - return ; - default: - return null; - } -}; - -const AWSSetupInfoContent = () => ( - <> - - -

- -

-
- - - - - -); - -const ConfigureIntegrationInfo = ({ type }: { type: PosturePolicyTemplate }) => ( - <> - -

- -

-
- - - {type === 'kspm' && ( - - )} - {type === 'cspm' && ( - - )} - - - -); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx new file mode 100644 index 0000000000000..083c700c6f6e1 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { EuiSpacer, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { NewPackagePolicy } from '@kbn/fleet-plugin/common'; +import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '../../../common/constants'; +import type { PostureInput, PosturePolicyTemplate } from '../../../common/types'; +import { getPolicyTemplateInputOptions, type NewPackagePolicyPostureInput } from './utils'; +import { RadioGroup } from './csp_boxed_radio_group'; +import { AwsCredentialsForm } from './aws_credentials_form'; + +interface PolicyTemplateSelectorProps { + selectedTemplate: PosturePolicyTemplate; + policy: NewPackagePolicy; + setPolicyTemplate(template: PosturePolicyTemplate): void; + disabled: boolean; +} + +export const PolicyTemplateSelector = ({ + policy, + selectedTemplate, + setPolicyTemplate, + disabled, +}: PolicyTemplateSelectorProps) => { + const policyTemplates = new Set(policy.inputs.map((input) => input.policy_template!)); + + return ( +
+ + + + + ({ id: v, label: v.toUpperCase() }))} + idSelected={selectedTemplate} + onChange={(id) => setPolicyTemplate(id as PosturePolicyTemplate)} + disabled={disabled} + /> +
+ ); +}; + +interface PolicyTemplateVarsFormProps { + newPolicy: NewPackagePolicy; + input: NewPackagePolicyPostureInput; + updatePolicy(updatedPolicy: NewPackagePolicy): void; +} + +export const PolicyTemplateVarsForm = ({ input, ...props }: PolicyTemplateVarsFormProps) => { + switch (input.type) { + case 'cloudbeat/cis_aws': + case 'cloudbeat/cis_eks': + return ; + default: + return null; + } +}; + +interface PolicyTemplateInfoProps { + postureType: PosturePolicyTemplate; +} + +export const PolicyTemplateInfo = ({ postureType }: PolicyTemplateInfoProps) => ( + + {postureType === KSPM_POLICY_TEMPLATE && ( + + )} + {postureType === CSPM_POLICY_TEMPLATE && ( + + )} + +); + +interface Props { + disabled: boolean; + input: NewPackagePolicyPostureInput; + setInput: (inputType: PostureInput) => void; +} + +export const PolicyTemplateInputSelector = ({ input, disabled, setInput }: Props) => { + const baseOptions = getPolicyTemplateInputOptions(input.policy_template); + const options = baseOptions.map((option) => ({ + ...option, + disabled: option.disabled || disabled, + label: option.label, + icon: option.icon, + })); + + return ( + setInput(inputType as PostureInput)} + size="m" + /> + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts new file mode 100644 index 0000000000000..aaed63a91da0a --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.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 { getPostureInputHiddenVars, getPosturePolicy } from './utils'; +import { getMockPolicyAWS, getMockPolicyK8s, getMockPolicyEKS } from './mocks'; + +describe('getPosturePolicy', () => { + for (const [name, getPolicy, expectedVars] of [ + ['cloudbeat/cis_aws', getMockPolicyAWS, { 'aws.credentials.type': { value: 'assume_role' } }], + ['cloudbeat/cis_eks', getMockPolicyEKS, { 'aws.credentials.type': { value: 'assume_role' } }], + ['cloudbeat/cis_k8s', getMockPolicyK8s, null], + ] as const) { + it(`updates package policy with hidden vars for ${name}`, () => { + const inputVars = getPostureInputHiddenVars(name); + const policy = getPosturePolicy(getPolicy(), name, inputVars); + + const enabledInputs = policy.inputs.filter( + (i) => i.type === name && i.enabled && i.streams.some((s) => s.enabled) + ); + + expect(enabledInputs.length).toBe(1); + if (expectedVars) expect(enabledInputs[0].streams[0].vars).toMatchObject({ ...expectedVars }); + else expect(enabledInputs[0].streams[0].vars).toBe(undefined); + }); + } + + it('updates package policy required vars (posture/deployment)', () => { + const mockCisAws = getMockPolicyAWS(); + expect(mockCisAws.vars?.posture.value).toBe('cspm'); + mockCisAws.vars!.extra = { value: 'value' }; + + const policy = getPosturePolicy(mockCisAws, 'cloudbeat/cis_k8s'); + expect(policy.vars?.posture.value).toBe('kspm'); + expect(policy.vars?.deployment.value).toBe('cloudbeat/cis_k8s'); + + // Does not change extra vars + expect(policy.vars?.extra.value).toBe('value'); + }); + + it('updates package policy with a single enabled input', () => { + const mockCisAws = getMockPolicyAWS(); + expect(mockCisAws.inputs.filter((i) => i.enabled).length).toBe(1); + expect(mockCisAws.inputs.filter((i) => i.enabled)[0].type).toBe('cloudbeat/cis_aws'); + + // enable all inputs of a policy + mockCisAws.inputs = mockCisAws.inputs.map((i) => ({ + ...i, + enabled: true, + streams: i.streams.map((s) => ({ ...s, enabled: true })), + })); + + // change input + const policy = getPosturePolicy(mockCisAws, 'cloudbeat/cis_k8s'); + const enabledInputs = policy.inputs.filter( + (i) => i.enabled && i.streams.some((s) => s.enabled) + ); + + expect(enabledInputs.length).toBe(1); + expect(enabledInputs.map((v) => v.type)[0]).toBe('cloudbeat/cis_k8s'); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index afbd7ffac9bf8..2d8f670b5a0fd 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -19,12 +19,11 @@ import { SUPPORTED_POLICY_TEMPLATES, SUPPORTED_CLOUDBEAT_INPUTS, } from '../../../common/constants'; +import { DEFAULT_AWS_VARS_GROUP } from './aws_credentials_form'; import type { PostureInput, PosturePolicyTemplate } from '../../../common/types'; import { assert } from '../../../common/utils/helpers'; import { cloudPostureIntegrations } from '../../common/constants'; -export const INPUTS_WITH_AWS_VARS = [CLOUDBEAT_EKS, CLOUDBEAT_AWS]; - type PosturePolicyInput = | { type: typeof CLOUDBEAT_AZURE; policy_template: 'cspm' } | { type: typeof CLOUDBEAT_GCP; policy_template: 'cspm' } @@ -60,7 +59,9 @@ const getPostureInput = ( // Merge new vars with existing vars ...(isInputEnabled && stream.vars && - inputVars && { vars: merge({}, stream.vars, inputVars) }), + inputVars && { + vars: merge({}, stream.vars, inputVars), + }), })), }; }; @@ -83,6 +84,19 @@ export const getPosturePolicy = ( }), }); +/** + * Input vars that are hidden from the user + */ +export const getPostureInputHiddenVars = (inputType: PostureInput) => { + switch (inputType) { + case 'cloudbeat/cis_aws': + case 'cloudbeat/cis_eks': + return { 'aws.credentials.type': { value: DEFAULT_AWS_VARS_GROUP } }; + default: + return undefined; + } +}; + export const getPolicyTemplateInputOptions = (policyTemplate: PosturePolicyTemplate) => cloudPostureIntegrations[policyTemplate].options.map((o) => ({ tooltip: o.tooltip, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx index f082171b22da1..968db8feb53b2 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx @@ -11,65 +11,11 @@ import * as TEST_SUBJECTS from '../test_subjects'; import { FindingsTable } from './latest_findings_table'; import type { PropsOf } from '@elastic/eui'; import Chance from 'chance'; -import type { EcsEvent } from '@kbn/ecs'; import { TestProvider } from '../../../test/test_provider'; -import { CspFinding } from '../../../../common/schemas/csp_finding'; +import { getFindingsFixture } from '../../../test/fixtures/findings_fixture'; const chance = new Chance(); -const getFakeFindings = (name: string): CspFinding & { id: string } => ({ - cluster_id: chance.guid(), - id: chance.word(), - result: { - expected: { - source: {}, - }, - evaluation: chance.weighted(['passed', 'failed'], [0.5, 0.5]), - evidence: { - filemode: chance.word(), - }, - }, - rule: { - audit: chance.paragraph(), - benchmark: { - rule_number: '1.1.1', - name: 'CIS Kubernetes', - version: '1.6.0', - id: 'cis_k8s', - }, - default_value: chance.sentence(), - description: chance.paragraph(), - id: chance.guid(), - impact: chance.word(), - name, - profile_applicability: chance.sentence(), - rationale: chance.paragraph(), - references: chance.paragraph(), - rego_rule_id: 'cis_X_X_X', - remediation: chance.word(), - section: chance.sentence(), - tags: [], - version: '1.0', - }, - agent: { - id: chance.string(), - name: chance.string(), - type: chance.string(), - version: chance.string(), - }, - resource: { - name: chance.string(), - type: chance.string(), - raw: {} as any, - sub_type: chance.string(), - id: chance.string(), - }, - host: {} as any, - ecs: {} as any, - event: {} as EcsEvent, - '@timestamp': new Date().toISOString(), -}); - type TableProps = PropsOf; describe('', () => { @@ -96,7 +42,7 @@ describe('', () => { it('renders the table with provided items', () => { const names = chance.unique(chance.sentence, 10); - const data = names.map(getFakeFindings); + const data = names.map(getFindingsFixture); const props: TableProps = { loading: false, @@ -120,7 +66,7 @@ describe('', () => { it('adds filter with a cell button click', () => { const names = chance.unique(chance.sentence, 10); - const data = names.map(getFakeFindings); + const data = names.map(getFindingsFixture); const filterProps = { onAddFilter: jest.fn() }; const props: TableProps = { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx index 630edd6cb8dcf..fb7ce817246a9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx @@ -57,13 +57,28 @@ const FindingsByResourceTableComponent = ({ 'data-test-subj': TEST_SUBJECTS.getFindingsByResourceTableRowTestId(getResourceId(row)), }); + const getNonSortableColumn = (column: EuiTableFieldDataColumnType) => ({ + ...column, + sortable: false, + }); + const columns = useMemo( () => [ - findingsByResourceColumns.resource_id, - createColumnWithFilters(findingsByResourceColumns['resource.sub_type'], { onAddFilter }), - createColumnWithFilters(findingsByResourceColumns['resource.name'], { onAddFilter }), - createColumnWithFilters(findingsByResourceColumns['rule.benchmark.name'], { onAddFilter }), - createColumnWithFilters(findingsByResourceColumns.cluster_id, { onAddFilter }), + getNonSortableColumn(findingsByResourceColumns.resource_id), + createColumnWithFilters( + getNonSortableColumn(findingsByResourceColumns['resource.sub_type']), + { onAddFilter } + ), + createColumnWithFilters(getNonSortableColumn(findingsByResourceColumns['resource.name']), { + onAddFilter, + }), + createColumnWithFilters( + getNonSortableColumn(findingsByResourceColumns['rule.benchmark.name']), + { onAddFilter } + ), + createColumnWithFilters(getNonSortableColumn(findingsByResourceColumns.cluster_id), { + onAddFilter, + }), findingsByResourceColumns.compliance_score, ], [onAddFilter] diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx index 05cc930a314f8..66a561e676098 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx @@ -135,7 +135,6 @@ export const ResourceFindings = ({ dataView }: FindingsBaseProps) => { }), }); }; - return (
{ title={i18n.translate( 'xpack.csp.findings.resourceFindings.resourceFindingsPageTitle', { - defaultMessage: '{resourceName} - Findings', - values: { resourceName: resourceFindings.data?.resourceName }, + defaultMessage: '{resourceName} {hyphen} Findings', + values: { + resourceName: resourceFindings.data?.resourceName, + hyphen: resourceFindings.data?.resourceName ? '-' : '', + }, } )} /> @@ -167,9 +169,9 @@ export const ResourceFindings = ({ dataView }: FindingsBaseProps) => { ) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.test.tsx new file mode 100644 index 0000000000000..101eb9d6ae67c --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render, screen, within } from '@testing-library/react'; +import * as TEST_SUBJECTS from '../../test_subjects'; +import { ResourceFindingsTable, ResourceFindingsTableProps } from './resource_findings_table'; +import { TestProvider } from '../../../../test/test_provider'; + +import { capitalize } from 'lodash'; +import moment from 'moment'; +import { getFindingsFixture } from '../../../../test/fixtures/findings_fixture'; + +describe('', () => { + it('should render no findings empty state when status success and data has a length of zero ', async () => { + const resourceFindingsProps: ResourceFindingsTableProps = { + loading: false, + items: [], + pagination: { pageIndex: 0, pageSize: 10, totalItemCount: 0 }, + sorting: { + sort: { field: '@timestamp', direction: 'desc' }, + }, + setTableOptions: jest.fn(), + onAddFilter: jest.fn(), + }; + + render( + + + + ); + + expect( + screen.getByTestId(TEST_SUBJECTS.RESOURCES_FINDINGS_TABLE_EMPTY_STATE) + ).toBeInTheDocument(); + }); + + it('should render resource finding table content when data has a non zero length', () => { + const data = Array.from({ length: 10 }, getFindingsFixture); + + const props: ResourceFindingsTableProps = { + loading: false, + items: data, + pagination: { pageIndex: 0, pageSize: 10, totalItemCount: 0 }, + sorting: { + sort: { field: 'cluster_id', direction: 'desc' }, + }, + setTableOptions: jest.fn(), + onAddFilter: jest.fn(), + }; + + render( + + + + ); + + data.forEach((item, i) => { + const row = screen.getByTestId( + TEST_SUBJECTS.getResourceFindingsTableRowTestId(item.resource.id) + ); + const { evaluation } = item.result; + const evaluationStatusText = capitalize( + item.result.evaluation.slice(0, evaluation.length - 2) + ); + + expect(row).toBeInTheDocument(); + expect(within(row).queryByText(item.rule.name)).toBeInTheDocument(); + expect(within(row).queryByText(evaluationStatusText)).toBeInTheDocument(); + expect(within(row).queryByText(moment(item['@timestamp']).fromNow())).toBeInTheDocument(); + expect(within(row).queryByText(item.rule.section)).toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx index 41f18af723b8e..50a9c19c6b6ab 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx @@ -25,8 +25,9 @@ import { } from '../../layout/findings_layout'; import { FindingsRuleFlyout } from '../../findings_flyout/findings_flyout'; import { getSelectedRowStyle } from '../../utils/utils'; +import * as TEST_SUBJECTS from '../../test_subjects'; -interface Props { +export interface ResourceFindingsTableProps { items: CspFinding[]; loading: boolean; pagination: Pagination; @@ -42,12 +43,13 @@ const ResourceFindingsTableComponent = ({ sorting, setTableOptions, onAddFilter, -}: Props) => { +}: ResourceFindingsTableProps) => { const { euiTheme } = useEuiTheme(); const [selectedFinding, setSelectedFinding] = useState(); const getRowProps = (row: CspFinding) => ({ style: getSelectedRowStyle(euiTheme, row, selectedFinding), + 'data-test-subj': TEST_SUBJECTS.getResourceFindingsTableRowTestId(row.resource.id), }); const columns: [ @@ -69,6 +71,7 @@ const ResourceFindingsTableComponent = ({ return ( + estypes.AggregationsMultiBucketAggregateBase< + estypes.AggregationsStringRareTermsBucketKeys | undefined + > >; const getResourceFindingsQuery = ({ @@ -92,19 +94,18 @@ export const useResourceFindings = (options: UseResourceFindingsOptions) => { keepPreviousData: true, select: ({ rawResponse: { hits, aggregations } }: ResourceFindingsResponse) => { if (!aggregations) throw new Error('expected aggregations to exists'); - - assertNonEmptyArray(aggregations.count.buckets); - assertNonEmptyArray(aggregations.clusterId.buckets); - assertNonEmptyArray(aggregations.resourceSubType.buckets); - assertNonEmptyArray(aggregations.resourceName.buckets); + assertNonBucketsArray(aggregations.count?.buckets); + assertNonBucketsArray(aggregations.clusterId?.buckets); + assertNonBucketsArray(aggregations.resourceSubType?.buckets); + assertNonBucketsArray(aggregations.resourceName?.buckets); return { page: hits.hits.map((hit) => hit._source!), total: number.is(hits.total) ? hits.total : 0, - count: getAggregationCount(aggregations.count.buckets), - clusterId: getFirstBucketKey(aggregations.clusterId.buckets), - resourceSubType: getFirstBucketKey(aggregations.resourceSubType.buckets), - resourceName: getFirstBucketKey(aggregations.resourceName.buckets), + count: getAggregationCount(aggregations.count?.buckets), + clusterId: getFirstBucketKey(aggregations.clusterId?.buckets), + resourceSubType: getFirstBucketKey(aggregations.resourceSubType?.buckets), + resourceName: getFirstBucketKey(aggregations.resourceName?.buckets), }; }, onError: (err: Error) => showErrorToast(toasts, err), @@ -112,11 +113,12 @@ export const useResourceFindings = (options: UseResourceFindingsOptions) => { ); }; -function assertNonEmptyArray(arr: unknown): asserts arr is T[] { - if (!Array.isArray(arr) || arr.length === 0) { - throw new Error('expected a non empty array'); +function assertNonBucketsArray(arr: unknown): asserts arr is T[] { + if (!Array.isArray(arr)) { + throw new Error('expected buckets to be an array'); } } -const getFirstBucketKey = (buckets: estypes.AggregationsStringRareTermsBucketKeys[]) => - buckets[0].key; +const getFirstBucketKey = ( + buckets: Array +): string | undefined => buckets[0]?.key; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/test_subjects.ts index 28c15e4913800..153811865c5c3 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/test_subjects.ts @@ -21,5 +21,10 @@ export const getFindingsTableCellTestId = (columnId: string, rowId: string) => export const FINDINGS_TABLE_CELL_ADD_FILTER = 'findings_table_cell_add_filter'; export const FINDINGS_TABLE_CELL_ADD_NEGATED_FILTER = 'findings_table_cell_add_negated_filter'; +export const RESOURCES_FINDINGS_TABLE_EMPTY_STATE = 'resource_findings_table_empty_state'; +export const RESOURCES_FINDINGS_TABLE = 'resource_findings_table'; +export const getResourceFindingsTableRowTestId = (id: string) => + `resource_findings_table_row_${id}`; + export const DASHBOARD_TABLE_HEADER_SCORE_TEST_ID = 'csp:dashboard-sections-table-header-score'; export const DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID = 'csp:dashboard-sections-table-column-score'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils/utils.ts index 984f0d2dfcaf8..32cb914b446e2 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils/utils.ts @@ -120,9 +120,11 @@ export const getFindingsCountAggQuery = () => ({ count: { terms: { field: 'result.evaluation' } }, }); -export const getAggregationCount = (buckets: estypes.AggregationsStringRareTermsBucketKeys[]) => { - const passed = buckets.find((bucket) => bucket.key === 'passed'); - const failed = buckets.find((bucket) => bucket.key === 'failed'); +export const getAggregationCount = ( + buckets: Array +) => { + const passed = buckets.find((bucket) => bucket?.key === 'passed'); + const failed = buckets.find((bucket) => bucket?.key === 'failed'); return { passed: passed?.doc_count || 0, diff --git a/x-pack/plugins/cloud_security_posture/public/plugin.tsx b/x-pack/plugins/cloud_security_posture/public/plugin.tsx index 3644bd19d49b7..9114dcd9e2f4d 100755 --- a/x-pack/plugins/cloud_security_posture/public/plugin.tsx +++ b/x-pack/plugins/cloud_security_posture/public/plugin.tsx @@ -19,10 +19,10 @@ import type { import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME } from '../common/constants'; import { SetupContext } from './application/setup_context'; -const LazyCspEditPolicy = lazy(() => import('./components/fleet_extensions/policy_extension_edit')); -const LazyCspCreatePolicy = lazy( - () => import('./components/fleet_extensions/policy_extension_create') +const LazyCspPolicyTemplateForm = lazy( + () => import('./components/fleet_extensions/policy_template_form') ); + const LazyCspCustomAssets = lazy( () => import('./components/fleet_extensions/custom_assets_extension') ); @@ -57,14 +57,8 @@ export class CspPlugin public start(core: CoreStart, plugins: CspClientPluginStartDeps): CspClientPluginStart { plugins.fleet.registerExtension({ package: CLOUD_SECURITY_POSTURE_PACKAGE_NAME, - view: 'package-policy-create', - Component: LazyCspCreatePolicy, - }); - - plugins.fleet.registerExtension({ - package: CLOUD_SECURITY_POSTURE_PACKAGE_NAME, - view: 'package-policy-edit', - Component: LazyCspEditPolicy, + view: 'package-policy-replace-define-step', + Component: LazyCspPolicyTemplateForm, }); plugins.fleet.registerExtension({ diff --git a/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.ts b/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.ts new file mode 100644 index 0000000000000..f2c325787f9f6 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.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 { EcsEvent } from '@kbn/ecs'; +import Chance from 'chance'; +import { CspFinding } from '../../../common/schemas/csp_finding'; + +const chance = new Chance(); + +export const getFindingsFixture = (): CspFinding & { id: string } => ({ + cluster_id: chance.guid(), + id: chance.word(), + result: { + expected: { + source: {}, + }, + evaluation: chance.weighted(['passed', 'failed'], [0.5, 0.5]), + evidence: { + filemode: chance.word(), + }, + }, + rule: { + audit: chance.paragraph(), + benchmark: { + name: 'CIS Kubernetes', + version: '1.6.0', + id: 'cis_k8s', + }, + default_value: chance.sentence(), + description: chance.paragraph(), + id: chance.guid(), + impact: chance.word(), + name: chance.string(), + profile_applicability: chance.sentence(), + rationale: chance.paragraph(), + references: chance.paragraph(), + rego_rule_id: 'cis_X_X_X', + remediation: chance.word(), + section: chance.sentence(), + tags: [], + version: '1.0', + }, + agent: { + id: chance.string(), + name: chance.string(), + type: chance.string(), + version: chance.string(), + }, + resource: { + name: chance.string(), + type: chance.string(), + raw: {} as any, + sub_type: chance.string(), + id: chance.string(), + }, + host: {} as any, + ecs: {} as any, + event: {} as EcsEvent, + '@timestamp': new Date().toISOString(), +}); diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts index 17c08cf3006e9..4a3b85b41a159 100644 --- a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { coreMock, httpServerMock } from '@kbn/core/server/mocks'; +import { + coreMock, + elasticsearchServiceMock, + httpServerMock, + savedObjectsClientMock, +} from '@kbn/core/server/mocks'; import { createPackagePolicyServiceMock, createArtifactsClientMock, @@ -43,6 +48,7 @@ import { } from '@kbn/core/server'; import { securityMock } from '@kbn/security-plugin/server/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; +import * as onPackagePolicyPostCreateCallback from './fleet_integration/fleet_integration'; const chance = new Chance(); @@ -147,12 +153,18 @@ describe('Cloud Security Posture Plugin', () => { }); it('should initialize when new package is created', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; fleetMock.packageService.asInternalUser.getInstallation.mockImplementationOnce( async (): Promise => { return; } ); + const onPackagePolicyPostCreateCallbackSpy = jest + .spyOn(onPackagePolicyPostCreateCallback, 'onPackagePolicyPostCreateCallback') + .mockResolvedValue(); + const packageMock = createPackagePolicyMock(); packageMock.package!.name = CLOUD_SECURITY_POSTURE_PACKAGE_NAME; @@ -172,19 +184,30 @@ describe('Cloud Security Posture Plugin', () => { await mockPlugins.fleet.fleetSetupCompleted(); // Assert + expect(onPackagePolicyPostCreateCallbackSpy).not.toHaveBeenCalled(); expect(fleetMock.packageService.asInternalUser.getInstallation).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(0); expect(packagePolicyPostCreateCallbacks.length).toBeGreaterThan(0); for (const cb of packagePolicyPostCreateCallbacks) { - await cb(packageMock, contextMock, httpServerMock.createKibanaRequest()); + await cb( + packageMock, + soClient, + esClient, + contextMock, + httpServerMock.createKibanaRequest() + ); } + expect(onPackagePolicyPostCreateCallbackSpy).toHaveBeenCalled(); expect(spy).toHaveBeenCalledTimes(1); }); it('should not initialize when other package is created', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + fleetMock.packageService.asInternalUser.getInstallation.mockImplementationOnce( async (): Promise => { return; @@ -216,7 +239,13 @@ describe('Cloud Security Posture Plugin', () => { expect(packagePolicyPostCreateCallbacks.length).toBeGreaterThan(0); for (const cb of packagePolicyPostCreateCallbacks) { - await cb(packageMock, contextMock, httpServerMock.createKibanaRequest()); + await cb( + packageMock, + soClient, + esClient, + contextMock, + httpServerMock.createKibanaRequest() + ); } expect(spy).toHaveBeenCalledTimes(0); @@ -266,9 +295,14 @@ describe('Cloud Security Posture Plugin', () => { expect(packagePolicyPostCreateCallbacks.length).toBeGreaterThan(0); + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + for (const cb of packagePolicyPostCreateCallbacks) { const updatedPackagePolicy = await cb( packageMock, + soClient, + esClient, contextMock, httpServerMock.createKibanaRequest() ); @@ -284,6 +318,9 @@ describe('Cloud Security Posture Plugin', () => { ])( 'should uninstall resources when package is removed', async (total, items, expectedNumberOfCallsToUninstallResources) => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + fleetMock.packagePolicyService.list.mockImplementationOnce( async (): Promise> => { return { @@ -320,7 +357,7 @@ describe('Cloud Security Posture Plugin', () => { expect(packagePolicyPostDeleteCallbacks.length).toBeGreaterThan(0); for (const cb of packagePolicyPostDeleteCallbacks) { - await cb(deletedPackagePolicyMock); + await cb(deletedPackagePolicyMock, soClient, esClient); } expect(fleetMock.packagePolicyService.list).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(expectedNumberOfCallsToUninstallResources); diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.ts b/x-pack/plugins/cloud_security_posture/server/plugin.ts index 027f92b293d78..7ab5923bcf71a 100755 --- a/x-pack/plugins/cloud_security_posture/server/plugin.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.ts @@ -6,13 +6,12 @@ */ import type { - KibanaRequest, - RequestHandlerContext, PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger, + SavedObjectsClientContract, } from '@kbn/core/server'; import type { DeepReadonly } from 'utility-types'; import type { @@ -107,11 +106,7 @@ export class CspPlugin plugins.fleet.registerExternalCallback( 'packagePolicyCreate', - async ( - packagePolicy: NewPackagePolicy, - _context: RequestHandlerContext, - _request: KibanaRequest - ): Promise => { + async (packagePolicy: NewPackagePolicy): Promise => { const license = await plugins.licensing.refresh(); if (isCspPackage(packagePolicy.package?.name)) { if (!isSubscriptionAllowed(this.isCloudEnabled, license)) { @@ -119,6 +114,10 @@ export class CspPlugin 'To use this feature you must upgrade your subscription or start a trial' ); } + + if (!isSingleEnabledInput(packagePolicy.inputs)) { + throw new Error('Only one enabled input is allowed per policy'); + } } return packagePolicy; @@ -129,12 +128,10 @@ export class CspPlugin 'packagePolicyPostCreate', async ( packagePolicy: PackagePolicy, - context: RequestHandlerContext, - _: KibanaRequest + soClient: SavedObjectsClientContract ): Promise => { if (isCspPackage(packagePolicy.package?.name)) { await this.initialize(core, plugins.taskManager); - const soClient = (await context.core).savedObjects.client; await onPackagePolicyPostCreateCallback(this.logger, packagePolicy, soClient); return packagePolicy; @@ -194,3 +191,6 @@ export class CspPlugin setupFindingsStatsTask(taskManager, coreStartServices, logger); } } + +const isSingleEnabledInput = (inputs: NewPackagePolicy['inputs']): boolean => + inputs.filter((i) => i.enabled).length === 1; diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts index 470ec21cddc7b..f6f59b21b4a68 100644 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts @@ -43,9 +43,19 @@ function migrateCspRuleTemplatesToV870( ): SavedObjectUnsanitizedDoc { // Keeps only metadata, deprecated state const { muted, enabled, ...attributes } = doc.attributes; + return { ...doc, - attributes, + attributes: { + metadata: { + ...attributes.metadata, + benchmark: { + ...attributes.metadata.benchmark, + // CSPM introduced in 8.7, so we can assume all docs from 8.4.0 are KSPM + posture_type: 'kspm', + }, + }, + }, }; } diff --git a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js index 75d860bcf75a6..f5b8c48d97a67 100644 --- a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js +++ b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js @@ -309,8 +309,7 @@ describe('', () => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/142774 - describe.skip('detail panel', () => { + describe('detail panel', () => { test('should open a detail panel when clicking on a follower index', async () => { expect(exists('followerIndexDetail')).toBe(false); @@ -334,7 +333,7 @@ describe('', () => { test('should have a "settings" section', async () => { await actions.clickFollowerIndexAt(0); expect(find('followerIndexDetail.settingsSection').find('h3').text()).toEqual('Settings'); - expect(exists('followerIndexDetail.settingsValues')).toBe(true); + expect(find('followerIndexDetail.settingsValues').length).toBeGreaterThan(0); }); test('should set the correct follower index settings values', async () => { diff --git a/x-pack/plugins/custom_branding/server/plugin.ts b/x-pack/plugins/custom_branding/server/plugin.ts index 6a9f6ad4829ec..1c6039ace92df 100755 --- a/x-pack/plugins/custom_branding/server/plugin.ts +++ b/x-pack/plugins/custom_branding/server/plugin.ts @@ -13,10 +13,12 @@ import { Logger, Plugin, PluginInitializerContext, + SECURITY_EXTENSION_ID, } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; import { License } from '@kbn/license-api-guard-plugin/server'; import { CustomBranding } from '@kbn/core-custom-branding-common'; +import { Subscription } from 'rxjs'; import { PLUGIN } from '../common/constants'; import type { CustomBrandingRequestHandlerContext } from './types'; import { Dependencies } from './types'; @@ -33,6 +35,8 @@ const settingsKeys: Array = [ export class CustomBrandingPlugin implements Plugin { private readonly license: License; private readonly logger: Logger; + private licensingSubscription?: Subscription; + private isValidLicense: boolean = false; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); @@ -48,14 +52,25 @@ export class CustomBrandingPlugin implements Plugin { const router = core.http.createRouter(); registerRoutes(router); - const fetchFn = async (request: KibanaRequest): Promise => { + const fetchFn = async ( + request: KibanaRequest, + unauthenticated: boolean + ): Promise => { + if (!this.isValidLicense) { + return {}; + } const [coreStart] = await core.getStartServices(); - const soClient = coreStart.savedObjects.getScopedClient(request); + const soClient = unauthenticated + ? coreStart.savedObjects.getScopedClient(request, { + excludedExtensions: [SECURITY_EXTENSION_ID], + }) + : coreStart.savedObjects.getScopedClient(request); const uiSettings = coreStart.uiSettings.globalAsScopedToClient(soClient); return await this.getBrandingFrom(uiSettings); }; core.customBranding.register(fetchFn); + return {}; } @@ -66,10 +81,15 @@ export class CustomBrandingPlugin implements Plugin { minimumLicenseType: PLUGIN.MINIMUM_LICENSE_REQUIRED, licensing, }); + this.licensingSubscription = licensing.license$.subscribe((next) => { + this.isValidLicense = next.hasAtLeast('enterprise'); + }); return {}; } - public stop() {} + public stop() { + this.licensingSubscription?.unsubscribe(); + } private getBrandingFrom = async (uiSettingsClient: IUiSettingsClient) => { const branding: CustomBranding = {}; diff --git a/x-pack/plugins/enterprise_search/common/__mocks__/initial_app_data.ts b/x-pack/plugins/enterprise_search/common/__mocks__/initial_app_data.ts index dd6dd58d02f70..02a9cf8175897 100644 --- a/x-pack/plugins/enterprise_search/common/__mocks__/initial_app_data.ts +++ b/x-pack/plugins/enterprise_search/common/__mocks__/initial_app_data.ts @@ -29,6 +29,7 @@ export const DEFAULT_INITIAL_APP_DATA = { }, access: { hasAppSearchAccess: true, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: true, }, appSearch: { diff --git a/x-pack/plugins/enterprise_search/common/types/connectors.ts b/x-pack/plugins/enterprise_search/common/types/connectors.ts index e9bc91892f4f6..87ccbad824e1f 100644 --- a/x-pack/plugins/enterprise_search/common/types/connectors.ts +++ b/x-pack/plugins/enterprise_search/common/types/connectors.ts @@ -17,6 +17,16 @@ export interface ConnectorScheduling { interval: string; } +export interface CustomScheduling { + configuration_overrides: Record; + enabled: boolean; + interval: string; + last_synced: string | null; + name: string; +} + +export type ConnectorCustomScheduling = Record; + export enum ConnectorStatus { CREATED = 'created', NEEDS_CONFIGURATION = 'needs_configuration', @@ -125,6 +135,7 @@ export type ConnectorFeatures = Partial<{ export interface Connector { api_key_id: string | null; configuration: ConnectorConfiguration; + custom_scheduling: ConnectorCustomScheduling; description: string | null; error: string | null; features: ConnectorFeatures; diff --git a/x-pack/plugins/enterprise_search/common/types/extraction_rules.ts b/x-pack/plugins/enterprise_search/common/types/extraction_rules.ts new file mode 100644 index 0000000000000..5bd33bc0af852 --- /dev/null +++ b/x-pack/plugins/enterprise_search/common/types/extraction_rules.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. + */ + +export enum FieldType { + HTML = 'html', + URL = 'url', +} + +export enum ExtractionFilter { + BEGINS = 'begins', + ENDS = 'ends', + CONTAINS = 'contains', + REGEX = 'regex', +} + +export enum ContentFrom { + FIXED = 'fixed', + EXTRACTED = 'extracted', +} + +export enum MultipleObjectsHandling { + ARRAY = 'array', + STRING = 'string', +} + +export interface ExtractionRuleFieldRule { + content_from: { + value: string | null; + value_type: ContentFrom; + }; + field_name: string; + multiple_objects_handling: MultipleObjectsHandling; + selector: string; + source_type: FieldType; +} + +export interface ExtractionRuleBase { + description: string; + rules: ExtractionRuleFieldRule[]; + url_filters: Array<{ filter: ExtractionFilter; pattern: string }>; +} + +export type ExtractionRule = ExtractionRuleBase & { + created_at: string; + domain_id: string; + edited_by: string; + id: string; + updated_at: string; +}; diff --git a/x-pack/plugins/enterprise_search/common/types/index.ts b/x-pack/plugins/enterprise_search/common/types/index.ts index 2e17862edaaf4..e1b0f2045893e 100644 --- a/x-pack/plugins/enterprise_search/common/types/index.ts +++ b/x-pack/plugins/enterprise_search/common/types/index.ts @@ -32,6 +32,7 @@ export interface ConfiguredLimits { export interface ProductAccess { hasAppSearchAccess: boolean; + hasSearchEnginesAccess: boolean; hasWorkplaceSearchAccess: boolean; } diff --git a/x-pack/plugins/enterprise_search/common/types/pagination.ts b/x-pack/plugins/enterprise_search/common/types/pagination.ts index 04f0fa6a46bc7..4dda0f9a4cfcb 100644 --- a/x-pack/plugins/enterprise_search/common/types/pagination.ts +++ b/x-pack/plugins/enterprise_search/common/types/pagination.ts @@ -5,11 +5,17 @@ * 2.0. */ +export interface Page { + from: number; // current page index, 0-based + has_more_hits_than_total?: boolean; + size: number; // size per page + total: number; // total number of hits +} +export interface Meta { + page: Page; +} + export interface Paginate { + _meta: Meta; data: T[]; - has_more_hits_than_total: boolean; - pageIndex: number; - pageSize: number; - size: number; - total: number; } diff --git a/x-pack/plugins/enterprise_search/common/ui_settings_keys.ts b/x-pack/plugins/enterprise_search/common/ui_settings_keys.ts index 9438c0a803f98..6a2a189e0cff9 100644 --- a/x-pack/plugins/enterprise_search/common/ui_settings_keys.ts +++ b/x-pack/plugins/enterprise_search/common/ui_settings_keys.ts @@ -6,4 +6,3 @@ */ export const enterpriseSearchFeatureId = 'enterpriseSearch'; -export const enableEnginesSection = 'enterpriseSearch:enableEnginesSection'; diff --git a/x-pack/plugins/enterprise_search/jest.sh b/x-pack/plugins/enterprise_search/jest.sh index b08459827fa7d..ea0e9f4739cba 100755 --- a/x-pack/plugins/enterprise_search/jest.sh +++ b/x-pack/plugins/enterprise_search/jest.sh @@ -25,4 +25,4 @@ fi # @see https://jestjs.io/docs/en/cli#options ARGS="${*:2}" -yarn test:jest $TARGET $ARGS +TZ="Etc/UTC" yarn test:jest $TARGET $ARGS diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts index dd654fb8357af..4689317cfd61a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts @@ -29,6 +29,7 @@ export const mockKibanaValues = { navigateToUrl: jest.fn(), productAccess: { hasAppSearchAccess: true, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: true, }, uiSettings: uiSettingsServiceMock.createStartContract(), diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts index 2555ef905caff..955cf219a8019 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts @@ -33,6 +33,15 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ connector: { api_key_id: null, configuration: { foo: { label: 'bar', value: 'barbar' } }, + custom_scheduling: { + foo: { + configuration_overrides: {}, + enabled: false, + interval: '', + last_synced: null, + name: '', + }, + }, description: null, error: null, features: null, @@ -119,6 +128,15 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ connector: { api_key_id: null, configuration: { foo: { label: 'bar', value: 'barbar' } }, + custom_scheduling: { + foo: { + configuration_overrides: {}, + enabled: false, + interval: '', + last_synced: null, + name: '', + }, + }, description: null, error: null, features: null, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts index 73e4a65874c05..78ef10664abcb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts @@ -43,6 +43,15 @@ export const connectorIndex: ConnectorViewIndex = { connector: { api_key_id: null, configuration: { foo: { label: 'bar', value: 'barbar' } }, + custom_scheduling: { + foo: { + configuration_overrides: {}, + enabled: false, + interval: '', + last_synced: null, + name: '', + }, + }, description: null, error: null, features: null, @@ -133,6 +142,15 @@ export const crawlerIndex: CrawlerViewIndex = { connector: { api_key_id: null, configuration: { foo: { label: 'bar', value: 'barbar' } }, + custom_scheduling: { + foo: { + configuration_overrides: {}, + enabled: false, + interval: '', + last_synced: null, + name: '', + }, + }, description: null, error: null, features: null, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.test.ts index eadad25b8342a..0b5322107694a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.test.ts @@ -24,18 +24,18 @@ describe('FetchSyncJobs', () => { await nextTick(); expect(http.get).toHaveBeenCalledWith( '/internal/enterprise_search/connectors/connectorId1/sync_jobs', - { query: { page: 0, size: 10 } } + { query: { from: 0, size: 10 } } ); await expect(result).resolves.toEqual('result'); }); it('appends query if specified', async () => { const promise = Promise.resolve('result'); http.get.mockReturnValue(promise); - const result = fetchSyncJobs({ connectorId: 'connectorId1', page: 10, size: 20 }); + const result = fetchSyncJobs({ connectorId: 'connectorId1', from: 10, size: 20 }); await nextTick(); expect(http.get).toHaveBeenCalledWith( '/internal/enterprise_search/connectors/connectorId1/sync_jobs', - { query: { page: 10, size: 20 } } + { query: { from: 10, size: 20 } } ); await expect(result).resolves.toEqual('result'); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.ts index a45bf5e0943ca..c6fb83b172e5e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/fetch_sync_jobs_api_logic.ts @@ -13,15 +13,15 @@ import { HttpLogic } from '../../../shared/http'; export interface FetchSyncJobsArgs { connectorId: string; - page?: number; + from?: number; size?: number; } export type FetchSyncJobsResponse = Paginate; -export const fetchSyncJobs = async ({ connectorId, page = 0, size = 10 }: FetchSyncJobsArgs) => { +export const fetchSyncJobs = async ({ connectorId, from = 0, size = 10 }: FetchSyncJobsArgs) => { const route = `/internal/enterprise_search/connectors/${connectorId}/sync_jobs`; - const query = { page, size }; + const query = { from, size }; return await HttpLogic.values.http.get>(route, { query }); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/_mocks_/crawler_domains.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/_mocks_/crawler_domains.mock.ts index 4b11aa699b081..ad6528ff600aa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/_mocks_/crawler_domains.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/_mocks_/crawler_domains.mock.ts @@ -69,6 +69,7 @@ export const CRAWLER_DOMAIN_FROM_SERVER: CrawlerDomainFromServer = { deduplication_fields: ['url'], document_count: 400, entry_points: [ENTRY_POINT], + extraction_rules: [], id: '123abc', name: 'https://www.elastic.co', sitemaps: [SITEMAP], @@ -101,6 +102,7 @@ export const CRAWLER_DOMAIN: CrawlerDomain = { deduplicationFields: ['url'], documentCount: 400, entryPoints: [ENTRY_POINT], + extractionRules: [], id: '123abc', sitemaps: [SITEMAP], url: 'https://www.elastic.co', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/add_extraction_rule_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/add_extraction_rule_api_logic.test.ts new file mode 100644 index 0000000000000..e564b2717e126 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/add_extraction_rule_api_logic.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. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockHttpValues } from '../../../../__mocks__/kea_logic'; + +import { addExtractionRule } from './add_extraction_rule_api_logic'; + +describe('AddExtractionRuleApiLogic', () => { + const { http } = mockHttpValues; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('addExtractionRule', () => { + it('calls correct api', async () => { + const domainId = 'domain-id'; + const indexName = 'elastic-crawler'; + const rule = { rules: 'fake' } as any; + http.post.mockReturnValue(Promise.resolve('result')); + + const result = addExtractionRule({ + domainId, + indexName, + rule, + }); + expect(http.post).toHaveBeenCalledWith( + `/internal/enterprise_search/indices/${indexName}/crawler/domains/${domainId}/extraction_rules`, + { body: JSON.stringify({ extraction_rule: rule }) } + ); + await expect(result).resolves.toEqual('result'); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/add_extraction_rule_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/add_extraction_rule_api_logic.ts new file mode 100644 index 0000000000000..5be30c66ead2f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/add_extraction_rule_api_logic.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ExtractionRule, + ExtractionRuleBase, +} from '../../../../../../common/types/extraction_rules'; +import { Actions } from '../../../../shared/api_logic/create_api_logic'; + +import { createApiLogic } from '../../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../../shared/http'; + +export interface AddExtractionRuleArgs { + domainId: string; + indexName: string; + rule: ExtractionRuleBase; +} + +export interface AddExtractionRuleResponse { + extraction_rules: ExtractionRule[]; +} + +export const addExtractionRule = async ({ + domainId, + indexName, + rule: { description, rules, url_filters: urlFilters }, +}: AddExtractionRuleArgs) => { + const route = `/internal/enterprise_search/indices/${indexName}/crawler/domains/${domainId}/extraction_rules`; + + const params = { + extraction_rule: { + description, + rules, + url_filters: urlFilters, + }, + }; + + return await HttpLogic.values.http.post(route, { + body: JSON.stringify(params), + }); +}; + +export const AddExtractionRuleApiLogic = createApiLogic( + ['add_extraction_rule_api_logic'], + addExtractionRule +); + +export type AddExtractionRuleActions = Actions; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/delete_extraction_rule_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/delete_extraction_rule_api_logic.test.ts new file mode 100644 index 0000000000000..5200a68df84dd --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/delete_extraction_rule_api_logic.test.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. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockHttpValues } from '../../../../__mocks__/kea_logic'; + +import { deleteExtractionRule } from './delete_extraction_rule_api_logic'; + +describe('DeleteExtractionRuleApiLogic', () => { + const { http } = mockHttpValues; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('deleteExtractionRule', () => { + it('calls correct api', async () => { + const domainId = 'domain-id'; + const indexName = 'elastic-crawler'; + const extractionRuleId = 'extraction_rule_id'; + http.delete.mockReturnValue(Promise.resolve('result')); + + const result = deleteExtractionRule({ + domainId, + extractionRuleId, + indexName, + }); + expect(http.delete).toHaveBeenCalledWith( + `/internal/enterprise_search/indices/${indexName}/crawler/domains/${domainId}/extraction_rules/${extractionRuleId}` + ); + await expect(result).resolves.toEqual('result'); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/delete_extraction_rule_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/delete_extraction_rule_api_logic.ts new file mode 100644 index 0000000000000..63b426a95c9ce --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/delete_extraction_rule_api_logic.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 { ExtractionRule } from '../../../../../../common/types/extraction_rules'; +import { Actions } from '../../../../shared/api_logic/create_api_logic'; + +import { createApiLogic } from '../../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../../shared/http'; + +export interface DeleteExtractionRuleArgs { + domainId: string; + extractionRuleId: string; + indexName: string; +} + +export interface DeleteExtractionRuleResponse { + extraction_rules: ExtractionRule[]; +} + +export const deleteExtractionRule = async ({ + domainId, + extractionRuleId, + indexName, +}: DeleteExtractionRuleArgs) => { + const route = `/internal/enterprise_search/indices/${indexName}/crawler/domains/${domainId}/extraction_rules/${extractionRuleId}`; + + return await HttpLogic.values.http.delete(route); +}; + +export const DeleteExtractionRuleApiLogic = createApiLogic( + ['delete_extraction_rule_api_logic'], + deleteExtractionRule +); + +export type DeleteExtractionRuleActions = Actions< + DeleteExtractionRuleArgs, + DeleteExtractionRuleResponse +>; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/fetch_extraction_rules_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/fetch_extraction_rules_api_logic.test.ts new file mode 100644 index 0000000000000..d7fb15501ddb6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/fetch_extraction_rules_api_logic.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 { mockHttpValues } from '../../../../__mocks__/kea_logic'; + +import { fetchExtractionRules } from './fetch_extraction_rules_api_logic'; + +describe('FetchExtractionRuleApiLogic', () => { + const { http } = mockHttpValues; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('fetchExtractionRule', () => { + it('calls correct api', async () => { + const domainId = 'domain-id'; + const indexName = 'elastic-crawler'; + http.get.mockReturnValue(Promise.resolve('result')); + + const result = fetchExtractionRules({ + domainId, + indexName, + }); + expect(http.get).toHaveBeenCalledWith( + `/internal/enterprise_search/indices/${indexName}/crawler/domains/${domainId}/extraction_rules` + ); + await expect(result).resolves.toEqual('result'); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/fetch_extraction_rules_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/fetch_extraction_rules_api_logic.ts new file mode 100644 index 0000000000000..903c4ebb3adfb --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/fetch_extraction_rules_api_logic.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 { ExtractionRule } from '../../../../../../common/types/extraction_rules'; +import { Actions } from '../../../../shared/api_logic/create_api_logic'; + +import { createApiLogic } from '../../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../../shared/http'; + +export interface FetchExtractionRulesArgs { + domainId: string; + indexName: string; +} + +export interface FetchExtractionRulesResponse { + extraction_rules: ExtractionRule[]; +} + +export const fetchExtractionRules = async ({ domainId, indexName }: FetchExtractionRulesArgs) => { + const route = `/internal/enterprise_search/indices/${indexName}/crawler/domains/${domainId}/extraction_rules`; + + return await HttpLogic.values.http.get(route); +}; + +export const FetchExtractionRulesApiLogic = createApiLogic( + ['fetch_extraction_rule_api_logic'], + fetchExtractionRules +); + +export type FetchExtractionRulesActions = Actions< + FetchExtractionRulesArgs, + FetchExtractionRulesResponse +>; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/update_extraction_rule_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/update_extraction_rule_api_logic.test.ts new file mode 100644 index 0000000000000..ed08a1e06776a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/update_extraction_rule_api_logic.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockHttpValues } from '../../../../__mocks__/kea_logic'; + +import { updateExtractionRule } from './update_extraction_rule_api_logic'; + +describe('UpdateExtractionRuleApiLogic', () => { + const { http } = mockHttpValues; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('updateExtractionRule', () => { + it('calls correct api', async () => { + const domainId = 'domain-id'; + const extractionRuleId = 'extraction_rule_id'; + const indexName = 'elastic-crawler'; + const rule = { + description: 'haha', + id: extractionRuleId, + rules: ['a'], + url_filters: ['b'], + } as any; + http.put.mockReturnValue(Promise.resolve('result')); + + const result = updateExtractionRule({ + domainId, + indexName, + rule, + }); + expect(http.put).toHaveBeenCalledWith( + `/internal/enterprise_search/indices/${indexName}/crawler/domains/${domainId}/extraction_rules/${extractionRuleId}`, + { + body: JSON.stringify({ + extraction_rule: { description: 'haha', rules: ['a'], url_filters: ['b'] }, + }), + } + ); + await expect(result).resolves.toEqual('result'); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/update_extraction_rule_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/update_extraction_rule_api_logic.ts new file mode 100644 index 0000000000000..f36e7833a024e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/extraction_rules/update_extraction_rule_api_logic.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 { + ExtractionRule, + ExtractionRuleBase, +} from '../../../../../../common/types/extraction_rules'; +import { Actions } from '../../../../shared/api_logic/create_api_logic'; + +import { createApiLogic } from '../../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../../shared/http'; + +export interface UpdateExtractionRuleArgs { + domainId: string; + indexName: string; + rule: ExtractionRule; +} + +export interface UpdateExtractionRuleResponse { + extraction_rules: ExtractionRule[]; +} + +export const updateExtractionRule = async ({ + domainId, + indexName, + rule, +}: UpdateExtractionRuleArgs) => { + const route = `/internal/enterprise_search/indices/${indexName}/crawler/domains/${domainId}/extraction_rules/${rule.id}`; + + const params: { extraction_rule: ExtractionRuleBase } = { + extraction_rule: { + description: rule.description, + rules: rule.rules, + url_filters: rule.url_filters, + }, + }; + + return await HttpLogic.values.http.put(route, { + body: JSON.stringify(params), + }); +}; + +export const UpdateExtractionRuleApiLogic = createApiLogic( + ['update_extraction_rule_api_logic'], + updateExtractionRule +); + +export type UpdateExtractionRuleActions = Actions< + UpdateExtractionRuleArgs, + UpdateExtractionRuleResponse +>; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/types.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/types.ts index ce941613e3587..4e6f4e2ff0c32 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/types.ts @@ -4,8 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { Meta } from '../../../../../common/types'; import { CrawlerStatus } from '../../../../../common/types/crawler'; +import { ExtractionRule } from '../../../../../common/types/extraction_rules'; // TODO remove this proxy export, which will affect a lot of files export { CrawlerStatus }; @@ -88,6 +90,7 @@ export interface CrawlerDomainFromServer { default_crawl_rule?: CrawlRule; document_count: number; entry_points: EntryPoint[]; + extraction_rules: ExtractionRule[]; id: string; last_visited_at?: string; name: string; @@ -179,6 +182,7 @@ export interface CrawlerDomain { defaultCrawlRule?: CrawlRule; documentCount: number; entryPoints: EntryPoint[]; + extractionRules: ExtractionRule[]; id: string; lastCrawl?: string; sitemaps: Sitemap[]; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/utils.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/utils.ts index 7886d349044c0..1de8addea5afb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/crawler/utils.ts @@ -44,6 +44,7 @@ export function crawlerDomainServerToClient(payload: CrawlerDomainFromServer): C default_crawl_rule: defaultCrawlRule, document_count: documentCount, entry_points: entryPoints, + extraction_rules: extractionRules, id, last_visited_at: lastCrawl, name, @@ -59,6 +60,7 @@ export function crawlerDomainServerToClient(payload: CrawlerDomainFromServer): C deduplicationFields, documentCount, entryPoints, + extractionRules, id, sitemaps, url: name, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/engines/fetch_engines_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/engines/fetch_engines_api_logic.ts index f29edf8ec4206..69e3b5f881666 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/engines/fetch_engines_api_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/engines/fetch_engines_api_logic.ts @@ -6,14 +6,13 @@ */ import { EnterpriseSearchEnginesResponse } from '../../../../../common/types/engines'; +import { Page } from '../../../../../common/types/pagination'; import { createApiLogic } from '../../../shared/api_logic/create_api_logic'; import { HttpLogic } from '../../../shared/http'; -import { Meta } from '../../components/engines/types'; - export interface EnginesListAPIArguments { - meta: Meta; + meta: Page; searchQuery?: string; } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx new file mode 100644 index 0000000000000..852e3698b968c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; + +import { useActions, useValues } from 'kea'; + +import { + EuiButton, + EuiButtonEmpty, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiFormRow, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { Status } from '../../../../../common/types/api'; +import { isNotNullish } from '../../../../../common/utils/is_not_nullish'; +import { getErrorsFromHttpResponse } from '../../../shared/flash_messages/handle_api_errors'; + +import { + IndicesSelectComboBox, + IndicesSelectComboBoxOption, + indexToOption, +} from '../engines/components/indices_select_combobox'; + +import { AddIndicesLogic } from './add_indices_logic'; + +export interface AddIndicesFlyoutProps { + onClose: () => void; +} + +export const AddIndicesFlyout: React.FC = ({ onClose }) => { + const { selectedIndices, updateEngineStatus, updateEngineError } = useValues(AddIndicesLogic); + const { setSelectedIndices, submitSelectedIndices } = useActions(AddIndicesLogic); + + const selectedOptions = useMemo(() => selectedIndices.map(indexToOption), [selectedIndices]); + const onIndicesChange = useCallback( + (options: IndicesSelectComboBoxOption[]) => { + setSelectedIndices(options.map(({ value }) => value).filter(isNotNullish)); + }, + [setSelectedIndices] + ); + + return ( + + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.engine.indices.addIndicesFlyout.title', + { defaultMessage: 'Add new indices' } + )} +

+
+ {updateEngineStatus === Status.ERROR && updateEngineError && ( + <> + + + {getErrorsFromHttpResponse(updateEngineError).map((errMessage, i) => ( +

{errMessage}

+ ))} +
+ + )} +
+ + + + + + + + + + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.indices.addIndicesFlyout.submitButton', + { defaultMessage: 'Add selected' } + )} + + + + + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.indices.addIndicesFlyout.cancelButton', + { defaultMessage: 'Cancel' } + )} + + + + +
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.test.ts new file mode 100644 index 0000000000000..cf09b0f80adb1 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.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 { LogicMounter } from '../../../__mocks__/kea_logic'; + +import { Status } from '../../../../../common/types/api'; +import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; + +import { AddIndicesLogic, AddIndicesLogicValues } from './add_indices_logic'; + +const DEFAULT_VALUES: AddIndicesLogicValues = { + selectedIndices: [], + updateEngineError: undefined, + updateEngineStatus: Status.IDLE, +}; + +const makeIndexData = (name: string): ElasticsearchIndexWithIngestion => ({ + count: 0, + hidden: false, + name, + total: { + docs: { count: 0, deleted: 0 }, + store: { size_in_bytes: 'n/a' }, + }, +}); + +describe('AddIndicesLogic', () => { + const { mount: mountAddIndicesLogic } = new LogicMounter(AddIndicesLogic); + const { mount: mountEngineIndicesLogic } = new LogicMounter(AddIndicesLogic); + + beforeEach(() => { + jest.clearAllMocks(); + jest.useRealTimers(); + + mountAddIndicesLogic(); + mountEngineIndicesLogic(); + }); + + it('has expected default values', () => { + expect(AddIndicesLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('actions', () => { + describe('setSelectedIndices', () => { + it('adds the indices to selectedIndices', () => { + AddIndicesLogic.actions.setSelectedIndices([ + makeIndexData('index-001'), + makeIndexData('index-002'), + ]); + + expect(AddIndicesLogic.values.selectedIndices).toEqual([ + makeIndexData('index-001'), + makeIndexData('index-002'), + ]); + }); + + it('replaces any existing indices', () => { + AddIndicesLogic.actions.setSelectedIndices([ + makeIndexData('index-001'), + makeIndexData('index-002'), + ]); + AddIndicesLogic.actions.setSelectedIndices([ + makeIndexData('index-003'), + makeIndexData('index-004'), + ]); + + expect(AddIndicesLogic.values.selectedIndices).toEqual([ + makeIndexData('index-003'), + makeIndexData('index-004'), + ]); + }); + }); + }); + + describe('listeners', () => { + describe('engineUpdated', () => { + it('closes the add indices flyout', () => { + jest.spyOn(AddIndicesLogic.actions, 'closeAddIndicesFlyout'); + + AddIndicesLogic.actions.engineUpdated({ + created: '1999-12-31T23:59:59Z', + indices: [], + name: 'engine-name', + updated: '1999-12-31T23:59:59Z', + }); + + expect(AddIndicesLogic.actions.closeAddIndicesFlyout).toHaveBeenCalledTimes(1); + }); + }); + + describe('submitSelectedIndices', () => { + it('does not make a request if there are no selectedIndices', () => { + jest.spyOn(AddIndicesLogic.actions, 'addIndicesToEngine'); + + AddIndicesLogic.actions.submitSelectedIndices(); + + expect(AddIndicesLogic.actions.addIndicesToEngine).toHaveBeenCalledTimes(0); + }); + + it('calls addIndicesToEngine when there are selectedIndices', () => { + jest.spyOn(AddIndicesLogic.actions, 'addIndicesToEngine'); + + AddIndicesLogic.actions.setSelectedIndices([ + makeIndexData('index-001'), + makeIndexData('index-002'), + ]); + AddIndicesLogic.actions.submitSelectedIndices(); + + expect(AddIndicesLogic.actions.addIndicesToEngine).toHaveBeenCalledTimes(1); + expect(AddIndicesLogic.actions.addIndicesToEngine).toHaveBeenCalledWith([ + 'index-001', + 'index-002', + ]); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.ts new file mode 100644 index 0000000000000..37e5cf43ebd00 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.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 { kea, MakeLogicType } from 'kea'; + +import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; + +import { UpdateEngineApiLogic } from '../../api/engines/update_engine_api_logic'; + +import { EngineIndicesLogic, EngineIndicesLogicActions } from './engine_indices_logic'; + +export interface AddIndicesLogicActions { + addIndicesToEngine: EngineIndicesLogicActions['addIndicesToEngine']; + closeAddIndicesFlyout: EngineIndicesLogicActions['closeAddIndicesFlyout']; + engineUpdated: EngineIndicesLogicActions['engineUpdated']; + setSelectedIndices: (indices: ElasticsearchIndexWithIngestion[]) => { + indices: ElasticsearchIndexWithIngestion[]; + }; + submitSelectedIndices: () => void; +} + +export interface AddIndicesLogicValues { + selectedIndices: ElasticsearchIndexWithIngestion[]; + updateEngineError: typeof UpdateEngineApiLogic.values.error | undefined; + updateEngineStatus: typeof UpdateEngineApiLogic.values.status; +} + +export const AddIndicesLogic = kea>({ + actions: { + setSelectedIndices: (indices: ElasticsearchIndexWithIngestion[]) => ({ indices }), + submitSelectedIndices: () => true, + }, + connect: { + actions: [EngineIndicesLogic, ['addIndicesToEngine', 'engineUpdated', 'closeAddIndicesFlyout']], + values: [UpdateEngineApiLogic, ['status as updateEngineStatus', 'error as updateEngineError']], + }, + listeners: ({ actions, values }) => ({ + engineUpdated: () => { + actions.closeAddIndicesFlyout(); + }, + submitSelectedIndices: () => { + const { selectedIndices } = values; + if (selectedIndices.length === 0) return; + + actions.addIndicesToEngine(selectedIndices.map(({ name }) => name)); + }, + }), + path: ['enterprise_search', 'content', 'add_indices_logic'], + reducers: { + selectedIndices: [ + [], + { + closeAddIndicesFlyout: () => [], + setSelectedIndices: (_, { indices }) => indices, + }, + ], + }, +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx index 00e14382afbb3..0bcdde5e47647 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx @@ -13,6 +13,8 @@ import { EuiBasicTableColumn, EuiButton, EuiConfirmModal, + EuiFlexGroup, + EuiFlexItem, EuiIcon, EuiInMemoryTable, EuiText, @@ -26,20 +28,25 @@ import { indexHealthToHealthColor } from '../../../shared/constants/health_color import { generateEncodedPath } from '../../../shared/encode_path_params'; import { KibanaLogic } from '../../../shared/kibana'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; + import { SEARCH_INDEX_PATH, EngineViewTabs } from '../../routes'; import { IngestionMethod } from '../../types'; import { ingestionMethodToText } from '../../utils/indices'; + import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template'; +import { AddIndicesFlyout } from './add_indices_flyout'; import { EngineIndicesLogic } from './engine_indices_logic'; -import { EngineViewLogic } from './engine_view_logic'; +import { EngineViewHeaderActions } from './engine_view_header_actions'; export const EngineIndices: React.FC = () => { - const { engineName, isLoadingEngine } = useValues(EngineViewLogic); - const { engineData } = useValues(EngineIndicesLogic); - const { removeIndexFromEngine } = useActions(EngineIndicesLogic); + const { engineData, engineName, isLoadingEngine, addIndicesFlyoutOpen } = + useValues(EngineIndicesLogic); + const { removeIndexFromEngine, openAddIndicesFlyout, closeAddIndicesFlyout } = + useActions(EngineIndicesLogic); const { navigateToUrl } = useValues(KibanaLogic); const [removeIndexConfirm, setConfirmRemoveIndex] = useState(null); + if (!engineData) return null; const { indices } = engineData; @@ -170,11 +177,26 @@ export const EngineIndices: React.FC = () => { defaultMessage: 'Indices', }), rightSideItems: [ - - {i18n.translate('xpack.enterpriseSearch.content.engine.indices.addNewIndicesButton', { - defaultMessage: 'Add new indices', - })} - , + + + + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.indices.addNewIndicesButton', + { + defaultMessage: 'Add new indices', + } + )} + + + + + + , ], }} engineName={engineName} @@ -231,6 +253,7 @@ export const EngineIndices: React.FC = () => { )} + {addIndicesFlyoutOpen && } ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.test.ts index e0b29154d9fba..bf088988ed125 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.test.ts @@ -13,8 +13,10 @@ import { FetchEngineApiLogic } from '../../api/engines/fetch_engine_api_logic'; import { EngineIndicesLogic, EngineIndicesLogicValues } from './engine_indices_logic'; const DEFAULT_VALUES: EngineIndicesLogicValues = { + addIndicesFlyoutOpen: false, engineData: undefined, engineName: 'my-test-engine', + isLoadingEngine: true, }; const mockEngineData: EnterpriseSearchEngineDetails = { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.ts index 891d25dc197bb..52c485c8ed7ec 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.ts @@ -16,15 +16,19 @@ import { EngineViewActions, EngineViewLogic, EngineViewValues } from './engine_v export interface EngineIndicesLogicActions { addIndicesToEngine: (indices: string[]) => { indices: string[] }; + closeAddIndicesFlyout: () => void; engineUpdated: UpdateEngineApiLogicActions['apiSuccess']; fetchEngine: EngineViewActions['fetchEngine']; + openAddIndicesFlyout: () => void; removeIndexFromEngine: (indexName: string) => { indexName: string }; updateEngineRequest: UpdateEngineApiLogicActions['makeRequest']; } export interface EngineIndicesLogicValues { + addIndicesFlyoutOpen: boolean; engineData: EngineViewValues['engineData']; engineName: EngineViewValues['engineName']; + isLoadingEngine: EngineViewValues['isLoadingEngine']; } export const EngineIndicesLogic = kea< @@ -32,6 +36,8 @@ export const EngineIndicesLogic = kea< >({ actions: { addIndicesToEngine: (indices) => ({ indices }), + closeAddIndicesFlyout: () => true, + openAddIndicesFlyout: () => true, removeIndexFromEngine: (indexName) => ({ indexName }), }, connect: { @@ -41,7 +47,7 @@ export const EngineIndicesLogic = kea< UpdateEngineApiLogic, ['makeRequest as updateEngineRequest', 'apiSuccess as engineUpdated'], ], - values: [EngineViewLogic, ['engineData', 'engineName']], + values: [EngineViewLogic, ['engineData', 'engineName', 'isLoadingEngine']], }, listeners: ({ actions, values }) => ({ addIndicesToEngine: ({ indices }) => { @@ -68,4 +74,13 @@ export const EngineIndicesLogic = kea< }, }), path: ['enterprise_search', 'content', 'engine_indices_logic'], + reducers: { + addIndicesFlyoutOpen: [ + false, + { + closeAddIndicesFlyout: () => false, + openAddIndicesFlyout: () => true, + }, + ], + }, }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view.tsx index 05b34088b1797..e6057a2d1b2bd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view.tsx @@ -15,18 +15,25 @@ import { Status } from '../../../../../common/types/api'; import { KibanaLogic } from '../../../shared/kibana'; import { ENGINE_PATH, EngineViewTabs } from '../../routes'; +import { DeleteEngineModal } from '../engines/delete_engine_modal'; import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template'; import { EngineAPI } from './engine_api/engine_api'; import { EngineError } from './engine_error'; import { EngineIndices } from './engine_indices'; +import { EngineViewHeaderActions } from './engine_view_header_actions'; import { EngineViewLogic } from './engine_view_logic'; import { EngineHeaderDocsAction } from './header_docs_action'; export const EngineView: React.FC = () => { - const { fetchEngine } = useActions(EngineViewLogic); - const { engineName, fetchEngineApiError, fetchEngineApiStatus, isLoadingEngine } = - useValues(EngineViewLogic); + const { fetchEngine, closeDeleteEngineModal } = useActions(EngineViewLogic); + const { + engineName, + fetchEngineApiError, + fetchEngineApiStatus, + isDeleteModalVisible, + isLoadingEngine, + } = useValues(EngineViewLogic); const { tabId = EngineViewTabs.OVERVIEW } = useParams<{ tabId?: string; }>(); @@ -54,23 +61,28 @@ export const EngineView: React.FC = () => { } return ( - - - - ( - - )} - /> - + <> + {isDeleteModalVisible ? ( + + ) : null} + + + + ( + ], + }} + engineName={engineName} + isLoading={isLoadingEngine} + /> + )} + /> + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_header_actions.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_header_actions.tsx new file mode 100644 index 0000000000000..ba563a9c23dbc --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_header_actions.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; + +import { useValues, useActions } from 'kea'; + +import { EuiPopover, EuiButtonIcon, EuiText, EuiContextMenu, EuiIcon } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { EngineViewLogic } from './engine_view_logic'; + +export const EngineViewHeaderActions: React.FC = () => { + const { engineData } = useValues(EngineViewLogic); + + const { openDeleteEngineModal } = useActions(EngineViewLogic); + + const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); + const toggleActionsPopover = () => setIsActionsPopoverOpen((isPopoverOpen) => !isPopoverOpen); + const closePopover = () => setIsActionsPopoverOpen(false); + return ( + <> + + } + isOpen={isActionsPopoverOpen} + panelPaddingSize="xs" + closePopover={closePopover} + display="block" + > + , + name: ( + + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.headerActions.delete', + { defaultMessage: 'Delete this engine' } + )} + + ), + onClick: () => { + if (engineData) { + openDeleteEngineModal(); + } + }, + size: 's', + }, + ], + }, + ]} + /> + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_logic.test.ts index 18ad97c54e649..48d85c9f0c6ac 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_logic.test.ts @@ -9,6 +9,11 @@ import { LogicMounter } from '../../../__mocks__/kea_logic'; import { Status } from '../../../../../common/types/api'; +import { KibanaLogic } from '../../../shared/kibana'; +import { DeleteEnginesApiLogicResponse } from '../../api/engines/delete_engines_api_logic'; +import { ENGINES_PATH } from '../../routes'; +import { EnginesListLogic } from '../engines/engines_list_logic'; + import { EngineViewLogic, EngineViewValues } from './engine_view_logic'; const DEFAULT_VALUES: EngineViewValues = { @@ -16,19 +21,36 @@ const DEFAULT_VALUES: EngineViewValues = { engineName: 'my-test-engine', fetchEngineApiError: undefined, fetchEngineApiStatus: Status.IDLE, + isDeleteModalVisible: false, isLoadingEngine: true, }; describe('EngineViewLogic', () => { const { mount } = new LogicMounter(EngineViewLogic); + const { mount: mountEnginesListLogic } = new LogicMounter(EnginesListLogic); beforeEach(() => { jest.clearAllMocks(); jest.useRealTimers(); + mountEnginesListLogic(); mount({ engineName: DEFAULT_VALUES.engineName }, { engineName: DEFAULT_VALUES.engineName }); }); it('has expected default values', () => { expect(EngineViewLogic.values).toEqual(DEFAULT_VALUES); }); + + describe('listeners', () => { + describe('deleteSuccess', () => { + it('should navigate to the engines list when an engine is deleted', () => { + jest.spyOn(EngineViewLogic.actions, 'deleteSuccess'); + jest + .spyOn(KibanaLogic.values, 'navigateToUrl') + .mockImplementationOnce(() => Promise.resolve()); + EnginesListLogic.actions.deleteSuccess({} as DeleteEnginesApiLogicResponse); + + expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(ENGINES_PATH); + }); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_logic.ts index 021ac6d9ea626..1cc3bfc5f4e90 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_logic.ts @@ -9,15 +9,24 @@ import { kea, MakeLogicType } from 'kea'; import { Status } from '../../../../../common/types/api'; +import { KibanaLogic } from '../../../shared/kibana'; + import { FetchEngineApiLogic, FetchEngineApiLogicActions, } from '../../api/engines/fetch_engine_api_logic'; +import { ENGINES_PATH } from '../../routes'; + +import { EnginesListLogic, EnginesListActions } from '../engines/engines_list_logic'; + import { EngineNameLogic } from './engine_name_logic'; export interface EngineViewActions { + closeDeleteEngineModal(): void; + deleteSuccess: EnginesListActions['deleteSuccess']; fetchEngine: FetchEngineApiLogicActions['makeRequest']; + openDeleteEngineModal(): void; } export interface EngineViewValues { @@ -25,12 +34,18 @@ export interface EngineViewValues { engineName: typeof EngineNameLogic.values.engineName; fetchEngineApiError?: typeof FetchEngineApiLogic.values.error; fetchEngineApiStatus: typeof FetchEngineApiLogic.values.status; + isDeleteModalVisible: boolean; isLoadingEngine: boolean; } export const EngineViewLogic = kea>({ connect: { - actions: [FetchEngineApiLogic, ['makeRequest as fetchEngine']], + actions: [ + FetchEngineApiLogic, + ['makeRequest as fetchEngine'], + EnginesListLogic, + ['deleteSuccess'], + ], values: [ EngineNameLogic, ['engineName'], @@ -38,7 +53,26 @@ export const EngineViewLogic = kea ({ + deleteSuccess: () => { + actions.closeDeleteEngineModal(); + KibanaLogic.values.navigateToUrl(ENGINES_PATH); + }, + }), path: ['enterprise_search', 'content', 'engine_view_logic'], + reducers: () => ({ + isDeleteModalVisible: [ + false, + { + closeDeleteEngineModal: () => false, + openDeleteEngineModal: () => true, + }, + ], + }), selectors: ({ selectors }) => ({ isLoadingEngine: [ () => [selectors.fetchEngineApiStatus, selectors.engineData], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx index 65a9879bb1e5e..5e6f7cebd89fc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx @@ -28,6 +28,8 @@ import { ElasticsearchIndexWithIngestion } from '../../../../../../common/types/ import { indexHealthToHealthColor } from '../../../../shared/constants/health_colors'; import { FetchIndicesForEnginesAPILogic } from '../../../api/engines/fetch_indices_api_logic'; +export type IndicesSelectComboBoxOption = EuiComboBoxOptionOption; + export type IndicesSelectComboBoxProps = Omit< EuiComboBoxProps, 'onCreateOption' | 'onSearchChange' | 'noSuggestions' | 'async' @@ -83,7 +85,7 @@ export const IndicesSelectComboBox = (props: IndicesSelectComboBoxProps) => { export const indexToOption = ( index: ElasticsearchIndexWithIngestion -): EuiComboBoxOptionOption => ({ +): IndicesSelectComboBoxOption => ({ label: index.name, value: index, }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx index 1c419704aa27d..b3c5c46b38128 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx @@ -20,22 +20,23 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EnterpriseSearchEngine } from '../../../../../../../common/types/engines'; +import { Page } from '../../../../../../../common/types/pagination'; + import { MANAGE_BUTTON_LABEL } from '../../../../../shared/constants'; import { generateEncodedPath } from '../../../../../shared/encode_path_params'; import { FormattedDateTime } from '../../../../../shared/formatted_date_time'; import { KibanaLogic } from '../../../../../shared/kibana'; +import { pageToPagination } from '../../../../../shared/pagination/page_to_pagination'; import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; import { ENGINE_PATH } from '../../../../routes'; -import { convertMetaToPagination, Meta } from '../../types'; - interface EnginesListTableProps { enginesList: EnterpriseSearchEngine[]; isLoading?: boolean; loading: boolean; - meta: Meta; + meta: Page; onChange: (criteria: CriteriaWithPagination) => void; onDelete: (engine: EnterpriseSearchEngine) => void; viewEngineIndices: (engineName: string) => void; @@ -157,7 +158,7 @@ export const EnginesListTable: React.FC = ({ diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx index 16dfdf50a7470..a8cde672107c2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx @@ -15,46 +15,42 @@ import { CANCEL_BUTTON_LABEL } from '../../../shared/constants'; import { EnginesListLogic } from './engines_list_logic'; -export const DeleteEngineModal: React.FC = () => { - const { closeDeleteEngineModal, deleteEngine } = useActions(EnginesListLogic); - const { - deleteModalEngineName: engineName, - isDeleteModalVisible, - isDeleteLoading, - } = useValues(EnginesListLogic); +export interface DeleteEngineModalProps { + engineName: string; + onClose: () => void; +} - if (isDeleteModalVisible) { - return ( - { - deleteEngine({ engineName }); - }} - cancelButtonText={CANCEL_BUTTON_LABEL} - confirmButtonText={i18n.translate( - 'xpack.enterpriseSearch.content.engineList.deleteEngineModal.confirmButton.title', +export const DeleteEngineModal: React.FC = ({ engineName, onClose }) => { + const { deleteEngine } = useActions(EnginesListLogic); + const { isDeleteLoading } = useValues(EnginesListLogic); + return ( + { + deleteEngine({ engineName }); + }} + cancelButtonText={CANCEL_BUTTON_LABEL} + confirmButtonText={i18n.translate( + 'xpack.enterpriseSearch.content.engineList.deleteEngineModal.confirmButton.title', + { + defaultMessage: 'Yes, delete this engine ', + } + )} + buttonColor="danger" + isLoading={isDeleteLoading} + > +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.engineList.deleteEngineModal.delete.description', { - defaultMessage: 'Yes, delete this engine ', + defaultMessage: + 'Deleting your engine is not a reversible action. Your indices will not be affected. ', } )} - buttonColor="danger" - isLoading={isDeleteLoading} - > -

- {i18n.translate( - 'xpack.enterpriseSearch.content.engineList.deleteEngineModal.delete.description', - { - defaultMessage: - 'Deleting your engine is not a reversible action. Your indices will not be affected. ', - } - )} -

-
- ); - } else { - return <>; - } +

+
+ ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx index cf97afc5673ec..7165e915fd9e5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx @@ -47,11 +47,26 @@ const CreateButton: React.FC = () => { }; export const EnginesList: React.FC = () => { - const { closeEngineCreate, fetchEngines, onPaginate, openDeleteEngineModal, setSearchQuery } = - useActions(EnginesListLogic); + const { + closeDeleteEngineModal, + closeEngineCreate, + fetchEngines, + onPaginate, + openDeleteEngineModal, + setSearchQuery, + } = useActions(EnginesListLogic); + const { openFetchEngineFlyout } = useActions(EnginesListFlyoutLogic); - const { isLoading, meta, results, createEngineFlyoutOpen, searchQuery } = - useValues(EnginesListLogic); + + const { + createEngineFlyoutOpen, + deleteModalEngineName, + isDeleteModalVisible, + isLoading, + meta, + results, + searchQuery, + } = useValues(EnginesListLogic); const throttledSearchQuery = useThrottle(searchQuery, INPUT_THROTTLE_DELAY_MS); @@ -61,7 +76,9 @@ export const EnginesList: React.FC = () => { return ( <> - + {isDeleteModalVisible ? ( + + ) : null} {createEngineFlyoutOpen && } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engine_list_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.test.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engine_list_logic.test.ts rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.test.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts index c515ada735d06..d83f5b179a862 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts @@ -11,8 +11,10 @@ import { Status } from '../../../../../common/types/api'; import { EnterpriseSearchEngine, + EnterpriseSearchEngineDetails, EnterpriseSearchEnginesResponse, } from '../../../../../common/types/engines'; +import { Page } from '../../../../../common/types/pagination'; import { Actions } from '../../../shared/api_logic/create_api_logic'; @@ -26,13 +28,13 @@ import { FetchEnginesAPILogic, } from '../../api/engines/fetch_engines_api_logic'; -import { DEFAULT_META, Meta, updateMetaPageIndex } from './types'; +import { DEFAULT_META, updateMetaPageIndex } from './types'; interface EuiBasicTableOnChange { page: { index: number }; } -type EnginesListActions = Pick< +export type EnginesListActions = Pick< Actions, 'apiError' | 'apiSuccess' | 'makeRequest' > & { @@ -45,10 +47,13 @@ type EnginesListActions = Pick< fetchEngines(): void; onPaginate(args: EuiBasicTableOnChange): { pageNumber: number }; - openDeleteEngineModal: (engine: EnterpriseSearchEngine) => { engine: EnterpriseSearchEngine }; + openDeleteEngineModal: (engine: EnterpriseSearchEngine | EnterpriseSearchEngineDetails) => { + engine: EnterpriseSearchEngine; + }; openEngineCreate(): void; setSearchQuery(searchQuery: string): { searchQuery: string }; }; + interface EngineListValues { createEngineFlyoutOpen: boolean; data: typeof FetchEnginesAPILogic.values.data; @@ -58,8 +63,8 @@ interface EngineListValues { isDeleteLoading: boolean; isDeleteModalVisible: boolean; isLoading: boolean; - meta: Meta; - parameters: { meta: Meta; searchQuery?: string }; // Added this variable to store to the search Query value as well + meta: Page; + parameters: { meta: Page; searchQuery?: string }; // Added this variable to store to the search Query value as well results: EnterpriseSearchEngine[]; // stores engine list value from data searchQuery: string; status: typeof FetchEnginesAPILogic.values.status; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx index 10687556a1e33..fdbc4e544c135 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx @@ -10,7 +10,6 @@ import { Route, Switch } from 'react-router-dom'; import { useValues } from 'kea'; -import { enableEnginesSection } from '../../../../../common/ui_settings_keys'; import { KibanaLogic } from '../../../shared/kibana'; import { ENGINES_PATH, ENGINE_PATH } from '../../routes'; @@ -20,8 +19,8 @@ import { NotFound } from '../not_found'; import { EnginesList } from './engines_list'; export const EnginesRouter: React.FC = () => { - const { uiSettings } = useValues(KibanaLogic); - const enginesSectionEnabled = uiSettings?.get(enableEnginesSection, false); + const { productAccess } = useValues(KibanaLogic); + const enginesSectionEnabled = productAccess.hasSearchEnginesAccess; if (!enginesSectionEnabled) { return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/types.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/types.ts index 90fb49a054688..ca30dfaea28b6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/types.ts @@ -5,11 +5,7 @@ * 2.0. */ -export interface Meta { - from: number; - size: number; - total: number; -} +import { Page } from '../../../../../common/types/pagination'; export const DEFAULT_META = { from: 0, @@ -17,13 +13,6 @@ export const DEFAULT_META = { total: 0, }; -export const convertMetaToPagination = (meta: Meta) => { - return { - pageIndex: meta.from / meta.size, - pageSize: meta.size, - totalItemCount: meta.total, - }; -}; -export const updateMetaPageIndex = (oldState: Meta, newPageIndex: number) => { +export const updateMetaPageIndex = (oldState: Page, newPageIndex: number) => { return { ...oldState, from: newPageIndex * oldState.size }; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_details_flyout/crawl_details_summary.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_details_flyout/crawl_details_summary.tsx index 43f0b1d570177..52f02b6833faa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_details_flyout/crawl_details_summary.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawl_details_flyout/crawl_details_summary.tsx @@ -14,12 +14,17 @@ import { EuiFlexItem, EuiHorizontalRule, EuiIconTip, + EuiLink, EuiPanel, EuiSpacer, EuiStat, EuiText, } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { docLinks } from '../../../../../shared/doc_links'; import { CrawlRequestStats } from '../../../../api/crawler/types'; @@ -236,13 +241,20 @@ export const CrawlDetailsSummary: React.FC = ({

- {i18n.translate( - 'xpack.enterpriseSearch.crawler.crawlDetailsSummary.logsDisabledMessage', - { - defaultMessage: - 'Enable Web Crawler logs in settings for more detailed crawl statistics.', - } - )} + + {i18n.translate( + 'xpack.enterpriseSearch.crawler.crawlDetailsSummary.configLink', + { defaultMessage: 'Enable web crawler logs' } + )} + + ), + }} + />

)} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/authentication_panel/authentication_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/authentication_panel/authentication_panel.tsx index 4f406ebe67939..4fe77b0196771 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/authentication_panel/authentication_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/authentication_panel/authentication_panel.tsx @@ -29,7 +29,7 @@ export const AuthenticationPanel: React.FC = () => {
- +

{i18n.translate('xpack.enterpriseSearch.crawler.authenticationPanel.title', { defaultMessage: 'Authentication', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawl_rules_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawl_rules_table.tsx index 6bead7b4314d9..db98f1fb987f7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawl_rules_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawl_rules_table.tsx @@ -251,9 +251,7 @@ export const CrawlRulesTable: React.FC = ({ updateCrawlRules(newCrawlRules as CrawlRule[]); clearFlashMessages(); }} - title={i18n.translate('xpack.enterpriseSearch.crawler.crawlRulesTable.title', { - defaultMessage: 'Crawl rules', - })} + title="" uneditableItems={defaultCrawlRule ? [defaultCrawlRule] : undefined} canRemoveLastItem /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawler_domain_detail_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawler_domain_detail_logic.ts index b36671d492714..d17f3df02550f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawler_domain_detail_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawler_domain_detail_logic.ts @@ -7,7 +7,8 @@ import { kea, MakeLogicType } from 'kea'; -import { HttpError, Status } from '../../../../../../../common/types/api'; +import { Status } from '../../../../../../../common/types/api'; +import { ExtractionRule } from '../../../../../../../common/types/extraction_rules'; import { generateEncodedPath } from '../../../../../shared/encode_path_params'; @@ -42,11 +43,11 @@ export interface CrawlerDomainDetailValues { deleteStatus: Status; domain: CrawlerDomain | null; domainId: string; + extractionRules: ExtractionRule[]; getLoading: boolean; } export interface CrawlerDomainDetailActions { - deleteApiError(error: HttpError): HttpError; deleteApiSuccess(response: DeleteCrawlerDomainResponse): DeleteCrawlerDomainResponse; deleteDomain(): void; deleteMakeRequest(args: DeleteCrawlerDomainArgs): DeleteCrawlerDomainArgs; @@ -59,24 +60,13 @@ export interface CrawlerDomainDetailActions { }; updateCrawlRules(crawlRules: CrawlRule[]): { crawlRules: CrawlRule[] }; updateEntryPoints(entryPoints: EntryPoint[]): { entryPoints: EntryPoint[] }; + updateExtractionRules(extractionRules: ExtractionRule[]): { extractionRules: ExtractionRule[] }; updateSitemaps(entryPoints: Sitemap[]): { sitemaps: Sitemap[] }; } export const CrawlerDomainDetailLogic = kea< MakeLogicType >({ - path: ['enterprise_search', 'crawler', 'crawler_domain_detail_logic'], - connect: { - actions: [ - DeleteCrawlerDomainApiLogic, - [ - 'apiError as deleteApiError', - 'apiSuccess as deleteApiSuccess', - 'makeRequest as deleteMakeRequest', - ], - ], - values: [DeleteCrawlerDomainApiLogic, ['status as deleteStatus']], - }, actions: { deleteDomain: () => true, deleteDomainComplete: () => true, @@ -86,36 +76,26 @@ export const CrawlerDomainDetailLogic = kea< submitDeduplicationUpdate: ({ fields, enabled }) => ({ enabled, fields }), updateCrawlRules: (crawlRules) => ({ crawlRules }), updateEntryPoints: (entryPoints) => ({ entryPoints }), + updateExtractionRules: (extractionRules) => ({ extractionRules }), updateSitemaps: (sitemaps) => ({ sitemaps }), }, - reducers: ({ props }) => ({ - domain: [ - null, - { - receiveDomainData: (_, { domain }) => domain, - updateCrawlRules: (currentDomain, { crawlRules }) => - ({ ...currentDomain, crawlRules } as CrawlerDomain), - updateEntryPoints: (currentDomain, { entryPoints }) => - ({ ...currentDomain, entryPoints } as CrawlerDomain), - updateSitemaps: (currentDomain, { sitemaps }) => - ({ ...currentDomain, sitemaps } as CrawlerDomain), - }, - ], - domainId: [props.domainId, { fetchDomainData: (_, { domainId }) => domainId }], - getLoading: [ - true, - { - receiveDomainData: () => false, - }, - ], - }), - selectors: ({ selectors }) => ({ - deleteLoading: [ - () => [selectors.deleteStatus], - (deleteStatus: Status) => deleteStatus === Status.LOADING, + connect: { + actions: [ + DeleteCrawlerDomainApiLogic, + ['apiSuccess as deleteApiSuccess', 'makeRequest as deleteMakeRequest'], ], - }), + values: [DeleteCrawlerDomainApiLogic, ['status as deleteStatus']], + }, listeners: ({ actions, values }) => ({ + deleteApiSuccess: () => { + const { indexName } = IndexNameLogic.values; + KibanaLogic.values.navigateToUrl( + generateEncodedPath(SEARCH_INDEX_TAB_PATH, { + indexName, + tabId: SearchIndexTabId.DOMAIN_MANAGEMENT, + }) + ); + }, deleteDomain: async () => { const { domain } = values; const { indexName } = IndexNameLogic.values; @@ -126,15 +106,6 @@ export const CrawlerDomainDetailLogic = kea< }); } }, - deleteApiSuccess: () => { - const { indexName } = IndexNameLogic.values; - KibanaLogic.values.navigateToUrl( - generateEncodedPath(SEARCH_INDEX_TAB_PATH, { - indexName, - tabId: SearchIndexTabId.DOMAIN_MANAGEMENT, - }) - ); - }, fetchDomainData: async ({ domainId }) => { const { http } = HttpLogic.values; const { indexName } = IndexNameLogic.values; @@ -201,4 +172,36 @@ export const CrawlerDomainDetailLogic = kea< } }, }), + path: ['enterprise_search', 'crawler', 'crawler_domain_detail_logic'], + reducers: ({ props }) => ({ + domain: [ + null, + { + receiveDomainData: (_, { domain }) => domain, + updateCrawlRules: (currentDomain, { crawlRules }) => + currentDomain ? { ...currentDomain, crawlRules } : currentDomain, + updateEntryPoints: (currentDomain, { entryPoints }) => + currentDomain ? { ...currentDomain, entryPoints } : currentDomain, + updateSitemaps: (currentDomain, { sitemaps }) => + currentDomain ? { ...currentDomain, sitemaps } : currentDomain, + }, + ], + domainId: [props.domainId, { fetchDomainData: (_, { domainId }) => domainId }], + getLoading: [ + true, + { + receiveDomainData: () => false, + }, + ], + }), + selectors: ({ selectors }) => ({ + deleteLoading: [ + () => [selectors.deleteStatus], + (deleteStatus: Status) => deleteStatus === Status.LOADING, + ], + extractionRules: [ + () => [selectors.domain], + (domain: CrawlerDomain | null) => domain?.extractionRules ?? [], + ], + }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawler_domain_detail_tabs.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawler_domain_detail_tabs.tsx index a62dc8bf3d558..9ca36ba97f7b4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawler_domain_detail_tabs.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/crawler_domain_detail_tabs.tsx @@ -7,7 +7,7 @@ import React, { useState } from 'react'; -import { EuiSpacer, EuiTabbedContent, EuiTabbedContentTab } from '@elastic/eui'; +import { EuiSpacer, EuiTabbedContent, EuiTabbedContentTab, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { CrawlerDomain } from '../../../../api/crawler/types'; @@ -16,6 +16,7 @@ import { AuthenticationPanel } from './authentication_panel/authentication_panel import { CrawlRulesTable } from './crawl_rules_table'; import { DeduplicationPanel } from './deduplication_panel/deduplication_panel'; import { EntryPointsTable } from './entry_points_table'; +import { ExtractionRules } from './extraction_rules/extraction_rules'; import { SitemapsTable } from './sitemaps_table'; export enum CrawlerDomainTabId { @@ -23,6 +24,7 @@ export enum CrawlerDomainTabId { AUTHENTICATION = 'authentication', SITE_MAPS = 'site_maps', CRAWL_RULES = 'crawl_rules', + EXTRACTION_RULES = 'extraction_rules', DEDUPLICATION = 'deduplication', } @@ -41,6 +43,13 @@ export const CrawlerDomainDetailTabs: React.FC = ( content: ( <> + +

+ {i18n.translate('xpack.enterpriseSearch.crawler.entryPointsTable.title', { + defaultMessage: 'Entry points', + })} +

+
), @@ -65,6 +74,13 @@ export const CrawlerDomainDetailTabs: React.FC = ( content: ( <> + +

+ {i18n.translate('xpack.enterpriseSearch.crawler.sitemapsTable.title', { + defaultMessage: 'Sitemaps', + })} +

+
), @@ -77,6 +93,13 @@ export const CrawlerDomainDetailTabs: React.FC = ( content: ( <> + +

+ {i18n.translate('xpack.enterpriseSearch.crawler.crawlRulesTable.title', { + defaultMessage: 'Crawl rules', + })} +

+
= ( defaultMessage: 'Crawl rules', }), }, + { + content: ( + <> + + + + ), + id: CrawlerDomainTabId.EXTRACTION_RULES, + name: i18n.translate('xpack.enterpriseSearch.content.crawler.extractionRules', { + defaultMessage: 'Extraction rules', + }), + }, { content: , id: CrawlerDomainTabId.DEDUPLICATION, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/deduplication_panel/deduplication_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/deduplication_panel/deduplication_panel.tsx index 8076b3e49aa1f..e17815765169e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/deduplication_panel/deduplication_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/deduplication_panel/deduplication_panel.tsx @@ -57,7 +57,7 @@ export const DeduplicationPanel: React.FC = () => { - +

{i18n.translate('xpack.enterpriseSearch.crawler.deduplicationPanel.title', { defaultMessage: 'Duplicate document handling', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/entry_points_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/entry_points_table.test.tsx index cf1224cb0dc47..438792b037ff9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/entry_points_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/entry_points_table.test.tsx @@ -33,6 +33,7 @@ describe('EntryPointsTable', () => { deduplicationFields: ['title'], documentCount: 10, entryPoints, + extractionRules: [], id: '6113e1407a2f2e6f42489794', sitemaps: [], url: 'https://www.elastic.co', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/entry_points_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/entry_points_table.tsx index a80ecc85646b3..8a38abf4efdc7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/entry_points_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/entry_points_table.tsx @@ -130,9 +130,7 @@ export const EntryPointsTable: React.FC = ({ domain, inde onAdd={onAdd} onDelete={onDelete} onUpdate={onUpdate} - title={i18n.translate('xpack.enterpriseSearch.crawler.entryPointsTable.title', { - defaultMessage: 'Entry points', - })} + title="" disableReordering /> ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/content_fields_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/content_fields_panel.tsx new file mode 100644 index 0000000000000..fae0089669538 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/content_fields_panel.tsx @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { + EuiEmptyPrompt, + EuiText, + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { ExtractionRuleFieldRule } from '../../../../../../../../common/types/extraction_rules'; + +import { FieldRulesTable } from './field_rules_table'; + +interface ContentFieldsPanelProps { + contentFields: Array; + editExistingField: (id: string) => void; + editNewField: () => void; + removeField: (id: string) => void; +} + +export const ContentFieldsPanel: React.FC = ({ + contentFields, + editNewField, + editExistingField, + removeField, +}) => { + return contentFields.length === 0 ? ( + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.fieldRules.emptyMessageTitle', + { + defaultMessage: 'This extraction rule has no content fields', + } + )} +

+ } + titleSize="s" + body={ + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.fieldRules.emptyMessageDescription', + { + defaultMessage: + 'Create a content field to pinpoint which parts of a webpage to pull data from.', + } + )} + + } + actions={ + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.fieldRules.emptyMessageAddRuleLabel', + { + defaultMessage: 'Add content fields', + } + )} + + } + /> + ) : ( + <> + + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.fieldRules.contentFieldDescription', + { + defaultMessage: + 'Create a content field to pinpoint which parts of a webpage to pull data from.', + } + )} +

+
+
+ + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.fieldRules.addContentFieldRuleLabel', + { + defaultMessage: 'Add content field rule', + } + )} + + +
+ + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/edit_extraction_rule.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/edit_extraction_rule.tsx new file mode 100644 index 0000000000000..c03a395591bae --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/edit_extraction_rule.tsx @@ -0,0 +1,446 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState } from 'react'; + +import { Controller, useFieldArray, useForm } from 'react-hook-form'; + +import { useActions, useValues } from 'kea'; + +import { + EuiButton, + EuiButtonEmpty, + EuiButtonIcon, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiLink, + EuiPanel, + EuiRadioGroup, + EuiSelect, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { + ExtractionFilter, + ExtractionRule, + ExtractionRuleBase, +} from '../../../../../../../../common/types/extraction_rules'; + +import { ContentFieldsPanel } from './content_fields_panel'; +import { EditFieldRuleFlyout } from './edit_field_rule_flyout'; +import { ExtractionRulesLogic } from './extraction_rules_logic'; + +interface EditExtractionRuleProps { + cancelEditing: () => void; + extractionRule: ExtractionRule | null; + isNewRule: boolean; + saveRule: (rule: ExtractionRuleBase) => void; +} + +enum UrlState { + ALL = 'all', + SPECIFIC = 'specific', +} + +const getReadableExtractionFilter = (rule: ExtractionFilter) => { + switch (rule) { + case ExtractionFilter.BEGINS: + return i18n.translate( + 'xpack.enterpriseSearch.crawler.extractionRulesExtractionFilter.beginsWithLabel', + { + defaultMessage: 'Begins with', + } + ); + case ExtractionFilter.ENDS: + return i18n.translate( + 'xpack.enterpriseSearch.crawler.extractionRulesExtractionFilter.endsWithLabel', + { + defaultMessage: 'Ends with', + } + ); + case ExtractionFilter.CONTAINS: + return i18n.translate( + 'xpack.enterpriseSearch.crawler.extractionRulesExtractionFilter.containsLabel', + { + defaultMessage: 'Contains', + } + ); + case ExtractionFilter.REGEX: + return i18n.translate( + 'xpack.enterpriseSearch.crawler.extractionRulesExtractionFilter.regexLabel', + { + defaultMessage: 'Regex', + } + ); + } +}; + +const extractionFilterOptions = [ + ExtractionFilter.BEGINS, + ExtractionFilter.ENDS, + ExtractionFilter.CONTAINS, + ExtractionFilter.REGEX, +].map((ruleOption: ExtractionFilter) => ({ + text: getReadableExtractionFilter(ruleOption), + value: ruleOption, +})); + +export const EditExtractionRule: React.FC = ({ + cancelEditing, + extractionRule, + isNewRule, + saveRule, +}) => { + const { closeEditRuleFlyout, openEditRuleFlyout } = useActions(ExtractionRulesLogic); + const { fieldRuleFlyoutVisible, fieldRuleToEdit, fieldRuleToEditIndex, fieldRuleToEditIsNew } = + useValues(ExtractionRulesLogic); + const [urlToggle, setUrlToggle] = useState(UrlState.ALL); + const { control, formState, getValues, handleSubmit, reset, setValue } = + useForm({ + defaultValues: extractionRule ?? { + description: '', + rules: [], + url_filters: [], + }, + mode: 'all', + }); + const { + append: appendUrlFilter, + fields: urlFiltersFields, + remove: removeUrlFilter, + } = useFieldArray({ + control, + name: 'url_filters', + }); + const { + append: appendRule, + fields: rulesFields, + remove: removeRule, + update: updateRule, + } = useFieldArray({ control, name: 'rules' }); + + useEffect(() => { + reset( + extractionRule ?? { + description: '', + rules: [], + url_filters: [], + } + ); + if (extractionRule) { + setUrlToggle(extractionRule.url_filters.length === 0 ? UrlState.ALL : UrlState.SPECIFIC); + } else { + setUrlToggle(UrlState.ALL); + } + }, [extractionRule]); + + return ( + <> + +

+ {isNewRule + ? i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.addRule.title', + { + defaultMessage: 'Create a content extraction rule', + } + ) + : i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.title', + { + defaultMessage: 'Edit content extraction rule', + } + )} +

+
+ + + { + if (!rule?.trim()) { + return i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.descriptionError', + { + defaultMessage: 'A description is required for a content extraction rule', + } + ); + } + }, + }} + render={({ field, fieldState }) => ( + + + + )} + /> + + { + setUrlToggle(value as UrlState); + // Make sure we always have one url filter when switching to specific URL filters + if (value === UrlState.SPECIFIC && urlFiltersFields.length < 1) { + setValue('url_filters', [{ filter: ExtractionFilter.BEGINS, pattern: '' }]); + } else { + setValue('url_filters', []); + } + }} + /> + + + {urlToggle === UrlState.SPECIFIC && ( + <> + {urlFiltersFields.map((urlFilter, index) => ( + + + ( + + + + )} + /> + + + ( + <> + + + + + + )} + /> + + + {urlFiltersFields.length > 1 && ( + removeUrlFilter(index)} + /> + )} + + + ))} + + appendUrlFilter({ filter: ExtractionFilter.BEGINS, pattern: '' })} + > + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.url.urlFilters.addFilter', + { + defaultMessage: 'Add URL filter', + } + )} + + + )} + + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.url.urlFiltersLink', + { + defaultMessage: 'Learn more about URL filters', + } + )} + + + + + openEditRuleFlyout({ + fieldRule: rulesFields.find(({ id: ruleId }) => ruleId === id), + isNewRule: false, + }) + } + editNewField={() => openEditRuleFlyout({ isNewRule: true })} + removeField={(id) => { + const index = rulesFields.findIndex(({ id: ruleId }) => ruleId === id); + if (index >= 0) { + removeRule(index); + } + }} + /> + + + + + + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.cancelButtonLabel', + { + defaultMessage: 'Cancel', + } + )} + + + + saveRule({ ...getValues() })} + disabled={!formState.isValid} + > + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editRule.saveButtonLabel', + { + defaultMessage: 'Save rule', + } + )} + + + + + + {fieldRuleFlyoutVisible && ( + { + if (fieldRuleToEditIsNew) { + appendRule(fieldRule); + } else { + updateRule(fieldRuleToEditIndex ?? 0, fieldRule); + } + closeEditRuleFlyout(); + }} + /> + )} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/edit_field_rule_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/edit_field_rule_flyout.tsx new file mode 100644 index 0000000000000..2bb28f0d27045 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/edit_field_rule_flyout.tsx @@ -0,0 +1,489 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect } from 'react'; + +import { Controller, useForm } from 'react-hook-form'; + +import { + EuiButton, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiForm, + EuiFormRow, + EuiPanel, + EuiRadioGroup, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { + ContentFrom, + ExtractionRuleFieldRule, + FieldType, + MultipleObjectsHandling, +} from '../../../../../../../../common/types/extraction_rules'; + +interface EditFieldRuleFlyoutProps { + fieldRule: ExtractionRuleFieldRule | null; + isNewRule: boolean; + onClose: () => void; + saveRule: (fieldRule: ExtractionRuleFieldRule & { id?: string; index?: number }) => void; +} + +const defaultRule = { + content_from: { + value: '', + value_type: undefined, + }, + field_name: '', + multiple_objects_handling: MultipleObjectsHandling.STRING, + selector: '', + source_type: undefined, +}; + +export const EditFieldRuleFlyout: React.FC = ({ + onClose, + fieldRule, + isNewRule, + saveRule, +}) => { + const { control, reset, getValues, formState } = useForm({ + defaultValues: fieldRule ?? defaultRule, + mode: 'all', + }); + + useEffect(() => { + reset(fieldRule ?? defaultRule); + }, [fieldRule]); + + return ( + + + +

+ {isNewRule + ? i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.addContentField.title', + { + defaultMessage: 'Add content field rule', + } + ) + : i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.title', + { + defaultMessage: 'Edit content field rule', + } + )} +

+
+
+ + + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.documentField.title', + { + defaultMessage: 'Document field', + } + )} +

+
+ + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.documentField.description', + { + defaultMessage: 'Select a document field to build a rule around.', + } + )} + + + + !!rule?.trim() || + i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.edilidtContentField.documentField.requiredError', + { + defaultMessage: 'A field name is required.', + } + ), + }} + render={({ field, fieldState: { error, isTouched } }) => ( + + + + )} + /> +
+ + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.source.title', + { + defaultMessage: 'Source', + } + )} +

+
+ + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.source.description', + { + defaultMessage: 'Where to extract the content for this field from.', + } + )} + + + + !!rule?.trim() || + i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.source.requiredError', + { + defaultMessage: 'A source for the content is required.', + } + ), + }} + render={({ field }) => ( + <> + + + + {!!field.value && ( + <> + + + ( + + )} + /> + + + )} + + )} + /> +
+ + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.content.title', + { + defaultMessage: 'Content', + } + )} +

+
+ + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.content.description', + { + defaultMessage: 'Populate the field with content.', + } + )} + + + + + !!field?.trim() || + i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.content.requiredError', + { + defaultMessage: 'A value for this content field is required', + } + ), + }} + render={({ field, fieldState: { error, isTouched } }) => ( + <> + + + + {field.value === ContentFrom.EXTRACTED ? ( + ( + <> + + + + + + )} + /> + ) : ( + field.value === ContentFrom.FIXED && ( + <> + + ( + + + + )} + /> + + ) + )} + + )} + /> +
+
+
+ + + + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.cancelButton.label', + { + defaultMessage: 'Cancel', + } + )} + + + + { + saveRule({ ...getValues() }); + }} + fill + > + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.extractionRules.editContentField.saveButton.label', + { + defaultMessage: 'Save', + } + )} + + + + +
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules.tsx new file mode 100644 index 0000000000000..5f3f734c24cd9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules.tsx @@ -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 React from 'react'; + +import { useActions, useValues } from 'kea'; + +import { + EuiButton, + EuiConfirmModal, + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { CANCEL_BUTTON_LABEL } from '../../../../../../shared/constants'; + +import { EditExtractionRule } from './edit_extraction_rule'; +import { ExtractionRulesLogic } from './extraction_rules_logic'; +import { ExtractionRulesTable } from './extraction_rules_table'; + +export const ExtractionRules: React.FC = () => { + const { + cancelEditExtractionRule, + deleteExtractionRule, + editNewExtractionRule, + hideDeleteModal, + saveExtractionRule, + } = useActions(ExtractionRulesLogic); + const { + deleteModalVisible, + editingExtractionRule, + extractionRules, + extractionRuleToDelete, + extractionRuleToEdit, + extractionRuleToEditIsNew, + } = useValues(ExtractionRulesLogic); + + return ( + <> + {deleteModalVisible && ( + + {i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.deleteModal.description', + { + defaultMessage: + 'Removing this rule will also delete {fields, plural, one {one field rule} other {# field rules}}. This action cannot be undone.', + values: { fields: extractionRuleToDelete?.rules.length ?? 0 }, + } + )} + + )} + + + +

+ {i18n.translate('xpack.enterpriseSearch.content.crawler.extractionRules.title', { + defaultMessage: 'Extraction rules', + })} +

+
+
+ {extractionRules.length === 0 ? ( + <> + ) : ( + + + {i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRulesTable.addRuleLabel', + { + defaultMessage: 'Add extraction rule', + } + )} + + + )} +
+ + +

+ + {i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.learnMoreLink', + { + defaultMessage: 'Learn more about content extraction rules.', + } + )} + + ), + }} + /> +

+
+ {editingExtractionRule ? ( + + ) : extractionRules.length === 0 ? ( + + {i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRulesTable.emptyMessageTitle', + { + defaultMessage: 'There are no content extraction rules', + } + )} +

+ } + titleSize="s" + body={ + + {i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRulesTable.emptyMessageDescription', + { + defaultMessage: + 'Create a content extraction rule to change where document fields get their data during a sync.', + } + )} + + } + actions={ + + {i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRulesTable.emptyMessageAddRuleLabel', + { + defaultMessage: 'Add content extraction rule', + } + )} + + } + /> + ) : ( + + )} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules_logic.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules_logic.tsx new file mode 100644 index 0000000000000..f44d83cb7c99d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules_logic.tsx @@ -0,0 +1,327 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kea, MakeLogicType } from 'kea'; + +import { Status } from '../../../../../../../../common/types/api'; +import { + ExtractionRule, + ExtractionRuleBase, + ExtractionRuleFieldRule, +} from '../../../../../../../../common/types/extraction_rules'; +import { + AddExtractionRuleActions, + AddExtractionRuleApiLogic, +} from '../../../../../api/crawler/extraction_rules/add_extraction_rule_api_logic'; +import { + DeleteExtractionRuleActions, + DeleteExtractionRuleApiLogic, +} from '../../../../../api/crawler/extraction_rules/delete_extraction_rule_api_logic'; +import { + FetchExtractionRulesActions, + FetchExtractionRulesApiLogic, +} from '../../../../../api/crawler/extraction_rules/fetch_extraction_rules_api_logic'; +import { + UpdateExtractionRuleActions, + UpdateExtractionRuleApiLogic, +} from '../../../../../api/crawler/extraction_rules/update_extraction_rule_api_logic'; +import { IndexNameLogic } from '../../../index_name_logic'; + +import { + CrawlerDomainDetailActions, + CrawlerDomainDetailLogic, + CrawlerDomainDetailValues, +} from '../crawler_domain_detail_logic'; + +export type ExtractionRuleView = ExtractionRule & { isExpanded: boolean }; + +interface ExtractionRulesActions { + addExtractionRule: AddExtractionRuleActions['makeRequest']; + addExtractionRuleSuccess: AddExtractionRuleActions['apiSuccess']; + applyDraft: () => void; + cancelEditExtractionRule: () => void; + closeEditRuleFlyout: () => void; + deleteExtractionRule: () => void; + deleteExtractionRuleRequest: DeleteExtractionRuleActions['makeRequest']; + deleteExtractionRuleSuccess: DeleteExtractionRuleActions['apiSuccess']; + deleteFieldRule: () => void; + editExtractionRule(extractionRule: ExtractionRule): { extractionRule: ExtractionRule }; + editNewExtractionRule: () => void; + fetchExtractionRules: FetchExtractionRulesActions['makeRequest']; + fetchExtractionRulesSuccess: FetchExtractionRulesActions['apiSuccess']; + hideDeleteFieldModal: () => void; + hideDeleteModal: () => void; + openEditRuleFlyout({ + fieldRule, + fieldRuleIndex, + isNewRule, + }: { + fieldRule?: ExtractionRuleFieldRule; + fieldRuleIndex?: number; + isNewRule: boolean; + }): { + fieldRule: ExtractionRuleFieldRule; + fieldRuleIndex?: number; + isNewRule: boolean; + }; + fetchDomainData: CrawlerDomainDetailActions['fetchDomainData']; + saveExtractionRule(extractionRule: ExtractionRuleBase): { + extractionRule: ExtractionRuleBase; + }; + setLocalExtractionRules(extractionRules: ExtractionRule[]): { extractionRules: ExtractionRule[] }; + showDeleteFieldModal({ + fieldRuleIndex, + extractionRuleId, + }: { + extractionRuleId: string; + fieldRuleIndex: number; + }): { extractionRuleId: string; fieldRuleIndex: number }; + showDeleteModal(extractionRule: ExtractionRule): { extractionRule: ExtractionRule }; + updateExtractionRule: UpdateExtractionRuleActions['makeRequest']; + updateExtractionRuleSuccess: UpdateExtractionRuleActions['apiSuccess']; +} + +interface ExtractionRulesValues { + addStatus: Status; + deleteFieldModalVisible: boolean; + deleteModalVisible: boolean; + deleteStatus: Status; + domain: CrawlerDomainDetailValues['domain']; + domainExtractionRules: ExtractionRule[] | null; + domainId: string; + editingExtractionRule: boolean; + extractionRuleToDelete: ExtractionRule | null; + extractionRuleToEdit: ExtractionRule | null; + extractionRuleToEditIsNew: boolean; + extractionRules: ExtractionRule[]; + fieldRuleFlyoutVisible: boolean; + fieldRuleToDelete: { extractionRuleId?: string; fieldRuleIndex?: number }; + fieldRuleToEdit: ExtractionRuleFieldRule | null; + fieldRuleToEditIndex: number | null; + fieldRuleToEditIsNew: boolean; + indexName: string; + isLoading: boolean; + isLoadingUpdate: boolean; + jsonValidationError: boolean; + updateStatus: Status; + updatedExtractionRules: ExtractionRule[] | null; +} + +export const ExtractionRulesLogic = kea< + MakeLogicType +>({ + actions: { + cancelEditExtractionRule: true, + closeEditRuleFlyout: true, + deleteExtractionRule: true, + deleteFieldRule: true, + editExtractionRule: (extractionRule) => ({ extractionRule }), + editNewExtractionRule: true, + hideDeleteFieldModal: true, + hideDeleteModal: true, + openEditRuleFlyout: ({ fieldRule, isNewRule }) => ({ + fieldRule, + isNewRule, + }), + saveExtractionRule: (extractionRule: ExtractionRuleBase) => ({ extractionRule }), + showDeleteFieldModal: ({ fieldRuleIndex, extractionRuleId }) => ({ + extractionRuleId, + fieldRuleIndex, + }), + showDeleteModal: (extractionRule: ExtractionRule) => ({ extractionRule }), + }, + connect: { + actions: [ + AddExtractionRuleApiLogic, + ['makeRequest as addExtractionRule', 'apiSuccess as addExtractionRuleSuccess'], + CrawlerDomainDetailLogic, + ['receiveDomainData'], + DeleteExtractionRuleApiLogic, + ['makeRequest as deleteExtractionRuleRequest', 'apiSuccess as deleteExtractionRuleSuccess'], + FetchExtractionRulesApiLogic, + ['makeRequest as fetchExtractionRules', 'apiSuccess as fetchExtractionRulesSuccess'], + UpdateExtractionRuleApiLogic, + ['makeRequest as updateExtractionRule', 'apiSuccess as updateExtractionRuleSuccess'], + ], + values: [ + AddExtractionRuleApiLogic, + ['status as addStatus'], + CrawlerDomainDetailLogic, + ['domain', 'domainId', 'extractionRules as domainExtractionRules', 'getLoading as isLoading'], + DeleteExtractionRuleApiLogic, + ['status as deleteStatus'], + IndexNameLogic, + ['indexName'], + UpdateExtractionRuleApiLogic, + ['status as updateStatus'], + ], + }, + events: ({ actions, values }) => ({ + beforeUnmount: () => { + // This prevents stale data from hanging around on unload + actions.fetchDomainData(values.domainId); + }, + }), + listeners: ({ actions, values }) => ({ + deleteExtractionRule: () => { + if (values.extractionRuleToDelete) { + actions.deleteExtractionRuleRequest({ + domainId: values.domainId, + extractionRuleId: values.extractionRuleToDelete?.id, + indexName: values.indexName, + }); + } + }, + deleteExtractionRuleSuccess: () => { + actions.hideDeleteModal(); + }, + deleteFieldRule: () => { + const { extractionRuleId, fieldRuleIndex } = values.fieldRuleToDelete; + const extractionRule = values.extractionRules.find(({ id }) => id === extractionRuleId); + if (extractionRule) { + const newFieldRules = extractionRule.rules.filter((_, index) => index !== fieldRuleIndex); + actions.updateExtractionRule({ + domainId: values.domainId, + indexName: values.indexName, + rule: { ...extractionRule, rules: newFieldRules }, + }); + } + }, + saveExtractionRule: ({ extractionRule }) => { + if (values.extractionRuleToEditIsNew) { + actions.addExtractionRule({ + domainId: values.domainId, + indexName: values.indexName, + rule: extractionRule, + }); + } else if (values.extractionRuleToEdit) { + actions.updateExtractionRule({ + domainId: values.domainId, + indexName: values.indexName, + rule: { ...values.extractionRuleToEdit, ...extractionRule }, + }); + } + }, + }), + path: ['enterprise_search', 'content', 'crawler', 'extraction_rules'], + reducers: () => ({ + deleteFieldModalVisible: [ + false, + { + hideDeleteFieldModal: () => false, + showDeleteFieldModal: () => true, + updateExtractionRuleSuccess: () => false, + }, + ], + deleteModalVisible: [ + false, + { + deleteExtractionRuleSuccess: () => false, + hideDeleteModal: () => false, + showDeleteModal: () => true, + }, + ], + editingExtractionRule: [ + false, + { + addExtractionRuleSuccess: () => false, + cancelEditExtractionRule: () => false, + editExtractionRule: () => true, + editNewExtractionRule: () => true, + updateExtractionRuleSuccess: () => false, + }, + ], + extractionRuleToDelete: [ + null, + { + deleteExtractionRuleSuccess: () => null, + hideDeleteModal: () => null, + showDeleteModal: (_, { extractionRule }) => extractionRule, + }, + ], + extractionRuleToEdit: [ + null, + { + addSuccess: () => null, + cancelEditExtractionRule: () => null, + editExtractionRule: (_, { extractionRule }) => extractionRule, + updateSuccess: () => null, + }, + ], + extractionRuleToEditIsNew: [ + false, + { + addSuccess: () => false, + editNewExtractionRule: () => true, + }, + ], + fieldRuleFlyoutVisible: [ + false, + { + addExtractionRuleSuccess: () => false, + closeEditRuleFlyout: () => false, + openEditRuleFlyout: () => true, + updateExtractionRuleSuccess: () => false, + }, + ], + fieldRuleToDelete: [ + {}, + { + hideDeleteFieldModal: () => ({}), + showDeleteFieldModal: (_, { extractionRuleId, fieldRuleIndex }) => ({ + extractionRuleId, + fieldRuleIndex, + }), + updateExtractionRuleSuccess: () => ({}), + }, + ], + fieldRuleToEdit: [ + null, + { + closeEditRuleFlyout: () => null, + openEditRuleFlyout: (_, { fieldRule }) => fieldRule ?? null, + }, + ], + fieldRuleToEditIndex: [ + null, + { + closeEditRuleFlyout: () => null, + openEditRuleFlyout: (_, { fieldRuleIndex }) => fieldRuleIndex ?? null, + }, + ], + fieldRuleToEditIsNew: [ + true, + { + closeEditRuleFlyout: () => true, + openEditRuleFlyout: (_, { isNewRule }) => isNewRule, + }, + ], + updatedExtractionRules: [ + null, + { + addExtractionRuleSuccess: (_, { extraction_rules: extractionRules }) => extractionRules, + deleteExtractionRuleSuccess: (_, { extraction_rules: extractionRules }) => extractionRules, + receiveDomainData: () => null, + updateExtractionRuleSuccess: (_, { extraction_rules: extractionRules }) => extractionRules, + }, + ], + }), + selectors: ({ selectors }) => ({ + extractionRules: [ + () => [selectors.domainExtractionRules, selectors.updatedExtractionRules], + ( + domainExtractionRules: ExtractionRule[] | null, + updatedExtractionRules: ExtractionRule[] | null + ) => updatedExtractionRules ?? domainExtractionRules ?? [], + ], + isLoadingUpdate: [ + () => [selectors.updateStatus, selectors.deleteStatus, selectors.addStatus], + (updateStatus: Status, deleteStatus: Status, addStatus: Status) => + [updateStatus, deleteStatus, addStatus].includes(Status.LOADING), + ], + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules_table.test.tsx new file mode 100644 index 0000000000000..2cc9bffb0f54f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules_table.test.tsx @@ -0,0 +1,302 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockFlashMessageHelpers, setMockActions } from '../../../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow, ShallowWrapper } from 'enzyme'; + +import { EuiFieldText, EuiSelect } from '@elastic/eui'; + +import { GenericEndpointInlineEditableTable } from '../../../../../../shared/tables/generic_endpoint_inline_editable_table'; +import { CrawlerPolicies, CrawlerRules } from '../../../../../api/crawler/types'; + +import { CrawlRulesTable, CrawlRulesTableProps } from '../crawl_rules_table'; + +describe('CrawlRulesTable', () => { + const { clearFlashMessages, flashSuccessToast } = mockFlashMessageHelpers; + const indexName = 'index-name'; + const crawlRules = [ + { id: '1', pattern: '*', policy: CrawlerPolicies.allow, rule: CrawlerRules.beginsWith }, + { id: '2', pattern: '*', policy: CrawlerPolicies.deny, rule: CrawlerRules.endsWith }, + ]; + + const DEFAULT_PROPS: CrawlRulesTableProps = { + crawlRules, + domainId: '6113e1407a2f2e6f42489794', + indexName, + }; + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(GenericEndpointInlineEditableTable).exists()).toBe(true); + }); + + describe('columns', () => { + const crawlRule = { + id: '1', + pattern: '*', + policy: CrawlerPolicies.allow, + rule: CrawlerRules.beginsWith, + }; + let wrapper: ShallowWrapper; + + beforeEach(() => { + wrapper = shallow(); + }); + + const renderColumn = (index: number) => { + const columns = wrapper.find(GenericEndpointInlineEditableTable).prop('columns'); + return shallow(
{columns[index].render(crawlRule)}
); + }; + + const onChange = jest.fn(); + const renderColumnInEditingMode = (index: number) => { + const columns = wrapper.find(GenericEndpointInlineEditableTable).prop('columns'); + return shallow( +
+ {columns[index].editingRender(crawlRule, onChange, { + isInvalid: false, + isLoading: false, + })} +
+ ); + }; + + describe('policy column', () => { + it('shows the policy of a crawl rule', () => { + expect(renderColumn(0).html()).toContain('Allow'); + }); + + it('can show the policy of a crawl rule as editable', () => { + const column = renderColumnInEditingMode(0); + + const selectField = column.find(EuiSelect); + expect(selectField.props()).toEqual( + expect.objectContaining({ + disabled: false, + isInvalid: false, + options: [ + { text: 'Allow', value: 'allow' }, + { text: 'Disallow', value: 'deny' }, + ], + value: 'allow', + }) + ); + + selectField.simulate('change', { target: { value: 'deny' } }); + expect(onChange).toHaveBeenCalledWith('deny'); + }); + }); + + describe('rule column', () => { + it('shows the rule of a crawl rule', () => { + expect(renderColumn(1).html()).toContain('Begins with'); + }); + + it('can show the rule of a crawl rule as editable', () => { + const column = renderColumnInEditingMode(1); + + const selectField = column.find(EuiSelect); + expect(selectField.props()).toEqual( + expect.objectContaining({ + disabled: false, + isInvalid: false, + options: [ + { text: 'Begins with', value: 'begins' }, + { text: 'Ends with', value: 'ends' }, + { text: 'Contains', value: 'contains' }, + { text: 'Regex', value: 'regex' }, + ], + value: 'begins', + }) + ); + + selectField.simulate('change', { target: { value: 'ends' } }); + expect(onChange).toHaveBeenCalledWith('ends'); + }); + }); + + describe('pattern column', () => { + it('shows the pattern of a crawl rule', () => { + expect(renderColumn(2).html()).toContain('*'); + }); + + it('can show the pattern of a crawl rule as editable', () => { + const column = renderColumnInEditingMode(2); + + const field = column.find(EuiFieldText); + expect(field.props()).toEqual( + expect.objectContaining({ + disabled: false, + isInvalid: false, + value: '*', + }) + ); + + field.simulate('change', { target: { value: 'foo' } }); + expect(onChange).toHaveBeenCalledWith('foo'); + }); + }); + }); + + describe('routes', () => { + it('can calculate an update and delete route correctly', () => { + const wrapper = shallow(); + + const table = wrapper.find(GenericEndpointInlineEditableTable); + + const crawlRule = { + id: '1', + pattern: '*', + policy: CrawlerPolicies.allow, + rule: CrawlerRules.beginsWith, + }; + expect(table.prop('deleteRoute')(crawlRule)).toEqual( + '/internal/enterprise_search/indices/index-name/crawler/domains/6113e1407a2f2e6f42489794/crawl_rules/1' + ); + expect(table.prop('updateRoute')(crawlRule)).toEqual( + '/internal/enterprise_search/indices/index-name/crawler/domains/6113e1407a2f2e6f42489794/crawl_rules/1' + ); + }); + }); + + it('shows a custom description if one is provided', () => { + const wrapper = shallow( + + ); + + const table = wrapper.find(GenericEndpointInlineEditableTable); + expect(table.prop('description')).toEqual('I am a description'); + }); + + it('shows a default crawl rule as uneditable if one is provided', () => { + const wrapper = shallow( + + ); + + const table = wrapper.find(GenericEndpointInlineEditableTable); + expect(table.prop('uneditableItems')).toEqual([crawlRules[0]]); + }); + + describe('when a crawl rule is added', () => { + it('should update the crawl rules for the current domain, and clear flash messages', () => { + const updateCrawlRules = jest.fn(); + setMockActions({ + updateCrawlRules, + }); + const wrapper = shallow( + + ); + const table = wrapper.find(GenericEndpointInlineEditableTable); + + const crawlRulesThatWasAdded = { + id: '2', + pattern: '*', + policy: CrawlerPolicies.deny, + rule: CrawlerRules.endsWith, + }; + const updatedCrawlRules = [ + { id: '1', pattern: '*', policy: CrawlerPolicies.allow, rule: CrawlerRules.beginsWith }, + { id: '2', pattern: '*', policy: CrawlerPolicies.deny, rule: CrawlerRules.endsWith }, + ]; + table.prop('onAdd')(crawlRulesThatWasAdded, updatedCrawlRules); + expect(updateCrawlRules).toHaveBeenCalledWith(updatedCrawlRules); + expect(clearFlashMessages).toHaveBeenCalled(); + }); + }); + + describe('when a crawl rule is updated', () => { + it('should update the crawl rules for the current domain, and clear flash messages', () => { + const updateCrawlRules = jest.fn(); + setMockActions({ + updateCrawlRules, + }); + const wrapper = shallow( + + ); + const table = wrapper.find(GenericEndpointInlineEditableTable); + + const crawlRulesThatWasUpdated = { + id: '2', + pattern: '*', + policy: CrawlerPolicies.deny, + rule: CrawlerRules.endsWith, + }; + const updatedCrawlRules = [ + { id: '1', pattern: '*', policy: CrawlerPolicies.allow, rule: CrawlerRules.beginsWith }, + { + id: '2', + pattern: 'newPattern', + policy: CrawlerPolicies.deny, + rule: CrawlerRules.endsWith, + }, + ]; + table.prop('onUpdate')(crawlRulesThatWasUpdated, updatedCrawlRules); + expect(updateCrawlRules).toHaveBeenCalledWith(updatedCrawlRules); + expect(clearFlashMessages).toHaveBeenCalled(); + }); + }); + + describe('when a crawl rule is deleted', () => { + it('should update the crawl rules for the current domain, clear flash messages, and show a success', () => { + const updateCrawlRules = jest.fn(); + setMockActions({ + updateCrawlRules, + }); + const wrapper = shallow( + + ); + const table = wrapper.find(GenericEndpointInlineEditableTable); + + const crawlRulesThatWasDeleted = { + id: '2', + pattern: '*', + policy: CrawlerPolicies.deny, + rule: CrawlerRules.endsWith, + }; + const updatedCrawlRules = [ + { id: '1', pattern: '*', policy: CrawlerPolicies.allow, rule: CrawlerRules.beginsWith }, + ]; + table.prop('onDelete')(crawlRulesThatWasDeleted, updatedCrawlRules); + expect(updateCrawlRules).toHaveBeenCalledWith(updatedCrawlRules); + expect(clearFlashMessages).toHaveBeenCalled(); + expect(flashSuccessToast).toHaveBeenCalled(); + }); + }); + + describe('when a crawl rule is reordered', () => { + it('should update the crawl rules for the current domain and clear flash messages', () => { + const updateCrawlRules = jest.fn(); + setMockActions({ + updateCrawlRules, + }); + const wrapper = shallow( + + ); + const table = wrapper.find(GenericEndpointInlineEditableTable); + + const updatedCrawlRules = [ + { id: '2', pattern: '*', policy: CrawlerPolicies.deny, rule: CrawlerRules.endsWith }, + { id: '1', pattern: '*', policy: CrawlerPolicies.allow, rule: CrawlerRules.beginsWith }, + ]; + table.prop('onReorder')!(updatedCrawlRules); + expect(updateCrawlRules).toHaveBeenCalledWith(updatedCrawlRules); + expect(clearFlashMessages).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules_table.tsx new file mode 100644 index 0000000000000..1a2e545dd4fe4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/extraction_rules_table.tsx @@ -0,0 +1,257 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState } from 'react'; + +import { useActions, useValues } from 'kea'; + +import { + EuiBasicTable, + EuiBasicTableColumn, + EuiButtonEmpty, + EuiCode, + EuiConfirmModal, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { FormattedRelative } from '@kbn/i18n-react'; + +import { ExtractionRule } from '../../../../../../../../common/types/extraction_rules'; +import { CANCEL_BUTTON_LABEL } from '../../../../../../shared/constants'; + +import { ContentFieldsPanel } from './content_fields_panel'; +import { ExtractionRulesLogic } from './extraction_rules_logic'; + +export const ExtractionRulesTable: React.FC = () => { + const { + deleteFieldRule, + editExtractionRule, + hideDeleteFieldModal, + openEditRuleFlyout, + showDeleteFieldModal, + showDeleteModal, + } = useActions(ExtractionRulesLogic); + + const { deleteFieldModalVisible, extractionRules } = useValues(ExtractionRulesLogic); + + const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState< + Record + >({}); + + useEffect(() => { + setItemIdToExpandedRowMap({}); + }, [extractionRules]); + + const toggleExpandedItem = (item: ExtractionRule) => { + if (itemIdToExpandedRowMap[item.id]) { + // omit item from rowmap + const { [item.id]: _, ...rest } = itemIdToExpandedRowMap; + setItemIdToExpandedRowMap(rest); + } else { + const rules = item.rules.map((val, index) => ({ ...val, id: `${index}`, index })); + const newItem = ( + + { + editExtractionRule(item); + const rule = rules.find(({ id: ruleId }) => id === ruleId); + if (rule) { + openEditRuleFlyout({ + fieldRule: rule, + fieldRuleIndex: rule.index, + isNewRule: false, + }); + } + }} + editNewField={() => { + editExtractionRule(item); + openEditRuleFlyout({ isNewRule: true }); + }} + removeField={(id) => { + const rule = rules.find(({ id: ruleId }) => id === ruleId); + if (rule) { + showDeleteFieldModal({ extractionRuleId: item.id, fieldRuleIndex: rule.index }); + } + }} + /> + + ); + setItemIdToExpandedRowMap({ ...itemIdToExpandedRowMap, [item.id]: newItem }); + } + }; + + const columns: Array> = [ + { + field: 'description', + name: i18n.translate( + 'xpack.enterpriseSearch.crawler.extractionRulesTable.descriptionTableLabel', + { + defaultMessage: 'Description', + } + ), + textOnly: true, + }, + { + field: 'url_filters', + name: i18n.translate('xpack.enterpriseSearch.crawler.extractionRulesTable.urlsLabel', { + defaultMessage: 'URLs', + }), + render: (filters: ExtractionRule['url_filters']) => ( + + {filters.length > 0 ? ( + filters.map(({ pattern }, index) => ( + + {pattern} + + )) + ) : ( + + {'/*'} + + )} + + ), + }, + { + name: i18n.translate('xpack.enterpriseSearch.crawler.extractionRulesTable.rulesLabel', { + defaultMessage: 'Field rules', + }), + render: (rule: ExtractionRule) => ( + toggleExpandedItem(rule)}> + {rule.rules.length} + + ), + textOnly: true, + }, + { + field: 'updated_at', + name: i18n.translate('xpack.enterpriseSearch.crawler.extractionRulesTable.lastUpdatedLabel', { + defaultMessage: 'Last updated', + }), + render: (lastUpdated: string) => , + textOnly: true, + }, + { + field: 'edited_by', + name: i18n.translate('xpack.enterpriseSearch.crawler.extractionRulesTable.editedByLabel', { + defaultMessage: 'Edited by', + }), + render: (editedBy: string) => editedBy, + textOnly: true, + }, + { + actions: [ + { + description: i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.actions.editRule.title', + { + defaultMessage: 'Edit this extraction rule', + } + ), + icon: 'pencil', + isPrimary: false, + name: i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.actions.editRule.caption', + { + defaultMessage: 'Edit this extraction rule', + } + ), + onClick: (extractionRule) => editExtractionRule(extractionRule), + type: 'icon', + }, + { + color: 'danger', + description: i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.actions.deleteRule.title', + { + defaultMessage: 'Delete this extraction rule', + } + ), + icon: 'trash', + isPrimary: false, + name: i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.actions.deleteRule.caption', + { + defaultMessage: 'Delete extraction rule', + } + ), + onClick: (extractionRule) => showDeleteModal(extractionRule), + type: 'icon', + }, + { + color: 'primary', + description: i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.actions.expandRule.title', + { + defaultMessage: 'Expand this extraction rule', + } + ), + icon: (item) => (!!itemIdToExpandedRowMap[item.id] ? 'arrowUp' : 'arrowDown'), + isPrimary: true, + name: i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.actions.expandRule.caption', + { + defaultMessage: 'Expand rule', + } + ), + onClick: (extractionRule) => toggleExpandedItem(extractionRule), + type: 'icon', + }, + ], + name: i18n.translate('xpack.enterpriseSearch.content.crawler.extractionRules.actions.label', { + defaultMessage: 'Actions', + }), + }, + ]; + + return ( + <> + {deleteFieldModalVisible && ( + + {i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.deleteFieldModal.description', + { + defaultMessage: 'This action cannot be undone.', + } + )} + + )} + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/field_rules_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/field_rules_table.tsx new file mode 100644 index 0000000000000..c8f6b3a882a96 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/extraction_rules/field_rules_table.tsx @@ -0,0 +1,151 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { EuiBasicTable, EuiBasicTableColumn, EuiCode, EuiFlexGroup, EuiText } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { + ContentFrom, + ExtractionRuleFieldRule, + FieldType, + MultipleObjectsHandling, +} from '../../../../../../../../common/types/extraction_rules'; + +type FieldRuleWithId = ExtractionRuleFieldRule & { id: string }; + +export interface FieldRulesTableProps { + editRule: (id: string) => void; + fieldRules: FieldRuleWithId[]; + removeRule: (id: string) => void; +} + +export const FieldRulesTable: React.FC = ({ + editRule, + fieldRules, + removeRule, +}) => { + const columns: Array> = [ + { + field: 'field_name', + name: i18n.translate( + 'xpack.enterpriseSearch.crawler.extractionRules.fieldRulesTable.fieldNameLabel', + { + defaultMessage: 'Field name', + } + ), + textOnly: true, + }, + { + name: i18n.translate('xpack.enterpriseSearch.crawler.extractionRulesTable.sourceLabel', { + defaultMessage: 'Source', + }), + render: (rule: FieldRuleWithId) => ( + + + {rule.source_type === FieldType.HTML + ? i18n.translate('xpack.enterpriseSearch.crawler.fieldRulesTable.HTMLLabel', { + defaultMessage: 'HTML: ', + }) + : i18n.translate('xpack.enterpriseSearch.crawler.fieldRulesTable.UrlLabel', { + defaultMessage: 'URL: ', + })} + + {rule.selector} + + ), + }, + { + name: i18n.translate('xpack.enterpriseSearch.crawler.fieldRulesTable.contentLabel', { + defaultMessage: 'Content', + }), + render: ({ + content_from: content, + multiple_objects_handling: multipleObjectsHandling, + }: FieldRuleWithId) => ( + + + {content.value_type === ContentFrom.EXTRACTED + ? i18n.translate('xpack.enterpriseSearch.crawler.fieldRulesTable.extractedLabel', { + defaultMessage: 'Extracted as: ', + }) + : i18n.translate('xpack.enterpriseSearch.crawler.fieldRulesTable.fixedLabel', { + defaultMessage: 'Fixed value: ', + })} + + + {content.value_type === ContentFrom.FIXED + ? content.value + : multipleObjectsHandling === MultipleObjectsHandling.ARRAY + ? i18n.translate('xpack.enterpriseSearch.crawler.fieldRulesTable.arrayLabel', { + defaultMessage: 'array', + }) + : i18n.translate('xpack.enterpriseSearch.crawler.fieldRulesTable.stringLabel', { + defaultMessage: 'string', + })} + + + ), + }, + { + actions: [ + { + description: i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.fieldRulesTable.editRule.title', + { + defaultMessage: 'Edit this content field rule', + } + ), + icon: 'pencil', + isPrimary: false, + name: i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.fieldRulesTable.editRule.caption', + { + defaultMessage: 'Edit this content field rule', + } + ), + onClick: ({ id }) => editRule(id), + type: 'icon', + }, + { + color: 'danger', + description: i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.actions.deleteRule.title', + { + defaultMessage: 'Delete this extraction rule', + } + ), + icon: 'trash', + isPrimary: false, + name: i18n.translate( + 'xpack.enterpriseSearch.content.crawler.extractionRules.actions.deleteRule.caption', + { + defaultMessage: 'Delete extraction rule', + } + ), + onClick: ({ id }) => removeRule(id), + type: 'icon', + }, + ], + name: i18n.translate('xpack.enterpriseSearch.content.crawler.extractionRules.actions.label', { + defaultMessage: 'Actions', + }), + }, + ]; + + return ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/sitemaps_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/sitemaps_table.test.tsx index 1cae5f5cbe096..e556c32b6eded 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/sitemaps_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/sitemaps_table.test.tsx @@ -35,6 +35,7 @@ describe('SitemapsTable', () => { url: 'https://www.elastic.co', crawlRules: [], entryPoints: [], + extractionRules: [], sitemaps, deduplicationEnabled: true, deduplicationFields: ['title'], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/sitemaps_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/sitemaps_table.tsx index c486cb2eeaed7..80ae2e886b095 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/sitemaps_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_domain_detail/sitemaps_table.tsx @@ -68,7 +68,8 @@ export const SitemapsTable: React.FC = ({ domain, indexName, description={

{i18n.translate('xpack.enterpriseSearch.crawler.sitemapsTable.description', { - defaultMessage: 'Specify sitemap URLs for the crawler on this domain.', + defaultMessage: + 'Add custom sitemap URLs for this domain. The crawler automatically detects existing sitemaps.', })}

} @@ -112,9 +113,7 @@ export const SitemapsTable: React.FC = ({ domain, indexName, updateSitemaps(newSitemaps as Sitemap[]); clearFlashMessages(); }} - title={i18n.translate('xpack.enterpriseSearch.crawler.sitemapsTable.title', { - defaultMessage: 'Sitemaps', - })} + title="" disableReordering /> ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/domain_management/domains_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/domain_management/domains_table.test.tsx index 350ef6ba68672..37250d99e789a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/domain_management/domains_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/domain_management/domains_table.test.tsx @@ -37,6 +37,7 @@ const domains: CrawlerDomain[] = [ createdOn: '2020-01-01T00:00:00-12:00', deduplicationEnabled: false, deduplicationFields: ['title'], + extractionRules: [], availableDeduplicationFields: ['title', 'description'], auth: null, }, @@ -46,6 +47,7 @@ const domains: CrawlerDomain[] = [ url: 'empty.site', crawlRules: [], entryPoints: [], + extractionRules: [], sitemaps: [], createdOn: '1970-01-01T00:00:00-12:00', deduplicationEnabled: false, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx index 822add8f8bde3..c885160693f44 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx @@ -16,6 +16,7 @@ import { i18n } from '@kbn/i18n'; import { SyncStatus } from '../../../../../../common/types/connectors'; import { FormattedDateTime } from '../../../../shared/formatted_date_time'; +import { pageToPagination } from '../../../../shared/pagination/page_to_pagination'; import { durationToText } from '../../../utils/duration_to_text'; import { syncStatusToColor, syncStatusToText } from '../../../utils/sync_status_to_text'; @@ -35,8 +36,8 @@ export const SyncJobs: React.FC = () => { if (connectorId) { fetchSyncJobs({ connectorId, - page: syncJobsPagination.pageIndex ?? 0, - size: syncJobsPagination.pageSize ?? 10, + from: syncJobsPagination.from ?? 0, + size: syncJobsPagination.size ?? 10, }); } }, [connectorId]); @@ -119,13 +120,10 @@ export const SyncJobs: React.FC = () => { hasActions onChange={({ page: { index, size } }: { page: { index: number; size: number } }) => { if (connectorId) { - fetchSyncJobs({ connectorId, page: index, size }); + fetchSyncJobs({ connectorId, from: index * size, size }); } }} - pagination={{ - ...syncJobsPagination, - totalItemCount: syncJobsPagination.total, - }} + pagination={pageToPagination(syncJobsPagination)} tableLayout="fixed" loading={syncJobsLoading} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts index 28af9f3d1a72d..1b965632194b8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts @@ -32,11 +32,9 @@ const DEFAULT_VALUES = { syncJobsData: undefined, syncJobsLoading: true, syncJobsPagination: { - data: [], + from: 0, has_more_hits_than_total: false, - pageIndex: 0, - pageSize: 10, - size: 0, + size: 10, total: 0, }, syncJobsStatus: Status.IDLE, @@ -93,30 +91,35 @@ describe('SyncJobsViewLogic', () => { }; it('should update values', async () => { FetchSyncJobsApiLogic.actions.apiSuccess({ + _meta: { + page: { + from: 40, + has_more_hits_than_total: false, + size: 20, + total: 50, + }, + }, data: [syncJob], - has_more_hits_than_total: false, - pageIndex: 3, - pageSize: 20, - size: 20, - total: 50, }); await nextTick(); expect(SyncJobsViewLogic.values).toEqual({ ...DEFAULT_VALUES, syncJobs: [syncJobView], syncJobsData: { + _meta: { + page: { + from: 40, + has_more_hits_than_total: false, + size: 20, + total: 50, + }, + }, data: [syncJob], - has_more_hits_than_total: false, - pageIndex: 3, - pageSize: 20, - size: 20, - total: 50, }, syncJobsLoading: false, syncJobsPagination: { + from: 40, has_more_hits_than_total: false, - pageIndex: 3, - pageSize: 20, size: 20, total: 50, }, @@ -125,6 +128,14 @@ describe('SyncJobsViewLogic', () => { }); it('should update values for incomplete job', async () => { FetchSyncJobsApiLogic.actions.apiSuccess({ + _meta: { + page: { + from: 40, + has_more_hits_than_total: false, + size: 20, + total: 50, + }, + }, data: [ { ...syncJob, @@ -133,11 +144,6 @@ describe('SyncJobsViewLogic', () => { status: SyncStatus.IN_PROGRESS, }, ], - has_more_hits_than_total: false, - pageIndex: 3, - pageSize: 20, - size: 20, - total: 50, }); await nextTick(); expect(SyncJobsViewLogic.values).toEqual({ @@ -153,6 +159,14 @@ describe('SyncJobsViewLogic', () => { }, ], syncJobsData: { + _meta: { + page: { + from: 40, + has_more_hits_than_total: false, + size: 20, + total: 50, + }, + }, data: [ { ...syncJob, @@ -161,17 +175,11 @@ describe('SyncJobsViewLogic', () => { status: SyncStatus.IN_PROGRESS, }, ], - has_more_hits_than_total: false, - pageIndex: 3, - pageSize: 20, - size: 20, - total: 50, }, syncJobsLoading: false, syncJobsPagination: { + from: 40, has_more_hits_than_total: false, - pageIndex: 3, - pageSize: 20, size: 20, total: 50, }, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.ts index e8feb159dbcd0..cd17eb9a931d6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.ts @@ -12,7 +12,7 @@ import moment from 'moment'; import { Status } from '../../../../../../common/types/api'; import { ConnectorSyncJob } from '../../../../../../common/types/connectors'; -import { Paginate } from '../../../../../../common/types/pagination'; +import { Page, Paginate } from '../../../../../../common/types/pagination'; import { Actions } from '../../../../shared/api_logic/create_api_logic'; import { FetchSyncJobsApiLogic, @@ -37,7 +37,7 @@ export interface IndexViewValues { syncJobs: SyncJobView[]; syncJobsData: Paginate | null; syncJobsLoading: boolean; - syncJobsPagination: Paginate; + syncJobsPagination: Page; syncJobsStatus: Status; } @@ -85,13 +85,11 @@ export const SyncJobsViewLogic = kea [selectors.syncJobsData], (data?: Paginate) => data - ? { ...data, data: undefined } + ? data._meta.page : { - data: [], + from: 0, has_more_hits_than_total: false, - pageIndex: 0, - pageSize: 10, - size: 0, + size: 10, total: 0, }, ], diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index 1d6de5d5b3b93..2b25fabe056f0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -45,6 +45,7 @@ export const renderApp = ( const noProductAccess: ProductAccess = { hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }; const productAccess = data.access || noProductAccess; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/__snapshots__/cron_editor.test.tsx.snap b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/__snapshots__/cron_editor.test.tsx.snap new file mode 100644 index 0000000000000..1b376f8653c7c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/__snapshots__/cron_editor.test.tsx.snap @@ -0,0 +1,11108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CronEditor is rendered with a DAY frequency 1`] = ` + + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ + + } + labelType="label" + > +
+
+ + + +
+
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "bqv98l-euiFlexGroup-responsive-xs-flexStart-stretch-row", + "next": undefined, + "styles": "display:flex;align-items:stretch;flex-grow:1;label:euiFlexGroup;;;@media only screen and (max-width: 767px){flex-wrap:wrap;&>.euiFlexItem{inline-size: 100%; flex-basis:100%;}};label:responsive;;;;gap:4px;;label:xs;;;justify-content:flex-start;label:flexStart;;;align-items:stretch;label:stretch;;;flex-direction:row;label:row;;;", + "toString": [Function], + } + } + /> +
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "kpsrin-euiFlexItem-growZero", + "next": undefined, + "styles": "display:flex;flex-direction:column;label:euiFlexItem;;;flex-grow:0;flex-basis:auto;label:growZero;;;;", + "toString": [Function], + } + } + /> +
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "9sbomz-euiFlexItem-grow-1", + "next": undefined, + "styles": "display:flex;flex-direction:column;label:euiFlexItem;;;flex-basis:0%;label:grow;;;flex-grow:1;label:1;;;", + "toString": [Function], + } + } + /> +
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`CronEditor is rendered with a HOUR frequency 1`] = ` + + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`CronEditor is rendered with a MINUTE frequency 1`] = ` + + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+`; + +exports[`CronEditor is rendered with a MONTH frequency 1`] = ` + + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ + } + labelType="label" + > +
+
+ + + +
+
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "bqv98l-euiFlexGroup-responsive-xs-flexStart-stretch-row", + "next": undefined, + "styles": "display:flex;align-items:stretch;flex-grow:1;label:euiFlexGroup;;;@media only screen and (max-width: 767px){flex-wrap:wrap;&>.euiFlexItem{inline-size: 100%; flex-basis:100%;}};label:responsive;;;;gap:4px;;label:xs;;;justify-content:flex-start;label:flexStart;;;align-items:stretch;label:stretch;;;flex-direction:row;label:row;;;", + "toString": [Function], + } + } + /> +
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "kpsrin-euiFlexItem-growZero", + "next": undefined, + "styles": "display:flex;flex-direction:column;label:euiFlexItem;;;flex-grow:0;flex-basis:auto;label:growZero;;;;", + "toString": [Function], + } + } + /> +
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "9sbomz-euiFlexItem-grow-1", + "next": undefined, + "styles": "display:flex;flex-direction:column;label:euiFlexItem;;;flex-basis:0%;label:grow;;;flex-grow:1;label:1;;;", + "toString": [Function], + } + } + /> +
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`CronEditor is rendered with a WEEK frequency 1`] = ` + + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ + } + labelType="label" + > +
+
+ + + +
+
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "bqv98l-euiFlexGroup-responsive-xs-flexStart-stretch-row", + "next": undefined, + "styles": "display:flex;align-items:stretch;flex-grow:1;label:euiFlexGroup;;;@media only screen and (max-width: 767px){flex-wrap:wrap;&>.euiFlexItem{inline-size: 100%; flex-basis:100%;}};label:responsive;;;;gap:4px;;label:xs;;;justify-content:flex-start;label:flexStart;;;align-items:stretch;label:stretch;;;flex-direction:row;label:row;;;", + "toString": [Function], + } + } + /> +
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "kpsrin-euiFlexItem-growZero", + "next": undefined, + "styles": "display:flex;flex-direction:column;label:euiFlexItem;;;flex-grow:0;flex-basis:auto;label:growZero;;;;", + "toString": [Function], + } + } + /> +
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "9sbomz-euiFlexItem-grow-1", + "next": undefined, + "styles": "display:flex;flex-direction:column;label:euiFlexItem;;;flex-basis:0%;label:grow;;;flex-grow:1;label:1;;;", + "toString": [Function], + } + } + /> +
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`CronEditor is rendered with a YEAR frequency 1`] = ` + + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ + } + labelType="label" + > +
+
+ + + +
+
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "bqv98l-euiFlexGroup-responsive-xs-flexStart-stretch-row", + "next": undefined, + "styles": "display:flex;align-items:stretch;flex-grow:1;label:euiFlexGroup;;;@media only screen and (max-width: 767px){flex-wrap:wrap;&>.euiFlexItem{inline-size: 100%; flex-basis:100%;}};label:responsive;;;;gap:4px;;label:xs;;;justify-content:flex-start;label:flexStart;;;align-items:stretch;label:stretch;;;flex-direction:row;label:row;;;", + "toString": [Function], + } + } + /> +
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "kpsrin-euiFlexItem-growZero", + "next": undefined, + "styles": "display:flex;flex-direction:column;label:euiFlexItem;;;flex-grow:0;flex-basis:auto;label:growZero;;;;", + "toString": [Function], + } + } + /> +
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+ +
+ + + + + + , + "ctr": 4, + "insertionPoint": undefined, + "isSpeedy": false, + "key": "css", + "nonce": undefined, + "prepend": undefined, + "tags": Array [ + , + , + , + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "9sbomz-euiFlexItem-grow-1", + "next": undefined, + "styles": "display:flex;flex-direction:column;label:euiFlexItem;;;flex-basis:0%;label:grow;;;flex-grow:1;label:1;;;", + "toString": [Function], + } + } + /> +
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.test.tsx index c62b2336201fb..44f69c9fa6e33 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.test.tsx @@ -17,9 +17,7 @@ import { CronEditor } from './cron_editor'; describe('CronEditor', () => { ['MINUTE', 'HOUR', 'DAY', 'WEEK', 'MONTH', 'YEAR'].forEach((unit) => { - // Skipped during Jest 29 upgrade - // No snapshot has been committed - test.skip(`is rendered with a ${unit} frequency`, () => { + it(`is rendered with a ${unit} frequency`, () => { const component = mountWithI18nProvider( { @@ -28,6 +26,7 @@ describe('useEnterpriseSearchContentNav', () => { it('returns an array of top-level Enterprise Search nav items', () => { const fullProductAccess: ProductAccess = { hasAppSearchAccess: true, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: true, }; setMockValues({ productAccess: fullProductAccess }); @@ -93,12 +92,12 @@ describe('useEnterpriseSearchContentNav', () => { name: 'Search', }, ]); - expect(mockKibanaValues.uiSettings.get).toHaveBeenCalledWith(enableEnginesSection, false); }); it('excludes legacy products when the user has no access to them', () => { const noProductAccess: ProductAccess = { hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }; @@ -129,6 +128,7 @@ describe('useEnterpriseSearchContentNav', () => { it('excludes App Search when the user has no access to it', () => { const workplaceSearchProductAccess: ProductAccess = { hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: true, }; @@ -163,6 +163,7 @@ describe('useEnterpriseSearchContentNav', () => { it('excludes Workplace Search when the user has no access to it', () => { const appSearchProductAccess: ProductAccess = { hasAppSearchAccess: true, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }; @@ -197,6 +198,7 @@ describe('useEnterpriseSearchContentNav', () => { it('excludes engines when feature flag is off', () => { const fullProductAccess: ProductAccess = { hasAppSearchAccess: true, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: true, }; setMockValues({ productAccess: fullProductAccess }); @@ -209,12 +211,12 @@ describe('useEnterpriseSearchContentNav', () => { describe('useEnterpriseSearchContentNav Engines feature flag', () => { beforeEach(() => { jest.clearAllMocks(); - mockKibanaValues.uiSettings.get.mockReturnValue(true); }); it('returns an array of top-level Enterprise Search nav items', () => { const fullProductAccess: ProductAccess = { hasAppSearchAccess: true, + hasSearchEnginesAccess: true, hasWorkplaceSearchAccess: true, }; setMockValues({ productAccess: fullProductAccess }); @@ -286,12 +288,12 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => { name: 'Standalone Experiences', }, ]); - expect(mockKibanaValues.uiSettings.get).toHaveBeenCalledWith(enableEnginesSection, false); }); it('excludes standalone experiences when the user has no access to them', () => { const fullProductAccess: ProductAccess = { hasAppSearchAccess: false, + hasSearchEnginesAccess: true, hasWorkplaceSearchAccess: false, }; setMockValues({ productAccess: fullProductAccess }); @@ -302,6 +304,7 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => { it('excludes App Search when the user has no access to it', () => { const fullProductAccess: ProductAccess = { hasAppSearchAccess: false, + hasSearchEnginesAccess: true, hasWorkplaceSearchAccess: true, }; setMockValues({ productAccess: fullProductAccess }); @@ -324,6 +327,7 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => { it('excludes Workplace Search when the user has no access to it', () => { const fullProductAccess: ProductAccess = { hasAppSearchAccess: true, + hasSearchEnginesAccess: true, hasWorkplaceSearchAccess: false, }; setMockValues({ productAccess: fullProductAccess }); @@ -351,6 +355,7 @@ describe('useEnterpriseSearchEngineNav', () => { mockKibanaValues.uiSettings.get.mockReturnValue(true); const fullProductAccess: ProductAccess = { hasAppSearchAccess: true, + hasSearchEnginesAccess: true, hasWorkplaceSearchAccess: true, }; setMockValues({ productAccess: fullProductAccess }); @@ -424,7 +429,6 @@ describe('useEnterpriseSearchEngineNav', () => { name: 'Standalone Experiences', }, ]); - expect(mockKibanaValues.uiSettings.get).toHaveBeenCalledWith(enableEnginesSection, false); }); it('returns selected engine sub nav items', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx index 8ffb822352324..a6e550e7c58b2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx @@ -19,7 +19,6 @@ import { SEARCH_EXPERIENCES_PLUGIN, WORKPLACE_SEARCH_PLUGIN, } from '../../../../common/constants'; -import { enableEnginesSection } from '../../../../common/ui_settings_keys'; import { ENGINES_PATH, SEARCH_INDICES_PATH, @@ -31,9 +30,9 @@ import { KibanaLogic } from '../kibana'; import { generateNavLink } from './nav_link_helpers'; export const useEnterpriseSearchNav = () => { - const { productAccess, uiSettings } = useValues(KibanaLogic); + const { productAccess } = useValues(KibanaLogic); - const enginesSectionEnabled = uiSettings?.get(enableEnginesSection, false); + const enginesSectionEnabled = productAccess.hasSearchEnginesAccess; const navItems: Array> = [ { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/pagination/page_to_pagination.ts b/x-pack/plugins/enterprise_search/public/applications/shared/pagination/page_to_pagination.ts new file mode 100644 index 0000000000000..4283ea4c3e6b9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/pagination/page_to_pagination.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 { Page } from '../../../../common/types/pagination'; + +export function pageToPagination(page: Page) { + // Prevent divide-by-zero-error + const pageIndex = page.size ? Math.trunc(page.from / page.size) : 0; + return { + pageIndex, + pageSize: page.size, + totalItemCount: page.total, + }; +} diff --git a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts index e312bada49e41..6ba4d9484ea87 100644 --- a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts +++ b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts @@ -38,6 +38,9 @@ describe('Setup Indices', () => { configuration: { type: 'object', }, + custom_scheduling: { + type: 'object', + }, description: { type: 'text' }, error: { type: 'keyword' }, features: { diff --git a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts index 36757667193a3..10ff75fcb566d 100644 --- a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts +++ b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts @@ -30,6 +30,7 @@ interface IndexDefinition { const connectorMappingsProperties: Record = { api_key_id: { type: 'keyword' }, configuration: { type: 'object' }, + custom_scheduling: { type: 'object' }, description: { type: 'text' }, error: { type: 'keyword' }, features: { 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 3da63a63828ae..d2be503dbdebd 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 @@ -59,6 +59,7 @@ describe('checkAccess', () => { }; expect(await checkAccess({ ...mockDependencies, security })).toEqual({ hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }); }); @@ -71,6 +72,7 @@ describe('checkAccess', () => { }; expect(await checkAccess({ ...mockDependencies, request })).toEqual({ hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }); }); @@ -81,6 +83,7 @@ describe('checkAccess', () => { mockSpaces.spacesService.getActiveSpace.mockResolvedValueOnce(disabledSpace); expect(await checkAccess({ ...mockDependencies })).toEqual({ hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }); }); @@ -94,6 +97,7 @@ describe('checkAccess', () => { ); expect(await checkAccess({ ...mockDependencies })).toEqual({ hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }); }); @@ -134,6 +138,7 @@ describe('checkAccess', () => { }; expect(await checkAccess({ ...mockDependencies, security })).toEqual({ hasAppSearchAccess: true, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: true, }); }); @@ -149,6 +154,7 @@ describe('checkAccess', () => { }; expect(await checkAccess({ ...mockDependencies, security })).toEqual({ hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }); }); @@ -170,6 +176,7 @@ describe('checkAccess', () => { const config = { host: undefined }; expect(await checkAccess({ ...mockDependencies, config })).toEqual({ hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }); }); @@ -180,11 +187,13 @@ describe('checkAccess', () => { (callEnterpriseSearchConfigAPI as jest.Mock).mockImplementationOnce(() => ({ access: { hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: true, }, })); expect(await checkAccess(mockDependencies)).toEqual({ hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: true, }); }); @@ -193,6 +202,7 @@ describe('checkAccess', () => { (callEnterpriseSearchConfigAPI as jest.Mock).mockImplementationOnce(() => ({})); expect(await checkAccess(mockDependencies)).toEqual({ hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }); }); @@ -204,6 +214,7 @@ describe('checkAccess', () => { })); expect(await checkAccess(mockDependencies)).toEqual({ hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }); }); diff --git a/x-pack/plugins/enterprise_search/server/lib/check_access.ts b/x-pack/plugins/enterprise_search/server/lib/check_access.ts index 444fa9d4fdb29..1f351d05785ad 100644 --- a/x-pack/plugins/enterprise_search/server/lib/check_access.ts +++ b/x-pack/plugins/enterprise_search/server/lib/check_access.ts @@ -23,12 +23,14 @@ interface CheckAccess { log: Logger; } -const ALLOW_ALL_PLUGINS = { +const ALLOW_ALL_PLUGINS: ProductAccess = { hasAppSearchAccess: true, + hasSearchEnginesAccess: false, // still false unless Feature Flag explicitly enabled on backend hasWorkplaceSearchAccess: true, }; -const DENY_ALL_PLUGINS = { +const DENY_ALL_PLUGINS: ProductAccess = { hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }; diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts index 14c3532efee91..e6584c0a8b205 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts @@ -85,6 +85,7 @@ describe('addConnector lib function', () => { document: { api_key_id: null, configuration: {}, + custom_scheduling: {}, description: null, error: null, features: null, @@ -269,6 +270,7 @@ describe('addConnector lib function', () => { document: { api_key_id: null, configuration: {}, + custom_scheduling: {}, description: null, error: null, features: null, @@ -375,6 +377,7 @@ describe('addConnector lib function', () => { document: { api_key_id: null, configuration: {}, + custom_scheduling: {}, description: null, error: null, features: null, diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts index e697cf57522f8..507ec3fd0bb4f 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts @@ -93,6 +93,7 @@ export const addConnector = async ( const document: ConnectorDocument = { api_key_id: null, configuration: {}, + custom_scheduling: {}, description: null, error: null, features: null, diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.test.ts index 6766f0c7535d8..5408ac4d4a9e7 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.test.ts @@ -31,12 +31,15 @@ describe('fetchSyncJobs lib', () => { Promise.resolve({ hits: { hits: ['result1', 'result2'] }, total: 2 }) ); await expect(fetchSyncJobsByConnectorId(mockClient as any, 'id', 0, 10)).resolves.toEqual({ + _meta: { + page: { + from: 0, + has_more_hits_than_total: false, + size: 10, + total: 0, + }, + }, data: [], - has_more_hits_than_total: false, - pageIndex: 0, - pageSize: 10, - size: 0, - total: 0, }); expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ from: 0, @@ -56,12 +59,15 @@ describe('fetchSyncJobs lib', () => { }); it('should return empty result if size is 0', async () => { await expect(fetchSyncJobsByConnectorId(mockClient as any, 'id', 0, 0)).resolves.toEqual({ + _meta: { + page: { + from: 0, + has_more_hits_than_total: false, + size: 10, + total: 0, + }, + }, data: [], - has_more_hits_than_total: false, - pageIndex: 0, - pageSize: 0, - size: 0, - total: 0, }); expect(mockClient.asCurrentUser.search).not.toHaveBeenCalled(); }); @@ -78,12 +84,15 @@ describe('fetchSyncJobs lib', () => { }) ); await expect(fetchSyncJobsByConnectorId(mockClient as any, 'id', 0, 10)).resolves.toEqual({ + _meta: { + page: { + from: 0, + has_more_hits_than_total: false, + size: 10, + total: 0, + }, + }, data: [], - has_more_hits_than_total: false, - pageIndex: 0, - pageSize: 10, - size: 0, - total: 0, }); expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ from: 0, @@ -114,13 +123,14 @@ describe('fetchSyncJobs lib', () => { }, }) ); - await expect(fetchSyncJobsByConnectorId(mockClient as any, 'id', 0, 10)).resolves.toEqual({ - data: [], - has_more_hits_than_total: false, - pageIndex: 0, - pageSize: 10, - size: 0, - total: 0, + await expect(fetchSyncJobsByConnectorId(mockClient as any, 'id', 0, 10)).rejects.toEqual({ + meta: { + body: { + error: { + type: 'other error', + }, + }, + }, }); expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ from: 0, diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.ts index a4e2fe119eed5..25369fa7979bb 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_sync_jobs.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; import { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; import { CONNECTORS_JOBS_INDEX } from '../..'; @@ -14,75 +13,56 @@ import { Paginate } from '../../../common/types/pagination'; import { isNotNullish } from '../../../common/utils/is_not_nullish'; import { setupConnectorsIndices } from '../../index_management/setup_indices'; +import { fetchWithPagination } from '../../utils/fetch_with_pagination'; import { isIndexNotFoundException } from '../../utils/identify_exceptions'; +const defaultResult: Paginate = { + _meta: { + page: { + from: 0, + has_more_hits_than_total: false, + size: 10, + total: 0, + }, + }, + data: [], +}; + export const fetchSyncJobsByConnectorId = async ( client: IScopedClusterClient, connectorId: string, - pageIndex: number, + from: number, size: number ): Promise> => { try { - if (size === 0) { - // prevent some divide by zero errors below - return { - data: [], - has_more_hits_than_total: false, - pageIndex: 0, - pageSize: size, - size: 0, - total: 0, - }; - } - const result = await client.asCurrentUser.search({ - from: pageIndex * size, - index: CONNECTORS_JOBS_INDEX, - query: { - term: { - 'connector.id': connectorId, - }, - }, - size, - // @ts-ignore Elasticsearch-js has the wrong internal typing for this field - sort: { created_at: { order: 'desc' } }, - }); - const total = totalToPaginateTotal(result.hits.total); - // If we get fewer results than the target page, make sure we return correct page we're on - const resultPageIndex = Math.min(pageIndex, Math.trunc(total.total / size)); - const data = - result.hits.hits - .map((hit) => (hit._source ? { ...hit._source, id: hit._id } : null)) - .filter(isNotNullish) ?? []; + const result = await fetchWithPagination( + async () => + await client.asCurrentUser.search({ + from, + index: CONNECTORS_JOBS_INDEX, + query: { + term: { + 'connector.id': connectorId, + }, + }, + size, + sort: { created_at: { order: 'desc' } }, + }), + from, + size + ); return { - data, - pageIndex: resultPageIndex, - pageSize: size, - size: data.length, - ...total, + ...result, + data: result.data + .map((hit) => (hit._source ? { ...hit._source, id: hit._id } : null)) + .filter(isNotNullish), }; } catch (error) { if (isIndexNotFoundException(error)) { await setupConnectorsIndices(client.asCurrentUser); + return defaultResult; + } else { + throw error; } - return { - data: [], - has_more_hits_than_total: false, - pageIndex: 0, - pageSize: size, - size: 0, - total: 0, - }; } }; - -function totalToPaginateTotal(input: number | SearchTotalHits | undefined): { - has_more_hits_than_total: boolean; - total: number; -} { - if (typeof input === 'number') { - return { has_more_hits_than_total: false, total: input }; - } - return input - ? { has_more_hits_than_total: input.relation === 'gte' ? true : false, total: input.value } - : { has_more_hits_than_total: false, total: 0 }; -} diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts index 144a9b1dbc8b3..25956b271245a 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts @@ -35,6 +35,7 @@ describe('addConnector lib function', () => { api_key_id: null, configuration: {}, created_at: null, + custom_scheduling: {}, error: null, index_name: 'index_name', last_seen: null, @@ -59,6 +60,7 @@ describe('addConnector lib function', () => { api_key_id: null, configuration: {}, created_at: null, + custom_scheduling: {}, error: null, index_name: 'index_name', last_seen: null, diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts index 68d3f7b17ef58..2fa4c0954b245 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts @@ -34,6 +34,7 @@ describe('addConnector lib function', () => { api_key_id: null, configuration: {}, created_at: null, + custom_scheduling: {}, error: null, index_name: 'index_name', last_seen: null, @@ -61,6 +62,7 @@ describe('addConnector lib function', () => { api_key_id: null, configuration: {}, created_at: null, + custom_scheduling: {}, error: null, index_name: 'index_name', last_seen: null, diff --git a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts index 5826fbca88b5c..41031eaf06678 100644 --- a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts @@ -70,6 +70,7 @@ describe('callEnterpriseSearchConfigAPI', () => { name: 'someuser', access: { app_search: true, + search_engines: true, workplace_search: false, }, app_search: { @@ -123,6 +124,7 @@ describe('callEnterpriseSearchConfigAPI', () => { kibanaVersion: '1.0.0', access: { hasAppSearchAccess: true, + hasSearchEnginesAccess: true, hasWorkplaceSearchAccess: false, }, publicUrl: 'http://some.vanity.url', @@ -136,6 +138,7 @@ describe('callEnterpriseSearchConfigAPI', () => { kibanaVersion: '1.0.0', access: { hasAppSearchAccess: false, + hasSearchEnginesAccess: false, hasWorkplaceSearchAccess: false, }, publicUrl: undefined, diff --git a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts index 3b9a37b32d1d7..b9e2ebdebfafa 100644 --- a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts +++ b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts @@ -87,6 +87,7 @@ export const callEnterpriseSearchConfigAPI = async ({ kibanaVersion: kibanaPackageJson.version, access: { hasAppSearchAccess: !!data?.current_user?.access?.app_search, + hasSearchEnginesAccess: !!data?.current_user?.access?.search_engines, hasWorkplaceSearchAccess: !!data?.current_user?.access?.workplace_search, }, publicUrl: stripTrailingSlash(data?.settings?.external_url), diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts index 602d8c48d520e..c1ce03f33b587 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts @@ -6,6 +6,7 @@ */ import { RouteDependencies } from '../../plugin'; +import { registerCrawlerExtractionRulesRoutes } from '../enterprise_search/crawler/crawler_extraction_rules'; import { registerSearchRelevanceSuggestionsRoutes } from './adaptive_relevance'; import { registerAnalyticsRoutes } from './analytics'; @@ -50,6 +51,7 @@ export const registerAppSearchRoutes = (dependencies: RouteDependencies) => { registerCrawlerRoutes(dependencies); registerCrawlerEntryPointRoutes(dependencies); registerCrawlerCrawlRulesRoutes(dependencies); + registerCrawlerExtractionRulesRoutes(dependencies); registerCrawlerSitemapRoutes(dependencies); registerSearchRelevanceSuggestionsRoutes(dependencies); }; diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/crawler/crawler_extraction_rules.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/crawler/crawler_extraction_rules.ts new file mode 100644 index 0000000000000..a5b9d38e70d9d --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/crawler/crawler_extraction_rules.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 { schema } from '@kbn/config-schema'; + +import { RouteDependencies } from '../../../plugin'; + +const extractionRuleSchema = schema.object({ + extraction_rule: schema.object({ + description: schema.string(), + rules: schema.arrayOf( + schema.object({ + content_from: schema.object({ + value: schema.nullable(schema.string()), + value_type: schema.string(), + }), + field_name: schema.string(), + multiple_objects_handling: schema.string(), + selector: schema.string(), + source_type: schema.string(), + }) + ), + url_filters: schema.arrayOf( + schema.object({ filter: schema.string(), pattern: schema.string() }) + ), + }), +}); + +export function registerCrawlerExtractionRulesRoutes({ + router, + enterpriseSearchRequestHandler, +}: RouteDependencies) { + router.post( + { + path: '/internal/enterprise_search/indices/{indexName}/crawler/domains/{domainId}/extraction_rules', + validate: { + body: extractionRuleSchema, + params: schema.object({ + domainId: schema.string(), + indexName: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + params: { + respond_with: 'index', + }, + path: '/api/ent/v1/internal/indices/:indexName/crawler2/domains/:domainId/extraction_rules', + }) + ); + + router.put( + { + path: '/internal/enterprise_search/indices/{indexName}/crawler/domains/{domainId}/extraction_rules/{crawlRuleId}', + validate: { + body: extractionRuleSchema, + params: schema.object({ + crawlRuleId: schema.string(), + domainId: schema.string(), + indexName: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + params: { + respond_with: 'index', + }, + path: '/api/ent/v1/internal/indices/:indexName/crawler2/domains/:domainId/extraction_rules/:crawlRuleId', + }) + ); + + router.delete( + { + path: '/internal/enterprise_search/indices/{indexName}/crawler/domains/{domainId}/extraction_rules/{crawlRuleId}', + validate: { + params: schema.object({ + crawlRuleId: schema.string(), + domainId: schema.string(), + indexName: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + params: { + respond_with: 'index', + }, + path: '/api/ent/v1/internal/indices/:indexName/crawler2/domains/:domainId/extraction_rules/:crawlRuleId', + }) + ); + + router.get( + { + path: '/internal/enterprise_search/indices/{indexName}/crawler/domains/{domainId}/extraction_rules/{crawlRuleId}', + validate: { + params: schema.object({ + crawlRuleId: schema.string(), + domainId: schema.string(), + indexName: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + params: { + respond_with: 'index', + }, + path: '/api/ent/v1/internal/indices/:indexName/crawler2/domains/:domainId/extraction_rules/:crawlRuleId', + }) + ); +} diff --git a/x-pack/plugins/enterprise_search/server/ui_settings.ts b/x-pack/plugins/enterprise_search/server/ui_settings.ts index c49d1287b1bf6..99fb5906a3050 100644 --- a/x-pack/plugins/enterprise_search/server/ui_settings.ts +++ b/x-pack/plugins/enterprise_search/server/ui_settings.ts @@ -5,25 +5,9 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; import { UiSettingsParams } from '@kbn/core/types'; -import { i18n } from '@kbn/i18n'; -import { enterpriseSearchFeatureId, enableEnginesSection } from '../common/ui_settings_keys'; /** * uiSettings definitions for Enterprise Search */ -export const uiSettings: Record> = { - [enableEnginesSection]: { - category: [enterpriseSearchFeatureId], - description: i18n.translate('xpack.enterpriseSearch.uiSettings.engines.description', { - defaultMessage: 'Enable the new Engines section in Enterprise Search.', - }), - name: i18n.translate('xpack.enterpriseSearch.uiSettings.engines.name', { - defaultMessage: 'Enable Engines', - }), - requiresPageReload: false, - schema: schema.boolean(), - value: false, - }, -}; +export const uiSettings: Record> = {}; diff --git a/x-pack/plugins/enterprise_search/server/utils/fetch_with_pagination.test.ts b/x-pack/plugins/enterprise_search/server/utils/fetch_with_pagination.test.ts new file mode 100644 index 0000000000000..82b2757abd394 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/utils/fetch_with_pagination.test.ts @@ -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 { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; + +import { fetchWithPagination } from './fetch_with_pagination'; + +describe('fetchWithPagination util', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('fetchWithPagination', () => { + it('should fetch mock data with pagination', async () => { + const mockFn = jest.fn(); + mockFn.mockImplementation(() => + Promise.resolve({ + hits: { hits: ['result1', 'result2'], total: 2 }, + } as any as SearchResponse) + ); + await expect(fetchWithPagination(mockFn, 0, 10)).resolves.toEqual({ + _meta: { + page: { + from: 0, + has_more_hits_than_total: false, + size: 10, + total: 2, + }, + }, + data: ['result1', 'result2'], + }); + }); + it('should return empty result if size is 0', async () => { + const mockFn = jest.fn(); + mockFn.mockImplementation(() => + Promise.resolve({ + hits: { hits: [], total: 0 }, + } as any as SearchResponse) + ); + await expect(fetchWithPagination(mockFn, 0, 0)).resolves.toEqual({ + _meta: { + page: { + from: 0, + has_more_hits_than_total: false, + size: 10, + total: 0, + }, + }, + data: [], + }); + }); + it('should handle total as an object correctly', async () => { + const mockFn = jest.fn(); + mockFn.mockImplementation(() => + Promise.resolve({ + hits: { + hits: [], + total: { + relation: 'lte', + value: 555, + }, + }, + } as any as SearchResponse) + ); + await expect(fetchWithPagination(mockFn, 0, 10)).resolves.toEqual({ + _meta: { + page: { + from: 0, + has_more_hits_than_total: false, + size: 10, + total: 555, + }, + }, + data: [], + }); + }); + + it('should handle undefined total correctly', async () => { + const mockFn = jest.fn(); + mockFn.mockImplementation(() => + Promise.resolve({ + hits: { + hits: [], + total: undefined, + }, + } as any as SearchResponse) + ); + await expect(fetchWithPagination(mockFn, 0, 10)).resolves.toEqual({ + _meta: { + page: { + from: 0, + has_more_hits_than_total: false, + size: 10, + total: 0, + }, + }, + data: [], + }); + }); + + it('should handle has_more_hits_than_total correctly', async () => { + const mockFn = jest.fn(); + mockFn.mockImplementation(() => + Promise.resolve({ + hits: { hits: ['result1', 'result2'], total: { relation: 'gte', value: 10000 } }, + } as any as SearchResponse) + ); + await expect(fetchWithPagination(mockFn, 50, 10)).resolves.toEqual({ + _meta: { + page: { + from: 50, + has_more_hits_than_total: true, + size: 10, + total: 10000, + }, + }, + data: ['result1', 'result2'], + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/utils/fetch_with_pagination.ts b/x-pack/plugins/enterprise_search/server/utils/fetch_with_pagination.ts new file mode 100644 index 0000000000000..be6a3db6a61d9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/utils/fetch_with_pagination.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 { SearchHit, SearchResponse, SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; + +import { Paginate } from '../../common/types/pagination'; + +const defaultResult = (data: T[]) => ({ + _meta: { + page: { + from: 0, + has_more_hits_than_total: false, + size: 10, + total: 0, + }, + }, + data, +}); + +export const fetchWithPagination = async ( + fetchFunction: () => Promise>, + from: number, + size: number +): Promise>> => { + if (size === 0) { + return defaultResult>([]); + } + const result = await fetchFunction(); + const total = totalToPaginateTotal(result.hits.total); + return { + _meta: { + page: { + from, + size, + ...total, + }, + }, + data: result.hits.hits, + }; +}; + +function totalToPaginateTotal(input: number | SearchTotalHits | undefined): { + has_more_hits_than_total: boolean; + total: number; +} { + if (typeof input === 'number') { + return { has_more_hits_than_total: false, total: input }; + } + + return input + ? { + has_more_hits_than_total: input.relation === 'gte' ? true : false, + total: input.value, + } + : { has_more_hits_than_total: false, total: 0 }; +} diff --git a/x-pack/plugins/file_upload/public/components/__snapshots__/import_complete_view.test.tsx.snap b/x-pack/plugins/file_upload/public/components/__snapshots__/import_complete_view.test.tsx.snap new file mode 100644 index 0000000000000..d24f877c21cb6 --- /dev/null +++ b/x-pack/plugins/file_upload/public/components/__snapshots__/import_complete_view.test.tsx.snap @@ -0,0 +1,519 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should render error when upload fails from elasticsearch request failure 1`] = ` + + +

+ Error: simulated elasticsearch request failure +

+
+ + + +

+ Import response +

+
+
+ + + + + +
+
+ +
+ +
+`; + +exports[`Should render error when upload fails from http request timeout 1`] = ` + + +

+ Error: simulated http request timeout +

+
+ + + +

+ Import response +

+
+
+ + + + + +
+
+ +
+ +
+`; + +exports[`Should render success 1`] = ` + + +

+ Indexed 10 features. +

+
+ + + +

+ Import response +

+
+
+ + + + + +
+
+ +
+ + + + +

+ Data view response +

+
+
+ + + + + +
+
+ +
+ + +

+ + + + +

+
+
+`; + +exports[`Should render warning when some features failed import 1`] = ` + + +

+ Unable to index 1 of 10 features. +

+
+ + + +

+ Import response +

+
+
+ + + + + +
+
+ +
+ + + + +

+ Data view response +

+
+
+ + + + + +
+
+ +
+ + +

+ + + + +

+
+
+`; diff --git a/x-pack/plugins/file_upload/public/components/geo_upload_wizard.tsx b/x-pack/plugins/file_upload/public/components/geo_upload_wizard.tsx index 0c7f09c56f36f..b3d1711b1d2a8 100644 --- a/x-pack/plugins/file_upload/public/components/geo_upload_wizard.tsx +++ b/x-pack/plugins/file_upload/public/components/geo_upload_wizard.tsx @@ -17,6 +17,7 @@ import { ImportResults } from '../importer'; import { GeoFileImporter } from '../importer/geo'; import type { Settings } from '../../common/types'; import { hasImportPermission } from '../api'; +import { getPartialImportMessage } from './utils'; enum PHASE { CONFIGURE = 'CONFIGURE', @@ -175,6 +176,25 @@ export class GeoUploadWizard extends Component }); this.props.onUploadError(); return; + } else if (importResults.docCount === importResults.failures?.length) { + this.setState({ + // Force importResults into failure shape when no features are indexed + importResults: { + ...importResults, + success: false, + error: { + error: { + reason: getPartialImportMessage( + importResults.failures!.length, + importResults.docCount + ), + }, + }, + }, + phase: PHASE.COMPLETE, + }); + this.props.onUploadError(); + return; } // diff --git a/x-pack/plugins/file_upload/public/components/import_complete_view.test.tsx b/x-pack/plugins/file_upload/public/components/import_complete_view.test.tsx new file mode 100644 index 0000000000000..a31d8dcd04b84 --- /dev/null +++ b/x-pack/plugins/file_upload/public/components/import_complete_view.test.tsx @@ -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 React from 'react'; +import { shallow } from 'enzyme'; + +import { ImportCompleteView } from './import_complete_view'; + +jest.mock('../kibana_services', () => ({ + get: jest.fn(), + getDocLinks: () => { + return { + links: { + maps: { + importGeospatialPrivileges: 'linkToPrvilegesDocs', + }, + }, + }; + }, + getHttp: () => { + return { + basePath: { + prepend: (path: string) => `abc${path}`, + }, + }; + }, + getUiSettings: () => { + return { + get: jest.fn(), + }; + }, +})); + +test('Should render success', () => { + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); +}); + +test('Should render warning when some features failed import', () => { + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); +}); + +test('Should render error when upload fails from http request timeout', () => { + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); +}); + +test('Should render error when upload fails from elasticsearch request failure', () => { + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); +}); diff --git a/x-pack/plugins/file_upload/public/components/import_complete_view.tsx b/x-pack/plugins/file_upload/public/components/import_complete_view.tsx index 46f566eb27e2e..f95aee869f93d 100644 --- a/x-pack/plugins/file_upload/public/components/import_complete_view.tsx +++ b/x-pack/plugins/file_upload/public/components/import_complete_view.tsx @@ -22,6 +22,7 @@ import { import { CodeEditor, KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { getDocLinks, getHttp, getUiSettings } from '../kibana_services'; import { ImportResults } from '../importer'; +import { getPartialImportMessage } from './utils'; const services = { uiSettings: getUiSettings(), @@ -133,7 +134,7 @@ export class ImportCompleteView extends Component { // Display http request error message reason = this.props.importResults.error.body.message; } else if (this.props.importResults?.error?.error?.reason) { - // Display elasticxsearch request error message + // Display elasticsearch request error message reason = this.props.importResults.error.error.reason; } const errorMsg = reason @@ -156,21 +157,25 @@ export class ImportCompleteView extends Component { ); } - const successMsg = i18n.translate('xpack.fileUpload.importComplete.uploadSuccessMsg', { - defaultMessage: 'Indexed {numFeatures} features.', - values: { - numFeatures: this.props.importResults.docCount, - }, - }); - - const failedFeaturesMsg = this.props.importResults.failures?.length - ? i18n.translate('xpack.fileUpload.importComplete.failedFeaturesMsg', { - defaultMessage: 'Unable to index {numFailures} features.', - values: { - numFailures: this.props.importResults.failures.length, - }, - }) - : ''; + if (this.props.importResults.failures?.length) { + return ( + +

+ {getPartialImportMessage( + this.props.importResults.failures!.length, + this.props.importResults.docCount + )} +

+
+ ); + } return ( { })} data-test-subj={STATUS_CALLOUT_DATA_TEST_SUBJ} > -

{`${successMsg} ${failedFeaturesMsg}`}

+

+ {i18n.translate('xpack.fileUpload.importComplete.uploadSuccessMsg', { + defaultMessage: 'Indexed {numFeatures} features.', + values: { + numFeatures: this.props.importResults.docCount, + }, + })} +

); } diff --git a/x-pack/plugins/file_upload/public/components/utils.ts b/x-pack/plugins/file_upload/public/components/utils.ts new file mode 100644 index 0000000000000..0b4df67f3a0ec --- /dev/null +++ b/x-pack/plugins/file_upload/public/components/utils.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 { i18n } from '@kbn/i18n'; + +export function getPartialImportMessage(failedFeaturesCount: number, totalFeaturesCount?: number) { + const outOfTotalMsg = + typeof totalFeaturesCount === 'number' + ? i18n.translate('xpack.fileUpload.geoUploadWizard.outOfTotalMsg', { + defaultMessage: 'of {totalFeaturesCount}', + values: { + totalFeaturesCount, + }, + }) + : ''; + return i18n.translate('xpack.fileUpload.geoUploadWizard.partialImportMsg', { + defaultMessage: 'Unable to index {failedFeaturesCount} {outOfTotalMsg} features.', + values: { + failedFeaturesCount, + outOfTotalMsg, + }, + }); +} diff --git a/x-pack/plugins/fleet/common/constants/agent_policy.ts b/x-pack/plugins/fleet/common/constants/agent_policy.ts index 8e818ddf206cc..50cef1fbe43cf 100644 --- a/x-pack/plugins/fleet/common/constants/agent_policy.ts +++ b/x-pack/plugins/fleet/common/constants/agent_policy.ts @@ -17,6 +17,7 @@ export const AGENT_POLICY_DEFAULT_MONITORING_DATASETS = [ 'elastic_agent.elastic_agent', 'elastic_agent.apm_server', 'elastic_agent.filebeat', + 'elastic_agent.filebeat_input', 'elastic_agent.fleet_server', 'elastic_agent.metricbeat', 'elastic_agent.osquerybeat', diff --git a/x-pack/plugins/fleet/common/constants/authz.ts b/x-pack/plugins/fleet/common/constants/authz.ts index fa2a2c0b44e93..c518dae975930 100644 --- a/x-pack/plugins/fleet/common/constants/authz.ts +++ b/x-pack/plugins/fleet/common/constants/authz.ts @@ -138,4 +138,10 @@ export const ENDPOINT_PRIVILEGES: Record = deepFreez privilegeType: 'api', privilegeName: 'writeFileOperations', }, + writeExecuteOperations: { + appId: DEFAULT_APP_CATEGORIES.security.id, + privilegeSplit: '-', + privilegeType: 'api', + privilegeName: 'writeExecuteOperations', + }, }); diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index bfc7208235c56..0a62474c89056 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -1372,6 +1372,14 @@ }, { "$ref": "#/components/parameters/with_metrics" + }, + { + "name": "getStatusSummary", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } } ], "security": [ @@ -5399,11 +5407,11 @@ "properties": { "cpu_avg": { "type": "number", - "description": "Average agent CPU usage during the last 5 minute, number between 0-1" + "description": "Average agent CPU usage during the last 5 minutes, number between 0-1" }, "memory_size_byte_avg": { "type": "number", - "description": "Average agent memory consumption during the last 5 minute" + "description": "Average agent memory consumption during the last 5 minutes" } } } @@ -5441,6 +5449,38 @@ }, "perPage": { "type": "number" + }, + "statusSummary": { + "type": "object", + "properties": { + "offline": { + "type": "number" + }, + "error": { + "type": "number" + }, + "online": { + "type": "number" + }, + "inactive": { + "type": "number" + }, + "enrolling": { + "type": "number" + }, + "unenrolling": { + "type": "number" + }, + "unenrolled": { + "type": "number" + }, + "updating": { + "type": "number" + }, + "degraded'": { + "type": "number" + } + } } }, "required": [ diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index bd8d18f2be5b1..7b93038b146bc 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -857,6 +857,11 @@ paths: - $ref: '#/components/parameters/sort_field' - $ref: '#/components/parameters/sort_order' - $ref: '#/components/parameters/with_metrics' + - name: getStatusSummary + in: query + required: false + schema: + type: boolean security: - basicAuth: [] /agents/bulk_upgrade: @@ -3421,11 +3426,11 @@ components: cpu_avg: type: number description: >- - Average agent CPU usage during the last 5 minute, number between - 0-1 + Average agent CPU usage during the last 5 minutes, number + between 0-1 memory_size_byte_avg: type: number - description: Average agent memory consumption during the last 5 minute + description: Average agent memory consumption during the last 5 minutes required: - type - active @@ -3451,6 +3456,27 @@ components: type: number perPage: type: number + statusSummary: + type: object + properties: + offline: + type: number + error: + type: number + online: + type: number + inactive: + type: number + enrolling: + type: number + unenrolling: + type: number + unenrolled: + type: number + updating: + type: number + degraded': + type: number required: - items - total diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/get_agents_response.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/get_agents_response.yaml index 7a4be64d29cbe..71416b6d4fe7a 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/get_agents_response.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/get_agents_response.yaml @@ -16,6 +16,27 @@ properties: type: number perPage: type: number + statusSummary: + type: object + properties: + offline: + type: number + error: + type: number + online : + type: number + inactive : + type: number + enrolling: + type: number + unenrolling: + type: number + unenrolled : + type: number + updating : + type: number + degraded': + type: number required: - items - total diff --git a/x-pack/plugins/fleet/common/openapi/paths/agents.yaml b/x-pack/plugins/fleet/common/openapi/paths/agents.yaml index cc1aaab2a679c..44d5e1c8a9738 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/agents.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/agents.yaml @@ -18,5 +18,10 @@ get: - $ref: ../components/parameters/sort_field.yaml - $ref: ../components/parameters/sort_order.yaml - $ref: ../components/parameters/with_metrics.yaml + - name: getStatusSummary + in: query + required: false + schema: + type: boolean security: - basicAuth: [] diff --git a/x-pack/plugins/fleet/common/services/agent_statuses_to_summary.test.ts b/x-pack/plugins/fleet/common/services/agent_statuses_to_summary.test.ts new file mode 100644 index 0000000000000..49144a75b5691 --- /dev/null +++ b/x-pack/plugins/fleet/common/services/agent_statuses_to_summary.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 { agentStatusesToSummary } from './agent_statuses_to_summary'; + +describe('agentStatusesToSummary', () => { + it('should return the correct summary', () => { + expect( + agentStatusesToSummary({ + online: 1, + error: 2, + degraded: 3, + inactive: 4, + offline: 5, + updating: 6, + enrolling: 7, + unenrolling: 8, + unenrolled: 9, + }) + ).toEqual({ + healthy: 1, + unhealthy: 5, + inactive: 4, + offline: 5, + updating: 21, + unenrolled: 9, + }); + }); +}); diff --git a/x-pack/plugins/fleet/common/services/agent_statuses_to_summary.ts b/x-pack/plugins/fleet/common/services/agent_statuses_to_summary.ts new file mode 100644 index 0000000000000..0c1f0311851d4 --- /dev/null +++ b/x-pack/plugins/fleet/common/services/agent_statuses_to_summary.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 { AgentStatus, SimplifiedAgentStatus } from '../types'; + +export function agentStatusesToSummary( + statuses: Record +): Record { + return { + healthy: statuses.online, + unhealthy: statuses.error + statuses.degraded, + inactive: statuses.inactive, + offline: statuses.offline, + updating: statuses.updating + statuses.enrolling + statuses.unenrolling, + unenrolled: statuses.unenrolled, + }; +} diff --git a/x-pack/plugins/fleet/common/services/index.ts b/x-pack/plugins/fleet/common/services/index.ts index 6f630cf117e29..2b5a8dd04bc91 100644 --- a/x-pack/plugins/fleet/common/services/index.ts +++ b/x-pack/plugins/fleet/common/services/index.ts @@ -58,3 +58,4 @@ export { } from './package_prerelease'; export { getAllowedOutputTypeForPolicy } from './output_helpers'; +export { agentStatusesToSummary } from './agent_statuses_to_summary'; diff --git a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts index 68b251f21e294..10866da70d93e 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts @@ -14,6 +14,7 @@ import type { CurrentUpgrade, NewAgentAction, AgentDiagnostics, + AgentStatus, } from '../models'; import type { ListResult, ListWithKuery } from './common'; @@ -27,9 +28,9 @@ export interface GetAgentsRequest { } export interface GetAgentsResponse extends ListResult { - totalInactive: number; // deprecated in 8.x list?: Agent[]; + statusSummary?: Record; } export interface GetAgentTagsResponse { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 24277e464582c..8d7259a77767f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -106,7 +106,11 @@ export function useOnSubmit({ // Form state const [formState, setFormState] = useState('VALID'); + // Used to render extension components only when package policy is initialized + const [isInitialized, setIsInitialized] = useState(false); + // Used to initialize the package policy once const isInitializedRef = useRef(false); + const [agentPolicy, setAgentPolicy] = useState(); // New package policy state const [packagePolicy, setPackagePolicy] = useState({ @@ -204,15 +208,16 @@ export function useOnSubmit({ packageToPackagePolicy( packageInfo, agentPolicy?.id || '', - DEFAULT_PACKAGE_POLICY.namespace, + agentPolicy?.namespace || DEFAULT_PACKAGE_POLICY.namespace, DEFAULT_PACKAGE_POLICY.name || incrementedName, DEFAULT_PACKAGE_POLICY.description, integrationToEnable ) ); + setIsInitialized(true); } init(); - }, [packageInfo, agentPolicy, updatePackagePolicy, integrationToEnable]); + }, [packageInfo, agentPolicy, updatePackagePolicy, integrationToEnable, isInitialized]); const onSaveNavigate = useOnSaveNavigate({ packagePolicy, @@ -360,7 +365,7 @@ export function useOnSubmit({ setValidationResults, hasAgentPolicyError, setHasAgentPolicyError, - isInitialized: isInitializedRef.current, + isInitialized, // TODO check navigateAddAgent, navigateAddAgentHelp, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index cd5b997d9c788..9a1c943763bd8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -267,6 +267,29 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ ); const extensionView = useUIExtension(packagePolicy.package?.name ?? '', 'package-policy-create'); + const replaceDefineStepView = useUIExtension( + packagePolicy.package?.name ?? '', + 'package-policy-replace-define-step' + ); + + if (replaceDefineStepView && extensionView) { + throw new Error( + "'package-policy-create' and 'package-policy-replace-define-step' cannot both be registered as UI extensions" + ); + } + + const replaceStepConfigurePackagePolicy = replaceDefineStepView && packageInfo?.name && ( + + + + ); const stepConfigurePackagePolicy = useMemo( () => @@ -329,7 +352,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ defaultMessage: 'Configure integration', }), 'data-test-subj': 'dataCollectionSetupStep', - children: stepConfigurePackagePolicy, + children: replaceStepConfigurePackagePolicy || stepConfigurePackagePolicy, }, { title: i18n.translate('xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle', { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.test.tsx index c295cdd41cecb..303629a99bb69 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.test.tsx @@ -331,11 +331,42 @@ describe('edit package policy page', () => { expect(useStartServices().application.navigateToUrl).not.toHaveBeenCalled(); }); - it('should show ready for upgrade if package useLatestPackageVersion and no conflicts', async () => { - (useUIExtension as MockFn).mockReturnValue({ - useLatestPackageVersion: true, - Component: TestComponent, + it("throws when both 'package-policy-edit' and 'package-policy-replace-define-step' are defined", async () => { + (useUIExtension as MockFn) + .mockReturnValueOnce({ + view: 'package-policy-edit', + Component: TestComponent, + }) + .mockReturnValueOnce({ + view: 'package-policy-replace-define-step', + Component: TestComponent, + }) + .mockReturnValueOnce({ + view: 'package-policy-edit-tabs', + Component: TestComponent, + }); + + render(); + + await waitFor(() => { + expect(renderResult.getByTestId('euiErrorBoundary')).toBeVisible(); }); + }); + + it('should show ready for upgrade if package useLatestPackageVersion and no conflicts', async () => { + (useUIExtension as MockFn) + .mockReturnValueOnce({ + view: 'package-policy-edit', + useLatestPackageVersion: true, + Component: TestComponent, + }) + .mockReturnValueOnce(undefined) + .mockReturnValueOnce({ + view: 'package-policy-edit-tabs', + useLatestPackageVersion: true, + Component: TestComponent, + }); + (sendUpgradePackagePolicyDryRun as MockFn).mockResolvedValue({ data: [ { @@ -357,10 +388,19 @@ describe('edit package policy page', () => { }); it('should show review field conflicts if package useLatestPackageVersion and has conflicts', async () => { - (useUIExtension as MockFn).mockReturnValue({ - useLatestPackageVersion: true, - Component: TestComponent, - }); + (useUIExtension as MockFn) + .mockReturnValueOnce({ + view: 'package-policy-edit', + useLatestPackageVersion: true, + Component: TestComponent, + }) + .mockReturnValueOnce(undefined) + .mockReturnValueOnce({ + view: 'package-policy-edit-tabs', + useLatestPackageVersion: true, + Component: TestComponent, + }); + (sendUpgradePackagePolicyDryRun as MockFn).mockResolvedValue({ data: [ { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index afab7a48ebb76..b066457b0aa2e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -35,7 +35,7 @@ import { } from '../../../../integrations/hooks'; import { Loading, - Error, + Error as ErrorComponent, ExtensionWrapper, EuiButtonWithTooltip, DevtoolsRequestFlyoutButton, @@ -240,10 +240,21 @@ export const EditPackagePolicyForm = memo<{ }; const extensionView = useUIExtension(packagePolicy.package?.name ?? '', 'package-policy-edit'); + const replaceDefineStepView = useUIExtension( + packagePolicy.package?.name ?? '', + 'package-policy-replace-define-step' + ); const extensionTabsView = useUIExtension( packagePolicy.package?.name ?? '', 'package-policy-edit-tabs' ); + + if (replaceDefineStepView && extensionView) { + throw new Error( + "'package-policy-create' and 'package-policy-replace-define-step' cannot both be registered as UI extensions" + ); + } + const tabsViews = extensionTabsView?.tabs; const [selectedTab, setSelectedTab] = useState(0); @@ -339,6 +350,20 @@ export const EditPackagePolicyForm = memo<{ ] ); + const replaceConfigurePackage = replaceDefineStepView && originalPackagePolicy && packageInfo && ( + + + + ); + const { showDevtoolsRequest: isShowDevtoolRequestExperimentEnabled } = ExperimentalFeaturesService.get(); @@ -361,7 +386,7 @@ export const EditPackagePolicyForm = memo<{ {isLoadingData ? ( ) : loadingError || !agentPolicy || !packageInfo ? ( - )} - {configurePackage} + {replaceConfigurePackage || configurePackage} {/* Extra space to accomodate the EuiBottomBar height */} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.tsx index 6e504c5e183cf..e7095e7af5a4c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.tsx @@ -294,7 +294,7 @@ const formattedTime = (time?: string) => { const inProgressTitle = (action: ActionStatus) => ( = action.nbAgentsActioned @@ -307,6 +307,7 @@ const inProgressTitle = (action: ActionStatus) => ( reassignText: action.type === 'POLICY_REASSIGN' && action.newPolicyId ? `to ${action.newPolicyId}` : '', upgradeText: action.type === 'UPGRADE' ? `to version ${action.version}` : '', + failuresText: action.nbAgentsFailed > 0 ? `, has ${action.nbAgentsFailed} failure(s)` : '', }} /> ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx index 1e3bb5ef50b7b..3faaa6e64d26a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx @@ -128,6 +128,7 @@ export const AgentListTable: React.FC = (props: Props) => { { field: 'policy_id', sortable: true, + truncateText: true, name: i18n.translate('xpack.fleet.agentList.policyColumnTitle', { defaultMessage: 'Agent policy', }), diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_last_seen_inactive_agents_count.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_last_seen_inactive_agents_count.ts index ce767c82eec37..e1baf8f277ff1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_last_seen_inactive_agents_count.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_last_seen_inactive_agents_count.ts @@ -11,7 +11,6 @@ const LOCAL_STORAGE_KEY = 'fleet.lastSeenInactiveAgentsCount'; export const useLastSeenInactiveAgentsCount = (): [number, (val: number) => void] => { const [lastSeenInactiveAgentsCount, setLastSeenInactiveAgentsCount] = useState(0); - useEffect(() => { const storageValue = localStorage.getItem(LOCAL_STORAGE_KEY); if (storageValue) { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.test.tsx index cefb6ae1c3f9b..2bc453a45a888 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.test.tsx @@ -100,14 +100,18 @@ describe('agent_list_page', () => { data: { items: mapAgents(['agent1', 'agent2', 'agent3', 'agent4', 'agent5']), total: 6, - totalInactive: 0, + statusSummary: { + online: 6, + }, }, }) .mockResolvedValueOnce({ data: { items: mapAgents(['agent1', 'agent2', 'agent3', 'agent4', 'agent6']), total: 6, - totalInactive: 0, + statusSummary: { + online: 6, + }, }, }); jest.useFakeTimers({ legacyFakeTimers: true }); @@ -128,12 +132,8 @@ describe('agent_list_page', () => { return { data: { results: { - online: 6, - error: 0, - offline: 0, - updating: 0, + inactive: 0, }, - totalInactive: 0, }, }; }); @@ -151,10 +151,7 @@ describe('agent_list_page', () => { mockedSendGetAgentStatus.mockResolvedValue({ data: { results: { - online: 6, - error: 0, - offline: 0, - updating: 0, + inactive: 0, }, totalInactive: 0, }, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx index ee77846bce93c..f6808ff2fa327 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx @@ -13,6 +13,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { agentStatusesToSummary } from '../../../../../../common/services'; + import type { Agent, AgentPolicy, SimplifiedAgentStatus } from '../../../types'; import { usePagination, @@ -226,7 +228,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { const [totalAgents, setTotalAgents] = useState(0); const [totalInactiveAgents, setTotalInactiveAgents] = useState(0); const [showAgentActivityTour, setShowAgentActivityTour] = useState({ isOpen: false }); - const getSortFieldForAPI = (field: keyof Agent): string => { if ([VERSION_FIELD, HOSTNAME_FIELD].includes(field as string)) { return `${field}.keyword`; @@ -275,25 +276,27 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { isLoadingVar.current = true; try { setIsLoading(true); - const [agentsResponse, agentsStatusResponse, agentTagsResponse] = await Promise.all([ - sendGetAgents({ - page: pagination.currentPage, - perPage: pagination.pageSize, - kuery: kuery && kuery !== '' ? kuery : undefined, - sortField: getSortFieldForAPI(sortField), - sortOrder, - showInactive, - showUpgradeable, - withMetrics: displayAgentMetrics, - }), - sendGetAgentStatus({ - kuery: kuery && kuery !== '' ? kuery : undefined, - }), - sendGetAgentTags({ - kuery: kuery && kuery !== '' ? kuery : undefined, - showInactive, - }), - ]); + const [agentsResponse, totalInactiveAgentsResponse, agentTagsResponse] = + await Promise.all([ + sendGetAgents({ + page: pagination.currentPage, + perPage: pagination.pageSize, + kuery: kuery && kuery !== '' ? kuery : undefined, + sortField: getSortFieldForAPI(sortField), + sortOrder, + showInactive, + showUpgradeable, + getStatusSummary: true, + withMetrics: displayAgentMetrics, + }), + sendGetAgentStatus({ + kuery: AgentStatusKueryHelper.buildKueryForInactiveAgents(), + }), + sendGetAgentTags({ + kuery: kuery && kuery !== '' ? kuery : undefined, + showInactive, + }), + ]); isLoadingVar.current = false; // Return if a newer request has been triggered if (currentRequestRef.current !== currentRequest) { @@ -305,27 +308,22 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { if (!agentsResponse.data) { throw new Error('Invalid GET /agents response'); } - if (agentsStatusResponse.error) { - throw agentsStatusResponse.error; - } - if (!agentsStatusResponse.data) { + if (!totalInactiveAgentsResponse.data) { throw new Error('Invalid GET /agents_status response'); } if (agentTagsResponse.error) { - throw agentsStatusResponse.error; + throw agentTagsResponse.error; } if (!agentTagsResponse.data) { throw new Error('Invalid GET /agent/tags response'); } - setAgentsStatus({ - healthy: agentsStatusResponse.data.results.online, - unhealthy: agentsStatusResponse.data.results.error, - offline: agentsStatusResponse.data.results.offline, - updating: agentsStatusResponse.data.results.updating, - inactive: agentsStatusResponse.data.results.inactive, - unenrolled: agentsStatusResponse.data.results.unenrolled, - }); + const statusSummary = agentsResponse.data.statusSummary; + + if (!statusSummary) { + throw new Error('Invalid GET /agents response - no status summary'); + } + setAgentsStatus(agentStatusesToSummary(statusSummary)); const newAllTags = agentTagsResponse.data.items; @@ -339,7 +337,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { setAgents(agentsResponse.data.items); setTotalAgents(agentsResponse.data.total); - setTotalInactiveAgents(agentsResponse.data.totalInactive); + setTotalInactiveAgents(totalInactiveAgentsResponse.data.results.inactive || 0); } catch (error) { notifications.toasts.addError(error, { title: i18n.translate('xpack.fleet.agentList.errorFetchingDataTitle', { diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx deleted file mode 100644 index ccb53ac7dc355..0000000000000 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { ReactNode, FunctionComponent } from 'react'; -import { useMemo } from 'react'; -import React, { useCallback, useState } from 'react'; -import { css } from '@emotion/react'; - -import { - EuiFlexGrid, - EuiFlexGroup, - EuiFlexItem, - EuiLink, - EuiSpacer, - EuiTitle, - EuiFieldSearch, - EuiText, - useEuiTheme, - EuiIcon, - EuiScreenReaderOnly, - EuiButton, - EuiButtonIcon, - EuiPopover, - EuiContextMenuPanel, - EuiContextMenuItem, -} from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; - -import { FormattedMessage } from '@kbn/i18n-react'; - -import { Loading } from '../../../components'; -import { useLocalSearch, searchIdField } from '../../../hooks'; - -import type { IntegrationCardItem } from '../../../../../../common/types/models'; - -import type { ExtendedIntegrationCategory, CategoryFacet } from '../screens/home/category_facets'; - -import type { IntegrationsURLParameters } from '../screens/home/hooks/use_available_packages'; - -import { ExperimentalFeaturesService } from '../../../services'; - -import { promoteFeaturedIntegrations } from './utils'; - -import { PackageCard } from './package_card'; - -export interface Props { - isLoading?: boolean; - controls?: ReactNode | ReactNode[]; - list: IntegrationCardItem[]; - searchTerm: string; - setSearchTerm: (search: string) => void; - selectedCategory: ExtendedIntegrationCategory; - setCategory: (category: ExtendedIntegrationCategory) => void; - categories: CategoryFacet[]; - setUrlandReplaceHistory: (params: IntegrationsURLParameters) => void; - setUrlandPushHistory: (params: IntegrationsURLParameters) => void; - callout?: JSX.Element | null; - // Props used only in AvailablePackages component: - showCardLabels?: boolean; - title?: string; - availableSubCategories?: CategoryFacet[]; - selectedSubCategory?: string; - setSelectedSubCategory?: (c: string | undefined) => void; - showMissingIntegrationMessage?: boolean; -} - -export const PackageListGrid: FunctionComponent = ({ - isLoading, - controls, - title, - list, - searchTerm, - setSearchTerm, - selectedCategory, - setCategory, - categories, - availableSubCategories, - setSelectedSubCategory, - selectedSubCategory, - setUrlandReplaceHistory, - setUrlandPushHistory, - showMissingIntegrationMessage = false, - callout, - showCardLabels = true, -}) => { - const localSearchRef = useLocalSearch(list); - const { euiTheme } = useEuiTheme(); - - const [isPopoverOpen, setPopover] = useState(false); - - const MAX_SUBCATEGORIES_NUMBER = 6; - - const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get(); - - const onButtonClick = () => { - setPopover(!isPopoverOpen); - }; - - const closePopover = () => { - setPopover(false); - }; - - const onQueryChange = (e: any) => { - const queryText = e.target.value; - setSearchTerm(queryText); - setUrlandReplaceHistory({ - searchString: queryText, - categoryId: selectedCategory, - subCategoryId: selectedSubCategory, - }); - }; - - const resetQuery = () => { - setSearchTerm(''); - setUrlandReplaceHistory({ searchString: '', categoryId: '', subCategoryId: '' }); - }; - - const onSubCategoryClick = useCallback( - (subCategory: string) => { - if (setSelectedSubCategory) setSelectedSubCategory(subCategory); - setUrlandPushHistory({ - categoryId: selectedCategory, - subCategoryId: subCategory, - }); - }, - [selectedCategory, setSelectedSubCategory, setUrlandPushHistory] - ); - - const selectedCategoryTitle = selectedCategory - ? categories.find((category) => category.id === selectedCategory)?.title - : undefined; - - const filteredPromotedList = useMemo(() => { - if (isLoading) return []; - const filteredList = searchTerm - ? list.filter((item) => - (localSearchRef.current!.search(searchTerm) as IntegrationCardItem[]) - .map((match) => match[searchIdField]) - .includes(item[searchIdField]) - ) - : list; - - return promoteFeaturedIntegrations(filteredList, selectedCategory); - }, [isLoading, list, localSearchRef, searchTerm, selectedCategory]); - - const splitSubcategories = ( - subcategories: CategoryFacet[] | undefined - ): { visibleSubCategories?: CategoryFacet[]; hiddenSubCategories?: CategoryFacet[] } => { - if (!subcategories) return {}; - else if (subcategories && subcategories?.length < MAX_SUBCATEGORIES_NUMBER) { - return { visibleSubCategories: subcategories, hiddenSubCategories: [] }; - } else if (subcategories && subcategories?.length >= MAX_SUBCATEGORIES_NUMBER) { - return { - visibleSubCategories: subcategories.slice(0, MAX_SUBCATEGORIES_NUMBER), - hiddenSubCategories: subcategories.slice(MAX_SUBCATEGORIES_NUMBER), - }; - } - return {}; - }; - - const splitSubcat = splitSubcategories(availableSubCategories); - const { visibleSubCategories } = splitSubcat; - const hiddenSubCategoriesItems = useMemo(() => { - return splitSubcat?.hiddenSubCategories?.map((subCategory) => { - return ( - { - onSubCategoryClick(subCategory.id); - closePopover(); - }} - > - {subCategory.title} - - ); - }); - }, [onSubCategoryClick, splitSubcat.hiddenSubCategories]); - - return ( - - - - - - onQueryChange(e)} - isClearable={true} - incremental={true} - fullWidth={true} - prepend={ - selectedCategoryTitle ? ( - - - Searching category: - - {selectedCategoryTitle} - - - ) : undefined - } - /> - {showIntegrationsSubcategories && availableSubCategories?.length ? : null} - {showIntegrationsSubcategories ? ( - - {visibleSubCategories?.map((subCategory) => ( - - onSubCategoryClick(subCategory.id)} - > - - - - ))} - {hiddenSubCategoriesItems?.length ? ( - - - } - isOpen={isPopoverOpen} - closePopover={closePopover} - panelPaddingSize="none" - anchorPosition="downLeft" - > - - - - ) : null} - - ) : null} - {callout ? ( - <> - - {callout} - - ) : null} - - - {showMissingIntegrationMessage && ( - <> - - - - - )} - - - ); -}; - -interface ControlsColumnProps { - controls: ReactNode; - title: string | undefined; -} - -const ControlsColumn = ({ controls, title }: ControlsColumnProps) => { - let titleContent; - if (title) { - titleContent = ( - <> - -

{title}

-
- - - ); - } - return ( - - {titleContent} - {controls} - - ); -}; - -interface GridColumnProps { - list: IntegrationCardItem[]; - isLoading: boolean; - showMissingIntegrationMessage?: boolean; - showCardLabels?: boolean; -} - -const GridColumn = ({ - list, - showMissingIntegrationMessage = false, - showCardLabels = false, - isLoading, -}: GridColumnProps) => { - if (isLoading) return ; - - return ( - - {list.length ? ( - list.map((item) => { - return ( - .euiPopover, - & > .euiPopover > .euiPopover__anchor, - & > .euiPopover > .euiPopover__anchor > .euiCard { - height: 100%; - } - `} - > - - - ); - }) - ) : ( - - -

- {showMissingIntegrationMessage ? ( - - ) : ( - - )} -

-
-
- )} -
- ); -}; - -interface MissingIntegrationContentProps { - resetQuery: () => void; - setSelectedCategory: (category: ExtendedIntegrationCategory) => void; - setUrlandPushHistory: (params: IntegrationsURLParameters) => void; -} - -const MissingIntegrationContent = ({ - resetQuery, - setSelectedCategory, - setUrlandPushHistory, -}: MissingIntegrationContentProps) => { - const handleCustomInputsLinkClick = useCallback(() => { - resetQuery(); - setSelectedCategory('custom'); - setUrlandPushHistory({ - categoryId: 'custom', - subCategoryId: '', - }); - }, [resetQuery, setSelectedCategory, setUrlandPushHistory]); - - return ( - -

- - - - ), - forumLink: ( - - - - ), - }} - /> -

-
- ); -}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/controls.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/controls.tsx new file mode 100644 index 0000000000000..1466cd6cb9dc5 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/controls.tsx @@ -0,0 +1,165 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { ReactNode } from 'react'; +import React, { useCallback } from 'react'; +import { css } from '@emotion/react'; + +import { + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiSpacer, + EuiTitle, + EuiText, +} from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n-react'; + +import { Loading } from '../../../../components'; + +import type { IntegrationCardItem } from '../../../../../../../common/types/models'; + +import type { ExtendedIntegrationCategory } from '../../screens/home/category_facets'; + +import type { IntegrationsURLParameters } from '../../screens/home/hooks/use_available_packages'; + +import { PackageCard } from '../package_card'; + +interface ControlsColumnProps { + controls: ReactNode; + title: string | undefined; +} + +export const ControlsColumn = ({ controls, title }: ControlsColumnProps) => { + let titleContent; + if (title) { + titleContent = ( + <> + +

{title}

+
+ + + ); + } + return ( + + {titleContent} + {controls} + + ); +}; + +interface GridColumnProps { + list: IntegrationCardItem[]; + isLoading: boolean; + showMissingIntegrationMessage?: boolean; + showCardLabels?: boolean; +} + +export const GridColumn = ({ + list, + showMissingIntegrationMessage = false, + showCardLabels = false, + isLoading, +}: GridColumnProps) => { + if (isLoading) return ; + + return ( + + {list.length ? ( + list.map((item) => { + return ( + .euiPopover, + & > .euiPopover > .euiPopover__anchor, + & > .euiPopover > .euiPopover__anchor > .euiCard { + height: 100%; + } + `} + > + + + ); + }) + ) : ( + + +

+ {showMissingIntegrationMessage ? ( + + ) : ( + + )} +

+
+
+ )} +
+ ); +}; + +interface MissingIntegrationContentProps { + resetQuery: () => void; + setSelectedCategory: (category: ExtendedIntegrationCategory) => void; + setUrlandPushHistory: (params: IntegrationsURLParameters) => void; +} + +export const MissingIntegrationContent = ({ + resetQuery, + setSelectedCategory, + setUrlandPushHistory, +}: MissingIntegrationContentProps) => { + const handleCustomInputsLinkClick = useCallback(() => { + resetQuery(); + setSelectedCategory('custom'); + setUrlandPushHistory({ + categoryId: 'custom', + subCategoryId: '', + }); + }, [resetQuery, setSelectedCategory, setUrlandPushHistory]); + + return ( + +

+ + + + ), + forumLink: ( + + + + ), + }} + /> +

+
+ ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/index.stories.tsx similarity index 97% rename from x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.tsx rename to x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/index.stories.tsx index 1ff0c435693e2..8aabb1771680a 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/index.stories.tsx @@ -9,8 +9,8 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; -import type { Props } from './package_list_grid'; -import { PackageListGrid } from './package_list_grid'; +import type { Props } from '.'; +import { PackageListGrid } from '.'; export default { component: PackageListGrid, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/index.tsx new file mode 100644 index 0000000000000..d3d283accf4d0 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/index.tsx @@ -0,0 +1,272 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ReactNode, FunctionComponent } from 'react'; +import { useMemo } from 'react'; +import React, { useCallback, useState } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiButton, + EuiButtonIcon, + EuiPopover, + EuiContextMenuPanel, + EuiContextMenuItem, +} from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n-react'; + +import { useLocalSearch, searchIdField } from '../../../../hooks'; + +import type { IntegrationCardItem } from '../../../../../../../common/types/models'; + +import type { + ExtendedIntegrationCategory, + CategoryFacet, +} from '../../screens/home/category_facets'; + +import type { IntegrationsURLParameters } from '../../screens/home/hooks/use_available_packages'; + +import { ExperimentalFeaturesService } from '../../../../services'; + +import { promoteFeaturedIntegrations } from '../utils'; + +import { ControlsColumn, MissingIntegrationContent, GridColumn } from './controls'; +import { SearchBox } from './search_box'; + +export interface Props { + isLoading?: boolean; + controls?: ReactNode | ReactNode[]; + list: IntegrationCardItem[]; + searchTerm: string; + setSearchTerm: (search: string) => void; + selectedCategory: ExtendedIntegrationCategory; + setCategory: (category: ExtendedIntegrationCategory) => void; + categories: CategoryFacet[]; + setUrlandReplaceHistory: (params: IntegrationsURLParameters) => void; + setUrlandPushHistory: (params: IntegrationsURLParameters) => void; + callout?: JSX.Element | null; + // Props used only in AvailablePackages component: + showCardLabels?: boolean; + title?: string; + availableSubCategories?: CategoryFacet[]; + selectedSubCategory?: string; + setSelectedSubCategory?: (c: string | undefined) => void; + showMissingIntegrationMessage?: boolean; +} + +export const PackageListGrid: FunctionComponent = ({ + isLoading, + controls, + title, + list, + searchTerm, + setSearchTerm, + selectedCategory, + setCategory, + categories, + availableSubCategories, + setSelectedSubCategory, + selectedSubCategory, + setUrlandReplaceHistory, + setUrlandPushHistory, + showMissingIntegrationMessage = false, + callout, + showCardLabels = true, +}) => { + const localSearchRef = useLocalSearch(list); + + const [isPopoverOpen, setPopover] = useState(false); + + const MAX_SUBCATEGORIES_NUMBER = 6; + + const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get(); + + const onButtonClick = () => { + setPopover(!isPopoverOpen); + }; + + const closePopover = () => { + setPopover(false); + }; + + const resetQuery = () => { + setSearchTerm(''); + setUrlandReplaceHistory({ searchString: '', categoryId: '', subCategoryId: '' }); + }; + + const onSubCategoryClick = useCallback( + (subCategory: string) => { + if (setSelectedSubCategory) setSelectedSubCategory(subCategory); + setUrlandPushHistory({ + categoryId: selectedCategory, + subCategoryId: subCategory, + }); + }, + [selectedCategory, setSelectedSubCategory, setUrlandPushHistory] + ); + + const filteredPromotedList = useMemo(() => { + if (isLoading) return []; + const filteredList = searchTerm + ? list.filter((item) => + (localSearchRef.current!.search(searchTerm) as IntegrationCardItem[]) + .map((match) => match[searchIdField]) + .includes(item[searchIdField]) + ) + : list; + + return promoteFeaturedIntegrations(filteredList, selectedCategory); + }, [isLoading, list, localSearchRef, searchTerm, selectedCategory]); + + const splitSubcategories = ( + subcategories: CategoryFacet[] | undefined + ): { visibleSubCategories?: CategoryFacet[]; hiddenSubCategories?: CategoryFacet[] } => { + if (!subcategories) return {}; + else if (subcategories && subcategories?.length < MAX_SUBCATEGORIES_NUMBER) { + return { visibleSubCategories: subcategories, hiddenSubCategories: [] }; + } else if (subcategories && subcategories?.length >= MAX_SUBCATEGORIES_NUMBER) { + return { + visibleSubCategories: subcategories.slice(0, MAX_SUBCATEGORIES_NUMBER), + hiddenSubCategories: subcategories.slice(MAX_SUBCATEGORIES_NUMBER), + }; + } + return {}; + }; + + const splitSubcat = splitSubcategories(availableSubCategories); + const { visibleSubCategories } = splitSubcat; + const hiddenSubCategoriesItems = useMemo(() => { + return splitSubcat?.hiddenSubCategories?.map((subCategory) => { + return ( + { + onSubCategoryClick(subCategory.id); + closePopover(); + }} + icon={selectedSubCategory === subCategory.id ? 'check' : 'empty'} + > + {subCategory.title} + + ); + }); + }, [onSubCategoryClick, selectedSubCategory, splitSubcat?.hiddenSubCategories]); + + return ( + + + + + + + {showIntegrationsSubcategories && availableSubCategories?.length ? : null} + {showIntegrationsSubcategories ? ( + + {visibleSubCategories?.map((subCategory) => { + const isSelected = subCategory.id === selectedSubCategory; + return ( + + onSubCategoryClick(subCategory.id)} + > + + + + ); + })} + {hiddenSubCategoriesItems?.length ? ( + + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + + ) : null} + + ) : null} + {callout ? ( + <> + + {callout} + + ) : null} + + + {showMissingIntegrationMessage && ( + <> + + + + + )} + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/search_box.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/search_box.tsx new file mode 100644 index 0000000000000..593ad3e60e82b --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/search_box.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 type { FunctionComponent } from 'react'; +import React, { useMemo } from 'react'; + +import { EuiFieldSearch, EuiText, useEuiTheme, EuiIcon, EuiScreenReaderOnly } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import type { + ExtendedIntegrationCategory, + CategoryFacet, +} from '../../screens/home/category_facets'; + +import type { IntegrationsURLParameters } from '../../screens/home/hooks/use_available_packages'; + +export interface Props { + searchTerm: string; + setSearchTerm: (search: string) => void; + selectedCategory: ExtendedIntegrationCategory; + setCategory: (category: ExtendedIntegrationCategory) => void; + categories: CategoryFacet[]; + availableSubCategories?: CategoryFacet[]; + setUrlandReplaceHistory: (params: IntegrationsURLParameters) => void; + selectedSubCategory?: string; + setSelectedSubCategory?: (c: string | undefined) => void; +} + +export const SearchBox: FunctionComponent = ({ + searchTerm, + setSearchTerm, + selectedCategory, + setCategory, + categories, + availableSubCategories, + setSelectedSubCategory, + selectedSubCategory, + setUrlandReplaceHistory, +}) => { + const { euiTheme } = useEuiTheme(); + + const onQueryChange = (e: any) => { + const queryText = e.target.value; + setSearchTerm(queryText); + setUrlandReplaceHistory({ + searchString: queryText, + categoryId: selectedCategory, + subCategoryId: selectedSubCategory, + }); + }; + + const selectedCategoryTitle = selectedCategory + ? categories.find((category) => category.id === selectedCategory)?.title + : undefined; + + const getCategoriesLabel = useMemo(() => { + const selectedSubCategoryTitle = + selectedSubCategory && availableSubCategories + ? availableSubCategories.find((subCat) => subCat.id === selectedSubCategory)?.title + : undefined; + + if (selectedCategoryTitle && selectedSubCategoryTitle) { + return `${selectedCategoryTitle}, ${selectedSubCategoryTitle}`; + } else if (selectedCategoryTitle) { + return `${selectedCategoryTitle}`; + } else return ''; + }, [availableSubCategories, selectedCategoryTitle, selectedSubCategory]); + + return ( + onQueryChange(e)} + isClearable={true} + incremental={true} + fullWidth={true} + prepend={ + selectedCategoryTitle ? ( + + + Searching category: + + {getCategoriesLabel} + + + ) : undefined + } + /> + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx index c1190a66c7034..f13fd592daccf 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx @@ -5,23 +5,20 @@ * 2.0. */ import React, { useState, useMemo } from 'react'; -import { useLocation, useParams, useHistory } from 'react-router-dom'; import { uniq, xorBy } from 'lodash'; import type { CustomIntegration } from '@kbn/custom-integrations-plugin/common'; import type { IntegrationPreferenceType } from '../../../components/integration_preference'; -import { usePackages, useCategories, useStartServices } from '../../../../../hooks'; +import { usePackages, useCategories } from '../../../../../hooks'; import { useGetAppendCustomIntegrations, useGetReplacementCustomIntegrations, - useLink, } from '../../../../../hooks'; import { useMergeEprPackagesWithReplacements } from '../../../../../hooks/use_merge_epr_with_replacements'; -import type { CategoryParams } from '..'; -import { getParams, mapToCard } from '..'; +import { mapToCard } from '..'; import type { PackageList, PackageListItem } from '../../../../../types'; import { doesPackageHaveIntegrations } from '../../../../../services'; @@ -31,8 +28,6 @@ import { isIntegrationPolicyTemplate, } from '../../../../../../../../common/services'; -import { pagePathGetters } from '../../../../../constants'; - import type { IntegrationCardItem } from '../../../../../../../../common/types/models'; import { ALL_CATEGORY } from '../category_facets'; @@ -40,6 +35,8 @@ import type { CategoryFacet } from '../category_facets'; import { mergeCategoriesAndCount } from '../util'; +import { useBuildIntegrationsUrl } from './use_build_integrations_url'; + export interface IntegrationsURLParameters { searchString?: string; categoryId?: string; @@ -111,14 +108,17 @@ export const useAvailablePackages = () => { const [prereleaseIntegrationsEnabled, setPrereleaseIntegrationsEnabled] = React.useState< boolean | undefined >(undefined); - const { http } = useStartServices(); - const addBasePath = http.basePath.prepend; const { - selectedCategory: initialSelectedCategory, - selectedSubcategory: initialSubcategory, + initialSelectedCategory, + initialSubcategory, + setUrlandPushHistory, + setUrlandReplaceHistory, + getHref, + getAbsolutePath, searchParam, - } = getParams(useParams(), useLocation().search); + addBasePath, + } = useBuildIntegrationsUrl(); const [selectedCategory, setCategory] = useState(initialSelectedCategory); const [selectedSubCategory, setSelectedSubCategory] = useState( @@ -126,45 +126,6 @@ export const useAvailablePackages = () => { ); const [searchTerm, setSearchTerm] = useState(searchParam || ''); - const { getHref, getAbsolutePath } = useLink(); - const history = useHistory(); - - const buildUrl = ({ searchString, categoryId, subCategoryId }: IntegrationsURLParameters) => { - const url = pagePathGetters.integrations_all({ - category: categoryId ? categoryId : '', - subCategory: subCategoryId ? subCategoryId : '', - searchTerm: searchString ? searchString : '', - })[1]; - return url; - }; - - const setUrlandPushHistory = ({ - searchString, - categoryId, - subCategoryId, - }: IntegrationsURLParameters) => { - const url = buildUrl({ - categoryId, - searchString, - subCategoryId, - }); - history.push(url); - }; - - const setUrlandReplaceHistory = ({ - searchString, - categoryId, - subCategoryId, - }: IntegrationsURLParameters) => { - const url = buildUrl({ - categoryId, - searchString, - subCategoryId, - }); - // Use .replace so the browser's back button is not tied to single keystroke - history.replace(url); - }; - const { data: eprPackages, isLoading: isLoadingAllPackages, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_build_integrations_url.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_build_integrations_url.tsx new file mode 100644 index 0000000000000..4c2a45586844d --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_build_integrations_url.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useLocation, useParams, useHistory } from 'react-router-dom'; + +import { useStartServices } from '../../../../../hooks'; +import { useLink } from '../../../../../hooks'; + +import type { CategoryParams } from '..'; +import { getParams } from '..'; + +import { pagePathGetters } from '../../../../../constants'; + +export interface IntegrationsURLParameters { + searchString?: string; + categoryId?: string; + subCategoryId?: string; +} + +export const useBuildIntegrationsUrl = () => { + const { http } = useStartServices(); + const addBasePath = http.basePath.prepend; + + const { + selectedCategory: initialSelectedCategory, + selectedSubcategory: initialSubcategory, + searchParam, + } = getParams(useParams(), useLocation().search); + + const { getHref, getAbsolutePath } = useLink(); + const history = useHistory(); + + const buildUrl = ({ searchString, categoryId, subCategoryId }: IntegrationsURLParameters) => { + const url = pagePathGetters.integrations_all({ + category: categoryId ? categoryId : '', + subCategory: subCategoryId ? subCategoryId : '', + searchTerm: searchString ? searchString : '', + })[1]; + return url; + }; + + const setUrlandPushHistory = ({ + searchString, + categoryId, + subCategoryId, + }: IntegrationsURLParameters) => { + const url = buildUrl({ + categoryId, + searchString, + subCategoryId, + }); + history.push(url); + }; + + const setUrlandReplaceHistory = ({ + searchString, + categoryId, + subCategoryId, + }: IntegrationsURLParameters) => { + const url = buildUrl({ + categoryId, + searchString, + subCategoryId, + }); + // Use .replace so the browser's back button is not tied to single keystroke + history.replace(url); + }; + + return { + initialSelectedCategory, + initialSubcategory, + setUrlandPushHistory, + setUrlandReplaceHistory, + getHref, + getAbsolutePath, + searchParam, + addBasePath, + }; +}; diff --git a/x-pack/plugins/fleet/public/types/ui_extensions.ts b/x-pack/plugins/fleet/public/types/ui_extensions.ts index fc8726e7b813a..53ae5322f0d9d 100644 --- a/x-pack/plugins/fleet/public/types/ui_extensions.ts +++ b/x-pack/plugins/fleet/public/types/ui_extensions.ts @@ -10,7 +10,9 @@ import type { ComponentType, LazyExoticComponent } from 'react'; import type { FleetServerAgentComponentUnit } from '../../common/types/models/agent'; -import type { Agent, NewPackagePolicy, PackageInfo, PackagePolicy } from '.'; +import type { PackagePolicyValidationResults } from '../services'; + +import type { Agent, AgentPolicy, NewPackagePolicy, PackageInfo, PackagePolicy } from '.'; /** Register a Fleet UI extension */ export type UIExtensionRegistrationCallback = (extensionPoint: UIExtensionPoint) => void; @@ -20,6 +22,22 @@ export interface UIExtensionsStorage { [key: string]: Partial>; } +/** + * UI Component Extension is used to replace the Define Step on + * the pages displaying the ability to edit/create an Integration Policy + */ +export type PackagePolicyReplaceDefineStepExtensionComponent = + ComponentType; + +export type PackagePolicyReplaceDefineStepExtensionComponentProps = ( + | (PackagePolicyEditExtensionComponentProps & { isEditPage: true }) + | (PackagePolicyCreateExtensionComponentProps & { isEditPage: false }) +) & { + validationResults?: PackagePolicyValidationResults; + agentPolicy?: AgentPolicy; + packageInfo: PackageInfo; +}; + /** * UI Component Extension is used on the pages displaying the ability to edit an * Integration Policy @@ -73,6 +91,12 @@ export interface PackageGenericErrorsListProps { packageErrors: FleetServerAgentComponentUnit[]; } +export interface PackagePolicyReplaceDefineStepExtension { + package: string; + view: 'package-policy-replace-define-step'; + Component: LazyExoticComponent; +} + /** Extension point registration contract for Integration Policy Edit views */ export interface PackagePolicyEditExtension { package: string; @@ -184,6 +208,7 @@ export interface AgentEnrollmentFlyoutFinalStepExtension { /** Fleet UI Extension Point */ export type UIExtensionPoint = + | PackagePolicyReplaceDefineStepExtension | PackagePolicyEditExtension | PackagePolicyResponseExtension | PackagePolicyEditTabsExtension diff --git a/x-pack/plugins/fleet/server/collectors/agent_policies.ts b/x-pack/plugins/fleet/server/collectors/agent_policies.ts index bd8075b09fd06..d70524e5f1e32 100644 --- a/x-pack/plugins/fleet/server/collectors/agent_policies.ts +++ b/x-pack/plugins/fleet/server/collectors/agent_policies.ts @@ -5,57 +5,55 @@ * 2.0. */ -import type { ElasticsearchClient } from '@kbn/core/server'; +import type { SavedObjectsClientContract } from '@kbn/core/server'; +import _ from 'lodash'; -import { AGENT_POLICY_INDEX } from '../../common'; -import { ES_SEARCH_LIMIT } from '../../common/constants'; -import { appContextService } from '../services'; +import { + AGENT_POLICY_SAVED_OBJECT_TYPE, + OUTPUT_SAVED_OBJECT_TYPE, + SO_SEARCH_LIMIT, +} from '../../common'; +import type { OutputSOAttributes, AgentPolicy } from '../types'; export interface AgentPoliciesUsage { count: number; output_types: string[]; } -const DEFAULT_AGENT_POLICIES_USAGE = { - count: 0, - output_types: [], -}; - export const getAgentPoliciesUsage = async ( - esClient: ElasticsearchClient, - abortController: AbortController + soClient: SavedObjectsClientContract ): Promise => { - try { - const res = await esClient.search( - { - index: AGENT_POLICY_INDEX, - size: ES_SEARCH_LIMIT, - track_total_hits: true, - rest_total_hits_as_int: true, - }, - { signal: abortController.signal } - ); - - const agentPolicies = res.hits.hits; - - const outputTypes = new Set(); - agentPolicies.forEach((item) => { - const source = (item._source as any) ?? {}; - Object.keys(source.data.outputs).forEach((output) => { - outputTypes.add(source.data.outputs[output].type); - }); + const { saved_objects: outputs } = await soClient.find({ + type: OUTPUT_SAVED_OBJECT_TYPE, + page: 1, + perPage: SO_SEARCH_LIMIT, + }); + + const defaultOutputId = outputs.find((output) => output.attributes.is_default)?.id || ''; + + const outputsById = _.keyBy(outputs, 'id'); + + const { saved_objects: agentPolicies, total: totalAgentPolicies } = + await soClient.find({ + type: AGENT_POLICY_SAVED_OBJECT_TYPE, + page: 1, + perPage: SO_SEARCH_LIMIT, }); - return { - count: res.hits.total as number, - output_types: Array.from(outputTypes), - }; - } catch (error) { - if (error.statusCode === 404) { - appContextService.getLogger().debug('Index .fleet-policies does not exist yet.'); - } else { - throw error; - } - return DEFAULT_AGENT_POLICIES_USAGE; - } + const uniqueOutputIds = new Set(); + agentPolicies.forEach((agentPolicy) => { + uniqueOutputIds.add(agentPolicy.attributes.monitoring_output_id || defaultOutputId); + uniqueOutputIds.add(agentPolicy.attributes.data_output_id || defaultOutputId); + }); + + const uniqueOutputTypes = new Set( + Array.from(uniqueOutputIds).map((outputId) => { + return outputsById[outputId]?.attributes.type; + }) + ); + + return { + count: totalAgentPolicies, + output_types: Array.from(uniqueOutputTypes), + }; }; diff --git a/x-pack/plugins/fleet/server/collectors/register.ts b/x-pack/plugins/fleet/server/collectors/register.ts index 876a60a2d9f67..d59298e835fa6 100644 --- a/x-pack/plugins/fleet/server/collectors/register.ts +++ b/x-pack/plugins/fleet/server/collectors/register.ts @@ -62,7 +62,7 @@ export const fetchFleetUsage = async ( packages: await getPackageUsage(soClient), ...(await getAgentData(esClient, abortController)), fleet_server_config: await getFleetServerConfig(soClient), - agent_policies: await getAgentPoliciesUsage(esClient, abortController), + agent_policies: await getAgentPoliciesUsage(soClient), ...(await getPanicLogsLastHour(esClient)), // TODO removed top errors telemetry as it causes this issue: https://github.com/elastic/kibana/issues/148976 // ...(await getAgentLogsTopErrors(esClient)), diff --git a/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts b/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts index 73f4af6c3038d..8d29d362125e3 100644 --- a/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts @@ -270,6 +270,50 @@ describe('fleet usage telemetry', () => { }, ], }); + + await soClient.create( + 'ingest-outputs', + { + name: 'output2', + type: 'third_type', + hosts: ['http://localhost:9300'], + is_default: false, + is_default_monitoring: false, + config_yaml: '', + ca_trusted_fingerprint: '', + proxy_id: null, + }, + { id: 'output2' } + ); + await soClient.create( + 'ingest-outputs', + { + name: 'output3', + type: 'logstash', + hosts: ['http://localhost:9400'], + is_default: false, + is_default_monitoring: false, + config_yaml: '', + ca_trusted_fingerprint: '', + proxy_id: null, + }, + { id: 'output3' } + ); + + await soClient.create('ingest-agent-policies', { + namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], + name: 'Another policy', + description: 'Policy 2', + inactivity_timeout: 1209600, + status: 'active', + is_managed: false, + revision: 2, + updated_by: 'system', + schema_version: '1.0.0', + data_output_id: 'output2', + monitoring_output_id: 'output3', + }); }); afterAll(async () => { @@ -324,7 +368,10 @@ describe('fleet usage telemetry', () => { }, ], }, - agent_policies: { count: 3, output_types: ['elasticsearch'] }, + agent_policies: { + count: 2, + output_types: expect.arrayContaining(['elasticsearch', 'logstash', 'third_type']), + }, agent_logs_panics_last_hour: [ { timestamp: expect.any(String), diff --git a/x-pack/plugins/fleet/server/routes/agent/handlers.ts b/x-pack/plugins/fleet/server/routes/agent/handlers.ts index bb43d32b6cfaf..24dd0d355411d 100644 --- a/x-pack/plugins/fleet/server/routes/agent/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/handlers.ts @@ -186,10 +186,10 @@ export const getAgentsHandler: RequestHandler< kuery: request.query.kuery, sortField: request.query.sortField, sortOrder: request.query.sortOrder, - getTotalInactive: request.query.showInactive, + getStatusSummary: request.query.getStatusSummary, }); - const { total, page, perPage, totalInactive = 0 } = agentRes; + const { total, page, perPage, statusSummary } = agentRes; let { agents } = agentRes; // Assign metrics @@ -201,9 +201,9 @@ export const getAgentsHandler: RequestHandler< list: agents, // deprecated items: agents, total, - totalInactive, page, perPage, + ...(statusSummary ? { statusSummary } : {}), }; return response.ok({ body }); } catch (error) { diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts index 04dce51e52969..92ab414eab2b1 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts @@ -14,16 +14,8 @@ import type { FleetAuthzRouter } from '../../services/security'; import { PACKAGE_POLICY_API_ROUTES } from '../../../common/constants'; import { appContextService, packagePolicyService } from '../../services'; import { createAppContextStartContractMock, xpackMocks } from '../../mocks'; -import type { - PackagePolicyClient, - PostPackagePolicyCreateCallback, - PutPackagePolicyUpdateCallback, - FleetRequestHandlerContext, -} from '../..'; -import type { - CreatePackagePolicyRequestSchema, - UpdatePackagePolicyRequestSchema, -} from '../../types/rest_spec'; +import type { PackagePolicyClient, FleetRequestHandlerContext } from '../..'; +import type { UpdatePackagePolicyRequestSchema } from '../../types/rest_spec'; import type { FleetRequestHandler } from '../../types'; import type { PackagePolicy } from '../../types'; @@ -125,7 +117,6 @@ describe('When calling package policy', () => { let routeConfig: RouteConfig; let context: FleetRequestHandlerContext; let response: ReturnType; - let packagePolicyServiceWithAuthzMock: jest.Mocked; beforeEach(() => { routerMock = httpServiceMock.createRouter() as unknown as jest.Mocked; @@ -135,8 +126,7 @@ describe('When calling package policy', () => { beforeEach(async () => { appContextService.start(createAppContextStartContractMock()); context = xpackMocks.createRequestHandlerContext() as unknown as FleetRequestHandlerContext; - packagePolicyServiceWithAuthzMock = (await context.fleet).packagePolicyService - .asCurrentUser as jest.Mocked; + (await context.fleet).packagePolicyService.asCurrentUser as jest.Mocked; response = httpServerMock.createResponseFactory(); }); @@ -145,186 +135,6 @@ describe('When calling package policy', () => { appContextService.stop(); }); - describe('create api handler', () => { - const getCreateKibanaRequest = ( - newData?: typeof CreatePackagePolicyRequestSchema.body - ): KibanaRequest => { - return httpServerMock.createKibanaRequest< - undefined, - undefined, - typeof CreatePackagePolicyRequestSchema.body - >({ - path: routeConfig.path, - method: 'post', - body: newData || { - name: 'endpoint-1', - description: '', - policy_id: 'a5ca00c0-b30c-11ea-9732-1bb05811278c', - enabled: true, - inputs: [], - namespace: 'default', - package: { name: 'endpoint', title: 'Elastic Endpoint', version: '0.5.0' }, - }, - }); - }; - - // Set the routeConfig and routeHandler to the Create API - beforeEach(() => { - [routeConfig, routeHandler] = routerMock.post.mock.calls.find( - ([{ path }]) => path === PACKAGE_POLICY_API_ROUTES.CREATE_PATTERN - )!; - }); - - describe('and external callbacks are registered', () => { - const callbackCallingOrder: string[] = []; - - // Callback one adds an input that includes a `config` property - const callbackOne: PostPackagePolicyCreateCallback | PutPackagePolicyUpdateCallback = jest.fn( - async (ds) => { - callbackCallingOrder.push('one'); - const newDs = { - ...ds, - inputs: [ - { - type: 'endpoint', - enabled: true, - streams: [], - config: { - one: { - value: 'inserted by callbackOne', - }, - }, - }, - ], - }; - return newDs; - } - ); - - // Callback two adds an additional `input[0].config` property - const callbackTwo: PostPackagePolicyCreateCallback | PutPackagePolicyUpdateCallback = jest.fn( - async (ds) => { - callbackCallingOrder.push('two'); - const newDs = { - ...ds, - inputs: [ - { - ...ds.inputs[0], - config: { - ...ds.inputs[0].config, - two: { - value: 'inserted by callbackTwo', - }, - }, - }, - ], - }; - return newDs; - } - ); - - beforeEach(() => { - appContextService.addExternalCallback('packagePolicyCreate', callbackOne); - appContextService.addExternalCallback('packagePolicyCreate', callbackTwo); - }); - - afterEach(() => (callbackCallingOrder.length = 0)); - - it('should create with data from callback', async () => { - const request = getCreateKibanaRequest(); - packagePolicyServiceMock.runExternalCallbacks.mockImplementationOnce(() => - Promise.resolve({ - policy_id: 'a5ca00c0-b30c-11ea-9732-1bb05811278c', - description: '', - enabled: true, - inputs: [ - { - config: { - one: { - value: 'inserted by callbackOne', - }, - two: { - value: 'inserted by callbackTwo', - }, - }, - enabled: true, - streams: [], - type: 'endpoint', - }, - ], - name: 'endpoint-1', - namespace: 'default', - package: { - name: 'endpoint', - title: 'Elastic Endpoint', - version: '0.5.0', - }, - }) - ); - await routeHandler(context, request, response); - expect(response.ok).toHaveBeenCalled(); - - expect(packagePolicyServiceWithAuthzMock.create.mock.calls[0][2]).toEqual({ - policy_id: 'a5ca00c0-b30c-11ea-9732-1bb05811278c', - description: '', - enabled: true, - inputs: [ - { - config: { - one: { - value: 'inserted by callbackOne', - }, - two: { - value: 'inserted by callbackTwo', - }, - }, - enabled: true, - streams: [], - type: 'endpoint', - }, - ], - name: 'endpoint-1', - namespace: 'default', - package: { - name: 'endpoint', - title: 'Elastic Endpoint', - version: '0.5.0', - }, - }); - }); - }); - - describe('postCreate callback registration', () => { - it('should call to packagePolicyCreate and packagePolicyPostCreate call backs', async () => { - const request = getCreateKibanaRequest(); - await routeHandler(context, request, response); - - expect(response.ok).toHaveBeenCalled(); - expect(packagePolicyService.runExternalCallbacks).toBeCalledTimes(2); - - const firstCB = packagePolicyServiceMock.runExternalCallbacks.mock.calls[0][0]; - const secondCB = packagePolicyServiceMock.runExternalCallbacks.mock.calls[1][0]; - - expect(firstCB).toEqual('packagePolicyCreate'); - expect(secondCB).toEqual('packagePolicyPostCreate'); - }); - - it('should not call packagePolicyPostCreate call back in case of packagePolicy create failed', async () => { - const request = getCreateKibanaRequest(); - - packagePolicyServiceWithAuthzMock.create.mockImplementationOnce(() => { - throw new Error('foo'); - }); - - await routeHandler(context, request, response); - const firstCB = packagePolicyServiceMock.runExternalCallbacks.mock.calls[0][0]; - - expect(firstCB).toEqual('packagePolicyCreate'); - expect(packagePolicyService.runExternalCallbacks).toBeCalledTimes(1); - }); - }); - }); - describe('update api handler', () => { const getUpdateKibanaRequest = ( newData?: typeof UpdatePackagePolicyRequestSchema.body diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index 8735cdf08cafd..51b4056843d25 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -247,33 +247,21 @@ export const createPackagePolicyHandler: FleetRequestHandler< } as NewPackagePolicy); } - const newData = await packagePolicyService.runExternalCallbacks( - 'packagePolicyCreate', - newPackagePolicy, - context, - request - ); - // Create package policy const packagePolicy = await fleetContext.packagePolicyService.asCurrentUser.create( soClient, esClient, - newData, + newPackagePolicy, { user, force, spaceId, - } - ); - - const enrichedPackagePolicy = await packagePolicyService.runExternalCallbacks( - 'packagePolicyPostCreate', - packagePolicy, + }, context, request ); - const body: CreatePackagePolicyResponse = { item: enrichedPackagePolicy }; + const body: CreatePackagePolicyResponse = { item: packagePolicy }; return response.ok({ body, @@ -368,12 +356,6 @@ export const updatePackagePolicyHandler: FleetRequestHandler< vars: body.vars ?? packagePolicy.vars, } as NewPackagePolicy; } - newData = await packagePolicyService.runExternalCallbacks( - 'packagePolicyUpdate', - newData, - context, - request - ); const updatedPackagePolicy = await packagePolicyService.update( soClient, @@ -400,46 +382,17 @@ export const deletePackagePolicyHandler: RequestHandler< const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; - const logger = appContextService.getLogger(); try { - try { - const packagePolicies = await packagePolicyService.getByIDs( - soClient, - request.body.packagePolicyIds, - { ignoreMissing: true } - ); - - if (packagePolicies) { - await packagePolicyService.runExternalCallbacks( - 'packagePolicyDelete', - packagePolicies, - context, - request - ); - } - } catch (error) { - logger.error(`An error occurred executing external callback: ${error}`); - logger.error(error); - } - const body: PostDeletePackagePoliciesResponse = await packagePolicyService.delete( soClient, esClient, request.body.packagePolicyIds, - { user, force: request.body.force, skipUnassignFromAgentPolicies: request.body.force } + { user, force: request.body.force, skipUnassignFromAgentPolicies: request.body.force }, + context, + request ); - try { - await packagePolicyService.runExternalCallbacks( - 'packagePolicyPostDelete', - body, - context, - request - ); - } catch (error) { - logger.error(`An error occurred executing external callback: ${error}`); - logger.error(error); - } + return response.ok({ body, }); @@ -457,30 +410,15 @@ export const deleteOnePackagePolicyHandler: RequestHandler< const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; - const logger = appContextService.getLogger(); try { - try { - const packagePolicy = await packagePolicyService.get( - soClient, - request.params.packagePolicyId - ); - await packagePolicyService.runExternalCallbacks( - 'packagePolicyDelete', - packagePolicy ? [packagePolicy] : [], - context, - request - ); - } catch (error) { - logger.error(`An error occurred executing external callback: ${error}`); - logger.error(error); - } - const res = await packagePolicyService.delete( soClient, esClient, [request.params.packagePolicyId], - { user, force: request.query.force, skipUnassignFromAgentPolicies: request.query.force } + { user, force: request.query.force, skipUnassignFromAgentPolicies: request.query.force }, + context, + request ); if ( @@ -493,17 +431,7 @@ export const deleteOnePackagePolicyHandler: RequestHandler< body: res[0].body, }); } - try { - await packagePolicyService.runExternalCallbacks( - 'packagePolicyPostDelete', - res, - context, - request - ); - } catch (error) { - logger.error(`An error occurred executing external callback: ${error}`); - logger.error(error); - } + return response.ok({ body: { id: request.params.packagePolicyId }, }); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/monitoring_permissions.test.ts.snap b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/monitoring_permissions.test.ts.snap index 3917a7d71533b..49a11c226ce49 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/monitoring_permissions.test.ts.snap +++ b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/monitoring_permissions.test.ts.snap @@ -109,6 +109,7 @@ Object { "logs-elastic_agent.elastic_agent-testnamespace123", "logs-elastic_agent.apm_server-testnamespace123", "logs-elastic_agent.filebeat-testnamespace123", + "logs-elastic_agent.filebeat_input-testnamespace123", "logs-elastic_agent.fleet_server-testnamespace123", "logs-elastic_agent.metricbeat-testnamespace123", "logs-elastic_agent.osquerybeat-testnamespace123", @@ -122,6 +123,7 @@ Object { "metrics-elastic_agent.elastic_agent-testnamespace123", "metrics-elastic_agent.apm_server-testnamespace123", "metrics-elastic_agent.filebeat-testnamespace123", + "metrics-elastic_agent.filebeat_input-testnamespace123", "metrics-elastic_agent.fleet_server-testnamespace123", "metrics-elastic_agent.metricbeat-testnamespace123", "metrics-elastic_agent.osquerybeat-testnamespace123", @@ -152,6 +154,7 @@ Object { "logs-elastic_agent.elastic_agent-testnamespace123", "logs-elastic_agent.apm_server-testnamespace123", "logs-elastic_agent.filebeat-testnamespace123", + "logs-elastic_agent.filebeat_input-testnamespace123", "logs-elastic_agent.fleet_server-testnamespace123", "logs-elastic_agent.metricbeat-testnamespace123", "logs-elastic_agent.osquerybeat-testnamespace123", @@ -182,6 +185,7 @@ Object { "metrics-elastic_agent.elastic_agent-testnamespace123", "metrics-elastic_agent.apm_server-testnamespace123", "metrics-elastic_agent.filebeat-testnamespace123", + "metrics-elastic_agent.filebeat_input-testnamespace123", "metrics-elastic_agent.fleet_server-testnamespace123", "metrics-elastic_agent.metricbeat-testnamespace123", "metrics-elastic_agent.osquerybeat-testnamespace123", diff --git a/x-pack/plugins/fleet/server/services/agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policy.test.ts index 014764743ee35..b223e88102d9c 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.test.ts @@ -169,13 +169,6 @@ describe('agent policy', () => { ]); }); - it('should run package policy delete external callbacks', async () => { - await agentPolicyService.delete(soClient, esClient, 'mocked'); - expect(packagePolicyService.runPostDeleteExternalCallbacks).toHaveBeenCalledWith([ - { id: 'package-1' }, - ]); - }); - it('should throw error for agent policy which has managed package poolicy', async () => { mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([ { @@ -192,12 +185,6 @@ describe('agent policy', () => { ).message ); } - - await agentPolicyService.delete(soClient, esClient, 'mocked', { force: true }); - - expect(packagePolicyService.runPostDeleteExternalCallbacks).toHaveBeenCalledWith([ - { id: 'package-1' }, - ]); }); }); diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index f2e51b9c95bf5..d845d466ea381 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -51,7 +51,6 @@ import type { FleetServerPolicy, Installation, Output, - PostDeletePackagePoliciesResponse, PackageInfo, } from '../../common/types'; import { @@ -681,25 +680,15 @@ class AgentPolicyService { ); } - await packagePolicyService.runDeleteExternalCallbacks(packagePolicies); - - const deletedPackagePolicies: PostDeletePackagePoliciesResponse = - await packagePolicyService.delete( - soClient, - esClient, - packagePolicies.map((p) => p.id), - { - force: options?.force, - skipUnassignFromAgentPolicies: true, - } - ); - try { - await packagePolicyService.runPostDeleteExternalCallbacks(deletedPackagePolicies); - } catch (error) { - const logger = appContextService.getLogger(); - logger.error(`An error occurred executing external callback: ${error}`); - logger.error(error); - } + await packagePolicyService.delete( + soClient, + esClient, + packagePolicies.map((p) => p.id), + { + force: options?.force, + skipUnassignFromAgentPolicies: true, + } + ); } if (agentPolicy.is_preconfigured && !options?.force) { diff --git a/x-pack/plugins/fleet/server/services/agents/action_status.ts b/x-pack/plugins/fleet/server/services/agents/action_status.ts index 251da3eeac7b5..24bb3e3b5b6b4 100644 --- a/x-pack/plugins/fleet/server/services/agents/action_status.ts +++ b/x-pack/plugins/fleet/server/services/agents/action_status.ts @@ -119,14 +119,13 @@ export async function getActionStatuses( ...action, nbAgentsAck: nbAgentsAck - errorCount, nbAgentsFailed: errorCount, - status: - errorCount > 0 - ? 'FAILED' - : complete - ? 'COMPLETE' - : cancelledAction - ? 'CANCELLED' - : action.status, + status: cancelledAction + ? 'CANCELLED' + : errorCount > 0 && complete + ? 'FAILED' + : complete + ? 'COMPLETE' + : action.status, nbAgentsActioned, cancellationTime: cancelledAction?.timestamp, completionTime, @@ -196,7 +195,10 @@ async function _getActions( const source = hit._source!; if (!acc[source.action_id!]) { - const isExpired = source.expiration ? Date.parse(source.expiration) < Date.now() : false; + const isExpired = + source.expiration && source.type !== 'UPGRADE' + ? Date.parse(source.expiration) < Date.now() + : false; acc[hit._source.action_id] = { actionId: hit._source.action_id, nbAgentsActionCreated: 0, diff --git a/x-pack/plugins/fleet/server/services/agents/actions.ts b/x-pack/plugins/fleet/server/services/agents/actions.ts index 76fcaac7086d5..80a23e4ecbb7b 100644 --- a/x-pack/plugins/fleet/server/services/agents/actions.ts +++ b/x-pack/plugins/fleet/server/services/agents/actions.ts @@ -261,6 +261,7 @@ export async function cancelAgentAction(esClient: ElasticsearchClient, actionId: continue; } if (hit._source.type === 'UPGRADE') { + const errors = {}; await bulkUpdateAgents( esClient, hit._source.agents.map((agentId) => ({ @@ -270,8 +271,13 @@ export async function cancelAgentAction(esClient: ElasticsearchClient, actionId: upgrade_started_at: null, }, })), - {} + errors ); + if (Object.keys(errors).length > 0) { + appContextService + .getLogger() + .debug(`Errors while bulk updating agents for cancel action: ${JSON.stringify(errors)}`); + } } await createAgentAction(esClient, { id: cancelActionId, diff --git a/x-pack/plugins/fleet/server/services/agents/crud.ts b/x-pack/plugins/fleet/server/services/agents/crud.ts index 412603df68c81..379a656b7d519 100644 --- a/x-pack/plugins/fleet/server/services/agents/crud.ts +++ b/x-pack/plugins/fleet/server/services/agents/crud.ts @@ -13,7 +13,7 @@ import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import type { AgentSOAttributes, Agent, ListWithKuery } from '../../types'; import { appContextService, agentPolicyService } from '..'; -import type { FleetServerAgent } from '../../../common/types'; +import type { AgentStatus, FleetServerAgent } from '../../../common/types'; import { SO_SEARCH_LIMIT } from '../../../common/constants'; import { isAgentUpgradeable } from '../../../common/services'; import { AGENTS_INDEX } from '../../constants'; @@ -194,7 +194,7 @@ export async function getAgentsByKuery( soClient: SavedObjectsClientContract, options: ListWithKuery & { showInactive: boolean; - getTotalInactive?: boolean; + getStatusSummary?: boolean; sortField?: string; sortOrder?: 'asc' | 'desc'; pitId?: string; @@ -205,7 +205,7 @@ export async function getAgentsByKuery( total: number; page: number; perPage: number; - totalInactive?: number; + statusSummary?: Record; }> { const { page = 1, @@ -214,10 +214,10 @@ export async function getAgentsByKuery( sortOrder = options.sortOrder ?? 'desc', kuery, showInactive = false, + getStatusSummary = false, showUpgradeable, searchAfter, pitId, - getTotalInactive = false, } = options; const filters = []; @@ -238,8 +238,24 @@ export async function getAgentsByKuery( const secondarySort: estypes.Sort = isDefaultSort ? [{ 'local_metadata.host.hostname.keyword': { order: 'asc' } }] : []; + + const statusSummary: Record = { + online: 0, + error: 0, + inactive: 0, + offline: 0, + updating: 0, + unenrolled: 0, + degraded: 0, + enrolling: 0, + unenrolling: 0, + }; + const queryAgents = async (from: number, size: number) => - esClient.search({ + esClient.search< + FleetServerAgent, + { status: { buckets: Array<{ key: AgentStatus; doc_count: number }> } } + >({ from, size, track_total_hits: true, @@ -247,7 +263,7 @@ export async function getAgentsByKuery( runtime_mappings: runtimeFields, fields: Object.keys(runtimeFields), sort: [{ [sortField]: { order: sortOrder } }, ...secondarySort], - post_filter: kueryNode ? toElasticsearchQuery(kueryNode) : undefined, + query: kueryNode ? toElasticsearchQuery(kueryNode) : undefined, ...(pitId ? { pit: { @@ -260,13 +276,7 @@ export async function getAgentsByKuery( ignore_unavailable: true, }), ...(pitId && searchAfter ? { search_after: searchAfter, from: 0 } : {}), - ...(getTotalInactive && { - aggregations: { - totalInactive: { - filter: { bool: { must: { terms: { status: ['inactive', 'unenrolled'] } } } }, - }, - }, - }), + ...(getStatusSummary && { aggs: { status: { terms: { field: 'status' } } } }), }); let res; try { @@ -278,10 +288,6 @@ export async function getAgentsByKuery( let agents = res.hits.hits.map(searchHitToAgent); let total = res.hits.total as number; - let totalInactive = 0; - if (getTotalInactive && res.aggregations) { - totalInactive = res.aggregations?.totalInactive?.doc_count ?? 0; - } // filtering for a range on the version string will not work, // nor does filtering on a flattened field (local_metadata), so filter here if (showUpgradeable) { @@ -303,12 +309,18 @@ export async function getAgentsByKuery( } } + if (getStatusSummary) { + res.aggregations?.status.buckets.forEach((bucket) => { + statusSummary[bucket.key] = bucket.doc_count; + }); + } + return { agents, total, page, perPage, - ...(getTotalInactive && { totalInactive }), + ...(getStatusSummary ? { statusSummary } : {}), }; } @@ -477,7 +489,7 @@ export async function bulkUpdateAgents( { update: { _id: agentId, - retry_on_conflict: 3, + retry_on_conflict: 5, }, }, { diff --git a/x-pack/plugins/fleet/server/services/agents/status.ts b/x-pack/plugins/fleet/server/services/agents/status.ts index fecc369f6ad33..88761c53ee473 100644 --- a/x-pack/plugins/fleet/server/services/agents/status.ts +++ b/x-pack/plugins/fleet/server/services/agents/status.ts @@ -16,6 +16,8 @@ import type { QueryDslQueryContainer, } from '@elastic/elasticsearch/lib/api/types'; +import { agentStatusesToSummary } from '../../../common/services'; + import { AGENTS_INDEX } from '../../constants'; import type { AgentStatus } from '../../types'; import { FleetUnauthorizedError } from '../../errors'; @@ -122,15 +124,8 @@ export async function getAgentStatusForAgentPolicy( } }); - const combinedStatuses = { - online: statuses.online, - error: statuses.error + statuses.degraded, - inactive: statuses.inactive, - offline: statuses.offline, - updating: statuses.updating + statuses.enrolling + statuses.unenrolling, - unenrolled: statuses.unenrolled, - }; - + const { healthy: online, unhealthy: error, ...otherStatuses } = agentStatusesToSummary(statuses); + const combinedStatuses = { online, error, ...otherStatuses }; return { ...combinedStatuses, /* @deprecated no agents will have other status */ diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index ae50ec3f785f3..343b6129d0a1a 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -826,6 +826,83 @@ describe('Package policy service', () => { expect(result.name).toEqual('endpoint-1'); }); + it('should not fail to update if skipUniqueNameVerification: false when the name is not updated but duplicates exists', async () => { + const savedObjectsClient = savedObjectsClientMock.create(); + savedObjectsClient.find.mockResolvedValue({ + total: 1, + per_page: 1, + page: 1, + saved_objects: [ + { + id: 'existing-package-policy', + type: 'ingest-package-policies', + score: 1, + references: [], + version: '1.0.0', + attributes: { + name: 'endpoint-1', + description: '', + namespace: 'default', + enabled: true, + policy_id: 'policy-id-1', + package: { + name: 'endpoint', + title: 'Elastic Endpoint', + version: '0.9.0', + }, + inputs: [], + }, + }, + ], + }); + savedObjectsClient.get.mockResolvedValue({ + id: 'the-package-policy-id', + type: 'abcd', + references: [], + version: 'test', + attributes: { + name: 'endpoint-1', + }, + }); + savedObjectsClient.update.mockImplementation( + async ( + type: string, + id: string, + attrs: any + ): Promise> => { + savedObjectsClient.get.mockResolvedValue({ + id: 'the-package-policy-id', + type, + references: [], + version: 'test', + attributes: attrs, + }); + return attrs; + } + ); + const elasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + const result = await packagePolicyService.update( + savedObjectsClient, + elasticsearchClient, + 'the-package-policy-id', + { + name: 'endpoint-1', + description: '', + namespace: 'default', + enabled: true, + policy_id: '93c46720-c217-11ea-9906-b5b8a21b268e', + package: { + name: 'endpoint', + title: 'Elastic Endpoint', + version: '0.9.0', + }, + inputs: [], + }, + { skipUniqueNameVerification: false } + ); + expect(result.name).toEqual('endpoint-1'); + }); + it('should throw if the user try to update input vars that are frozen', async () => { const savedObjectsClient = savedObjectsClientMock.create(); const mockPackagePolicy = createPackagePolicyMock(); @@ -2055,10 +2132,10 @@ describe('Package policy service', () => { { id: 'a', success: true }, { id: 'a', success: true }, ]; - callbackOne = jest.fn(async (deletedPolicies) => { + callbackOne = jest.fn(async (deletedPolicies, soClient, esClient) => { callingOrder.push('one'); }); - callbackTwo = jest.fn(async (deletedPolicies) => { + callbackTwo = jest.fn(async (deletedPolicies, soClient, esClient) => { callingOrder.push('two'); }); appContextService.addExternalCallback('packagePolicyPostDelete', callbackOne); @@ -2070,25 +2147,54 @@ describe('Package policy service', () => { }); it('should execute external callbacks', async () => { - await packagePolicyService.runPostDeleteExternalCallbacks(deletedPackagePolicies); + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - expect(callbackOne).toHaveBeenCalledWith(deletedPackagePolicies); - expect(callbackTwo).toHaveBeenCalledWith(deletedPackagePolicies); + await packagePolicyService.runPostDeleteExternalCallbacks( + deletedPackagePolicies, + soClient, + esClient + ); + + expect(callbackOne).toHaveBeenCalledWith( + deletedPackagePolicies, + expect.any(Object), + expect.any(Object), + undefined, + undefined + ); + expect(callbackTwo).toHaveBeenCalledWith( + deletedPackagePolicies, + expect.any(Object), + expect.any(Object), + undefined, + undefined + ); expect(callingOrder).toEqual(['one', 'two']); }); it("should execute all external callbacks even if one throw's", async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + callbackOne.mockImplementation(async (deletedPolicies) => { callingOrder.push('one'); throw new Error('foo'); }); await expect( - packagePolicyService.runPostDeleteExternalCallbacks(deletedPackagePolicies) + packagePolicyService.runPostDeleteExternalCallbacks( + deletedPackagePolicies, + soClient, + esClient + ) ).rejects.toThrow(FleetError); expect(callingOrder).toEqual(['one', 'two']); }); it('should provide an array of errors encountered by running external callbacks', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + let error: FleetError; const callbackOneError = new Error('foo 1'); const callbackTwoError = new Error('foo 2'); @@ -2103,7 +2209,7 @@ describe('Package policy service', () => { }); await packagePolicyService - .runPostDeleteExternalCallbacks(deletedPackagePolicies) + .runPostDeleteExternalCallbacks(deletedPackagePolicies, soClient, esClient) .catch((e) => { error = e; }); @@ -2126,10 +2232,10 @@ describe('Package policy service', () => { appContextService.start(createAppContextStartContractMock()); callingOrder = []; packagePolicies = [{ id: 'a' }, { id: 'a' }] as DeletePackagePoliciesResponse; - callbackOne = jest.fn(async (deletedPolicies) => { + callbackOne = jest.fn(async (deletedPolicies, soClient, esClient) => { callingOrder.push('one'); }); - callbackTwo = jest.fn(async (deletedPolicies) => { + callbackTwo = jest.fn(async (deletedPolicies, soClient, esClient) => { callingOrder.push('two'); }); appContextService.addExternalCallback('packagePolicyDelete', callbackOne); @@ -2141,25 +2247,31 @@ describe('Package policy service', () => { }); it('should execute external callbacks', async () => { - await packagePolicyService.runDeleteExternalCallbacks(packagePolicies); + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await packagePolicyService.runDeleteExternalCallbacks(packagePolicies, soClient, esClient); - expect(callbackOne).toHaveBeenCalledWith(packagePolicies); - expect(callbackTwo).toHaveBeenCalledWith(packagePolicies); + expect(callbackOne).toHaveBeenCalledWith(packagePolicies, soClient, esClient); + expect(callbackTwo).toHaveBeenCalledWith(packagePolicies, soClient, esClient); expect(callingOrder).toEqual(['one', 'two']); }); it("should execute all external callbacks even if one throw's", async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; callbackOne.mockImplementation(async (deletedPolicies) => { callingOrder.push('one'); throw new Error('foo'); }); await expect( - packagePolicyService.runDeleteExternalCallbacks(packagePolicies) + packagePolicyService.runDeleteExternalCallbacks(packagePolicies, soClient, esClient) ).rejects.toThrow(FleetError); expect(callingOrder).toEqual(['one', 'two']); }); it('should provide an array of errors encountered by running external callbacks', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; let error: FleetError; const callbackOneError = new Error('foo 1'); const callbackTwoError = new Error('foo 2'); @@ -2173,9 +2285,11 @@ describe('Package policy service', () => { throw callbackTwoError; }); - await packagePolicyService.runDeleteExternalCallbacks(packagePolicies).catch((e) => { - error = e; - }); + await packagePolicyService + .runDeleteExternalCallbacks(packagePolicies, soClient, esClient) + .catch((e) => { + error = e; + }); expect(error!.message).toEqual( '2 encountered while executing package delete external callbacks' @@ -2257,6 +2371,9 @@ describe('Package policy service', () => { }); it('should call external callbacks in expected order', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + const callbackA: CombinedExternalCallback = jest.fn(async (ds) => { callbackCallingOrder.push('a'); return ds; @@ -2273,6 +2390,8 @@ describe('Package policy service', () => { await packagePolicyService.runExternalCallbacks( 'packagePolicyCreate', newPackagePolicy, + soClient, + esClient, coreMock.createCustomRequestHandlerContext(context), request ); @@ -2280,12 +2399,17 @@ describe('Package policy service', () => { }); it('should feed package policy returned by last callback', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + appContextService.addExternalCallback('packagePolicyCreate', callbackOne); appContextService.addExternalCallback('packagePolicyCreate', callbackTwo); await packagePolicyService.runExternalCallbacks( 'packagePolicyCreate', newPackagePolicy, + soClient, + esClient, coreMock.createCustomRequestHandlerContext(context), request ); @@ -2329,10 +2453,15 @@ describe('Package policy service', () => { }); it('should fail to execute remaining callbacks after a callback exception', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + try { await packagePolicyService.runExternalCallbacks( 'packagePolicyCreate', newPackagePolicy, + soClient, + esClient, coreMock.createCustomRequestHandlerContext(context), request ); @@ -2348,10 +2477,14 @@ describe('Package policy service', () => { }); it('should fail to return the package policy', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; expect( packagePolicyService.runExternalCallbacks( 'packagePolicyCreate', newPackagePolicy, + soClient, + esClient, coreMock.createCustomRequestHandlerContext(context), request ) @@ -2427,6 +2560,9 @@ describe('Package policy service', () => { }); it('should execute PostPackagePolicyPostCreateCallback external callbacks', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + const callbackA: PostPackagePolicyPostCreateCallback = jest.fn(async (ds) => { callbackCallingOrder.push('a'); return ds; @@ -2444,12 +2580,26 @@ describe('Package policy service', () => { await packagePolicyService.runExternalCallbacks( 'packagePolicyPostCreate', packagePolicy, + soClient, + esClient, requestContext, request ); - expect(callbackA).toHaveBeenCalledWith(packagePolicy, requestContext, request); - expect(callbackB).toHaveBeenCalledWith(packagePolicy, requestContext, request); + expect(callbackA).toHaveBeenCalledWith( + packagePolicy, + soClient, + esClient, + requestContext, + request + ); + expect(callbackB).toHaveBeenCalledWith( + packagePolicy, + soClient, + esClient, + requestContext, + request + ); expect(callbackCallingOrder).toEqual(['a', 'b']); }); }); diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index c7152e2d26a3e..c665d9a6f6f97 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -13,9 +13,9 @@ import { getFlattenedObject } from '@kbn/std'; import type { KibanaRequest, ElasticsearchClient, - RequestHandlerContext, SavedObjectsClientContract, Logger, + RequestHandlerContext, } from '@kbn/core/server'; import { v4 as uuidv4 } from 'uuid'; import { safeLoad } from 'js-yaml'; @@ -133,31 +133,47 @@ class PackagePolicyClientImpl implements PackagePolicyClient { skipUniqueNameVerification?: boolean; overwrite?: boolean; packageInfo?: PackageInfo; - } + }, + context?: RequestHandlerContext, + request?: KibanaRequest ): Promise { const logger = appContextService.getLogger(); - const agentPolicy = await agentPolicyService.get(soClient, packagePolicy.policy_id, true); - if (agentPolicy && packagePolicy.package?.name === FLEET_APM_PACKAGE) { + const enrichedPackagePolicy = await packagePolicyService.runExternalCallbacks( + 'packagePolicyCreate', + packagePolicy, + soClient, + esClient, + context, + request + ); + + const agentPolicy = await agentPolicyService.get( + soClient, + enrichedPackagePolicy.policy_id, + true + ); + + if (agentPolicy && enrichedPackagePolicy.package?.name === FLEET_APM_PACKAGE) { const dataOutput = await getDataOutputForAgentPolicy(soClient, agentPolicy); if (dataOutput.type === outputType.Logstash) { throw new FleetError('You cannot add APM to a policy using a logstash output'); } } - await validateIsNotHostedPolicy(soClient, packagePolicy.policy_id, options?.force); + await validateIsNotHostedPolicy(soClient, enrichedPackagePolicy.policy_id, options?.force); // trailing whitespace causes issues creating API keys - packagePolicy.name = packagePolicy.name.trim(); + enrichedPackagePolicy.name = enrichedPackagePolicy.name.trim(); if (!options?.skipUniqueNameVerification) { const existingPoliciesWithName = await this.list(soClient, { perPage: 1, - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name: "${packagePolicy.name}"`, + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name: "${enrichedPackagePolicy.name}"`, }); // Check that the name does not exist already if (existingPoliciesWithName.items.length > 0) { throw new FleetError( - `An integration policy with the name ${packagePolicy.name} already exists. Please rename it or choose a different name.` + `An integration policy with the name ${enrichedPackagePolicy.name} already exists. Please rename it or choose a different name.` ); } } @@ -165,32 +181,36 @@ class PackagePolicyClientImpl implements PackagePolicyClient { let elasticsearchPrivileges: NonNullable['privileges']; // Add ids to stream const packagePolicyId = options?.id || uuidv4(); - let inputs: PackagePolicyInput[] = packagePolicy.inputs.map((input) => + let inputs: PackagePolicyInput[] = enrichedPackagePolicy.inputs.map((input) => assignStreamIdToInput(packagePolicyId, input) ); // Make sure the associated package is installed - if (packagePolicy.package?.name) { + if (enrichedPackagePolicy.package?.name) { if (!options?.skipEnsureInstalled) { await ensureInstalledPackage({ esClient, spaceId: options?.spaceId || DEFAULT_SPACE_ID, savedObjectsClient: soClient, - pkgName: packagePolicy.package.name, - pkgVersion: packagePolicy.package.version, + pkgName: enrichedPackagePolicy.package.name, + pkgVersion: enrichedPackagePolicy.package.version, force: options?.force, }); } // Handle component template/mappings updates for experimental features, e.g. synthetic source - await handleExperimentalDatastreamFeatureOptIn({ soClient, esClient, packagePolicy }); + await handleExperimentalDatastreamFeatureOptIn({ + soClient, + esClient, + packagePolicy: enrichedPackagePolicy, + }); const pkgInfo = options?.packageInfo ?? (await getPackageInfo({ savedObjectsClient: soClient, - pkgName: packagePolicy.package.name, - pkgVersion: packagePolicy.package.version, + pkgName: enrichedPackagePolicy.package.name, + pkgVersion: enrichedPackagePolicy.package.version, prerelease: true, })); @@ -203,9 +223,9 @@ class PackagePolicyClientImpl implements PackagePolicyClient { ); } } - validatePackagePolicyOrThrow(packagePolicy, pkgInfo); + validatePackagePolicyOrThrow(enrichedPackagePolicy, pkgInfo); - inputs = await _compilePackagePolicyInputs(pkgInfo, packagePolicy.vars || {}, inputs); + inputs = await _compilePackagePolicyInputs(pkgInfo, enrichedPackagePolicy.vars || {}, inputs); elasticsearchPrivileges = pkgInfo.elasticsearch?.privileges; @@ -214,7 +234,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { soClient, esClient, pkgInfo, - packagePolicy, + packagePolicy: enrichedPackagePolicy, force: !!options?.force, logger, }); @@ -225,9 +245,9 @@ class PackagePolicyClientImpl implements PackagePolicyClient { const newSo = await soClient.create( SAVED_OBJECT_TYPE, { - ...packagePolicy, - ...(packagePolicy.package - ? { package: omit(packagePolicy.package, 'experimental_data_stream_features') } + ...enrichedPackagePolicy, + ...(enrichedPackagePolicy.package + ? { package: omit(enrichedPackagePolicy.package, 'experimental_data_stream_features') } : {}), inputs, ...(elasticsearchPrivileges && { elasticsearch: { privileges: elasticsearchPrivileges } }), @@ -242,16 +262,18 @@ class PackagePolicyClientImpl implements PackagePolicyClient { ); if (options?.bumpRevision ?? true) { - await agentPolicyService.bumpRevision(soClient, esClient, packagePolicy.policy_id, { + await agentPolicyService.bumpRevision(soClient, esClient, enrichedPackagePolicy.policy_id, { user: options?.user, }); } - return { - id: newSo.id, - version: newSo.version, - ...newSo.attributes, - }; + const createdPackagePolicy = { id: newSo.id, version: newSo.version, ...newSo.attributes }; + return packagePolicyService.runExternalCallbacks( + 'packagePolicyPostCreate', + createdPackagePolicy, + soClient, + esClient + ); } public async bulkCreate( @@ -494,7 +516,23 @@ class PackagePolicyClientImpl implements PackagePolicyClient { options?: { user?: AuthenticatedUser; force?: boolean; skipUniqueNameVerification?: boolean }, currentVersion?: string ): Promise { - const packagePolicy = { ...packagePolicyUpdate, name: packagePolicyUpdate.name.trim() }; + let enrichedPackagePolicy: UpdatePackagePolicy; + + try { + enrichedPackagePolicy = await packagePolicyService.runExternalCallbacks( + 'packagePolicyUpdate', + packagePolicyUpdate, + soClient, + esClient + ); + } catch (error) { + const logger = appContextService.getLogger(); + logger.error(`An error occurred executing "packagePolicyUpdate" callback: ${error}`); + logger.error(error); + enrichedPackagePolicy = packagePolicyUpdate; + } + + const packagePolicy = { ...enrichedPackagePolicy, name: enrichedPackagePolicy.name.trim() }; const oldPackagePolicy = await this.get(soClient, id); const { version, ...restOfPackagePolicy } = packagePolicy; @@ -505,7 +543,11 @@ class PackagePolicyClientImpl implements PackagePolicyClient { throw new Error('Package policy not found'); } - if (!options?.skipUniqueNameVerification) { + if ( + packagePolicy.name && + packagePolicy.name !== oldPackagePolicy.name && + !options?.skipUniqueNameVerification + ) { // Check that the name does not exist already but exclude the current package policy const existingPoliciesWithName = await this.list(soClient, { perPage: SO_SEARCH_LIMIT, @@ -710,15 +752,35 @@ class PackagePolicyClientImpl implements PackagePolicyClient { soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, ids: string[], - options?: { user?: AuthenticatedUser; skipUnassignFromAgentPolicies?: boolean; force?: boolean } + options?: { + user?: AuthenticatedUser; + skipUnassignFromAgentPolicies?: boolean; + force?: boolean; + }, + context?: RequestHandlerContext, + request?: KibanaRequest ): Promise { const result: PostDeletePackagePoliciesResponse = []; + const logger = appContextService.getLogger(); const packagePolicies = await this.getByIDs(soClient, ids, { ignoreMissing: true }); if (!packagePolicies) { return []; } + try { + await packagePolicyService.runDeleteExternalCallbacks( + packagePolicies, + soClient, + esClient, + context, + request + ); + } catch (error) { + logger.error(`An error occurred executing "packagePolicyDelete" callback: ${error}`); + logger.error(error); + } + const uniqueAgentPolicyIds = [ ...new Set(packagePolicies.map((packagePolicy) => packagePolicy.policy_id)), ]; @@ -819,6 +881,19 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } } + try { + await packagePolicyService.runPostDeleteExternalCallbacks( + result, + soClient, + esClient, + context, + request + ); + } catch (error) { + logger.error(`An error occurred executing "packagePolicyPostDelete" callback: ${error}`); + logger.error(error); + } + return result; } @@ -1231,9 +1306,13 @@ class PackagePolicyClientImpl implements PackagePolicyClient { ? PostDeletePackagePoliciesResponse : A extends 'packagePolicyPostCreate' ? PackagePolicy - : NewPackagePolicy, - context: RequestHandlerContext, - request: KibanaRequest + : A extends 'packagePolicyCreate' + ? NewPackagePolicy + : never, + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest ): Promise< A extends 'packagePolicyDelete' ? void @@ -1241,7 +1320,9 @@ class PackagePolicyClientImpl implements PackagePolicyClient { ? void : A extends 'packagePolicyPostCreate' ? PackagePolicy - : NewPackagePolicy + : A extends 'packagePolicyCreate' + ? NewPackagePolicy + : never >; public async runExternalCallbacks( externalCallbackType: ExternalCallback[0], @@ -1250,53 +1331,95 @@ class PackagePolicyClientImpl implements PackagePolicyClient { | NewPackagePolicy | PostDeletePackagePoliciesResponse | DeletePackagePoliciesResponse, - context: RequestHandlerContext, - request: KibanaRequest + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest ): Promise { - if (externalCallbackType === 'packagePolicyPostDelete') { - return await this.runPostDeleteExternalCallbacks( - packagePolicy as PostDeletePackagePoliciesResponse - ); - } else if (externalCallbackType === 'packagePolicyDelete') { - return await this.runDeleteExternalCallbacks(packagePolicy as DeletePackagePoliciesResponse); - } else { - if (!Array.isArray(packagePolicy)) { - let newData = packagePolicy; - const externalCallbacks = appContextService.getExternalCallbacks(externalCallbackType); - if (externalCallbacks && externalCallbacks.size > 0) { - let updatedNewData = newData; - for (const callback of externalCallbacks) { - let result; - if (externalCallbackType === 'packagePolicyPostCreate') { - result = await (callback as PostPackagePolicyPostCreateCallback)( - updatedNewData as PackagePolicy, - context, - request - ); - updatedNewData = PackagePolicySchema.validate(result); - } else { - result = await (callback as PostPackagePolicyCreateCallback)( - updatedNewData as NewPackagePolicy, - context, - request - ); - } - if (externalCallbackType === 'packagePolicyCreate') { - updatedNewData = NewPackagePolicySchema.validate(result); - } else if (externalCallbackType === 'packagePolicyUpdate') { - updatedNewData = UpdatePackagePolicySchema.validate(result); + const logger = appContextService.getLogger(); + const numberOfCallbacks = appContextService.getExternalCallbacks(externalCallbackType)?.size; + logger.debug(`Running ${numberOfCallbacks} external callbacks for ${externalCallbackType}`); + try { + if (externalCallbackType === 'packagePolicyPostDelete') { + return await this.runPostDeleteExternalCallbacks( + packagePolicy as PostDeletePackagePoliciesResponse, + soClient, + esClient, + context, + request + ); + } else if (externalCallbackType === 'packagePolicyDelete') { + return await this.runDeleteExternalCallbacks( + packagePolicy as DeletePackagePoliciesResponse, + soClient, + esClient + ); + } else { + if (!Array.isArray(packagePolicy)) { + let newData = packagePolicy; + const externalCallbacks = appContextService.getExternalCallbacks(externalCallbackType); + if (externalCallbacks && externalCallbacks.size > 0) { + let updatedNewData = newData; + for (const callback of externalCallbacks) { + let result; + if (externalCallbackType === 'packagePolicyPostCreate') { + result = await (callback as PostPackagePolicyPostCreateCallback)( + updatedNewData as PackagePolicy, + soClient, + esClient, + context, + request + ); + updatedNewData = PackagePolicySchema.validate(result); + } else { + result = await (callback as PostPackagePolicyCreateCallback)( + updatedNewData as NewPackagePolicy, + soClient, + esClient, + context, + request + ); + } + + if (externalCallbackType === 'packagePolicyCreate') { + updatedNewData = NewPackagePolicySchema.validate(result); + } else if (externalCallbackType === 'packagePolicyUpdate') { + const omitted = { + ...omit(result, [ + 'id', + 'version', + 'revision', + 'updated_at', + 'updated_by', + 'created_at', + 'created_by', + 'elasticsearch', + ]), + inputs: result.inputs.map((input) => omit(input, ['compiled_input'])), + }; + + updatedNewData = UpdatePackagePolicySchema.validate(omitted); + } } - } - newData = updatedNewData; + newData = updatedNewData; + } + return newData; } - return newData; } + } catch (error) { + logger.error(`Error running external callbacks for ${externalCallbackType}:`); + logger.error(error); + throw error; } } public async runPostDeleteExternalCallbacks( - deletedPackagePolicies: PostDeletePackagePoliciesResponse + deletedPackagePolicies: PostDeletePackagePoliciesResponse, + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest ): Promise { const externalCallbacks = appContextService.getExternalCallbacks('packagePolicyPostDelete'); const errorsThrown: Error[] = []; @@ -1306,7 +1429,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { // Failures from an external callback should not prevent other external callbacks from being // executed. Errors (if any) will be collected and `throw`n after processing the entire set try { - await callback(deletedPackagePolicies); + await callback(deletedPackagePolicies, soClient, esClient, context, request); } catch (error) { errorsThrown.push(error); } @@ -1322,7 +1445,9 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } public async runDeleteExternalCallbacks( - deletedPackagePolices: DeletePackagePoliciesResponse + deletedPackagePolices: DeletePackagePoliciesResponse, + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient ): Promise { const externalCallbacks = appContextService.getExternalCallbacks('packagePolicyDelete'); const errorsThrown: Error[] = []; @@ -1332,7 +1457,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { // Failures from an external callback should not prevent other external callbacks from being // executed. Errors (if any) will be collected and `throw`n after processing the entire set try { - await callback(deletedPackagePolices); + await callback(deletedPackagePolices, soClient, esClient); } catch (error) { errorsThrown.push(error); } @@ -1398,7 +1523,9 @@ class PackagePolicyClientWithAuthz extends PackagePolicyClientImpl { skipUniqueNameVerification?: boolean; overwrite?: boolean; packageInfo?: PackageInfo; - } + }, + context?: RequestHandlerContext, + request?: KibanaRequest ): Promise { await this.#runPreflight({ fleetAuthz: { @@ -1406,7 +1533,7 @@ class PackagePolicyClientWithAuthz extends PackagePolicyClientImpl { }, }); - return super.create(soClient, esClient, packagePolicy, options); + return super.create(soClient, esClient, packagePolicy, options, context, request); } } diff --git a/x-pack/plugins/fleet/server/services/package_policy_service.ts b/x-pack/plugins/fleet/server/services/package_policy_service.ts index 790622a6ae6b4..5ca2a107c0281 100644 --- a/x-pack/plugins/fleet/server/services/package_policy_service.ts +++ b/x-pack/plugins/fleet/server/services/package_policy_service.ts @@ -5,12 +5,8 @@ * 2.0. */ -import type { KibanaRequest, Logger } from '@kbn/core/server'; -import type { - ElasticsearchClient, - RequestHandlerContext, - SavedObjectsClientContract, -} from '@kbn/core/server'; +import type { KibanaRequest, Logger, RequestHandlerContext } from '@kbn/core/server'; +import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import type { AuthenticatedUser } from '@kbn/security-plugin/server'; import type { @@ -50,7 +46,9 @@ export interface PackagePolicyClient { skipUniqueNameVerification?: boolean; overwrite?: boolean; packageInfo?: PackageInfo; - } + }, + context?: RequestHandlerContext, + request?: KibanaRequest ): Promise; bulkCreate( @@ -108,7 +106,13 @@ export interface PackagePolicyClient { soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, ids: string[], - options?: { user?: AuthenticatedUser; skipUnassignFromAgentPolicies?: boolean; force?: boolean } + options?: { + user?: AuthenticatedUser; + skipUnassignFromAgentPolicies?: boolean; + force?: boolean; + }, + context?: RequestHandlerContext, + request?: KibanaRequest ): Promise; upgrade( @@ -146,9 +150,13 @@ export interface PackagePolicyClient { ? PostDeletePackagePoliciesResponse : A extends 'packagePolicyPostCreate' ? PackagePolicy + : A extends 'packagePolicyUpdate' + ? UpdatePackagePolicy : NewPackagePolicy, - context: RequestHandlerContext, - request: KibanaRequest + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest ): Promise< A extends 'packagePolicyDelete' ? void @@ -156,13 +164,25 @@ export interface PackagePolicyClient { ? void : A extends 'packagePolicyPostCreate' ? PackagePolicy + : A extends 'packagePolicyUpdate' + ? UpdatePackagePolicy : NewPackagePolicy >; - runDeleteExternalCallbacks(deletedPackagePolicies: DeletePackagePoliciesResponse): Promise; + runDeleteExternalCallbacks( + deletedPackagePolicies: DeletePackagePoliciesResponse, + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest + ): Promise; runPostDeleteExternalCallbacks( - deletedPackagePolicies: PostDeletePackagePoliciesResponse + deletedPackagePolicies: PostDeletePackagePoliciesResponse, + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest ): Promise; getUpgradePackagePolicyInfo( diff --git a/x-pack/plugins/fleet/server/services/saved_object.test.ts b/x-pack/plugins/fleet/server/services/saved_object.test.ts index d2683caf9c725..4dd99a3db2d2b 100644 --- a/x-pack/plugins/fleet/server/services/saved_object.test.ts +++ b/x-pack/plugins/fleet/server/services/saved_object.test.ts @@ -18,7 +18,7 @@ describe('Saved object service', () => { it('should escape quotes', () => { const res = escapeSearchQueryPhrase('test1"test2'); - expect(res).toEqual(`"test1\"test2"`); + expect(res).toEqual(`"test1\\"test2"`); }); }); }); diff --git a/x-pack/plugins/fleet/server/services/saved_object.ts b/x-pack/plugins/fleet/server/services/saved_object.ts index 6a45061d89334..2a1f5e216fb2f 100644 --- a/x-pack/plugins/fleet/server/services/saved_object.ts +++ b/x-pack/plugins/fleet/server/services/saved_object.ts @@ -16,7 +16,7 @@ import type { ListWithKuery } from '../types'; * @param val */ export function escapeSearchQueryPhrase(val: string): string { - return `"${val.replace(/["]/g, '"')}"`; + return `"${val.replace(/["]/g, '\\"')}"`; } // Adds `.attributes` to any kuery strings that are missing it, this comes from diff --git a/x-pack/plugins/fleet/server/services/security/security.test.ts b/x-pack/plugins/fleet/server/services/security/security.test.ts index f99a708504d6c..d40e2a40514c8 100644 --- a/x-pack/plugins/fleet/server/services/security/security.test.ts +++ b/x-pack/plugins/fleet/server/services/security/security.test.ts @@ -85,6 +85,9 @@ describe('When using calculateRouteAuthz()', () => { writeFileOperations: { executePackageAction: false, }, + writeExecuteOperations: { + executePackageAction: false, + }, }, }, diff --git a/x-pack/plugins/fleet/server/types/extensions.ts b/x-pack/plugins/fleet/server/types/extensions.ts index 6d3ebc32b523f..ca5a0d84c958e 100644 --- a/x-pack/plugins/fleet/server/types/extensions.ts +++ b/x-pack/plugins/fleet/server/types/extensions.ts @@ -6,6 +6,7 @@ */ import type { KibanaRequest, RequestHandlerContext } from '@kbn/core/server'; +import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import type { DeepReadonly } from 'utility-types'; @@ -18,29 +19,43 @@ import type { } from '../../common/types'; export type PostPackagePolicyDeleteCallback = ( - packagePolicies: DeletePackagePoliciesResponse + packagePolicies: DeletePackagePoliciesResponse, + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest ) => Promise; export type PostPackagePolicyPostDeleteCallback = ( - deletedPackagePolicies: DeepReadonly + deletedPackagePolicies: DeepReadonly, + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest ) => Promise; export type PostPackagePolicyCreateCallback = ( newPackagePolicy: NewPackagePolicy, - context: RequestHandlerContext, - request: KibanaRequest + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest ) => Promise; export type PostPackagePolicyPostCreateCallback = ( packagePolicy: PackagePolicy, - context: RequestHandlerContext, - request: KibanaRequest + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest ) => Promise; export type PutPackagePolicyUpdateCallback = ( updatePackagePolicy: UpdatePackagePolicy, - context: RequestHandlerContext, - request: KibanaRequest + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + context?: RequestHandlerContext, + request?: KibanaRequest ) => Promise; export type ExternalCallbackCreate = ['packagePolicyCreate', PostPackagePolicyCreateCallback]; diff --git a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts index 96227b2c33bfe..92b0f098ae19d 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts @@ -22,6 +22,7 @@ export const GetAgentsRequestSchema = { showInactive: schema.boolean({ defaultValue: false }), withMetrics: schema.boolean({ defaultValue: false }), showUpgradeable: schema.boolean({ defaultValue: false }), + getStatusSummary: schema.boolean({ defaultValue: false }), sortField: schema.maybe(schema.string()), sortOrder: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), }, diff --git a/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts b/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts index b926cdb6d034a..89717134002ec 100644 --- a/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts +++ b/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts @@ -19,8 +19,7 @@ import { createNonDataStreamIndex, } from '../client_integration/home/data_streams_tab.helpers'; -// FLAKY: https://github.com/elastic/kibana/issues/128836 -describe.skip('A11y Indices tab', () => { +describe('A11y Indices tab', () => { let testBed: IndicesTestBed; let httpSetup: ReturnType['httpSetup']; let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; @@ -54,7 +53,8 @@ describe.skip('A11y Indices tab', () => { await expectToBeAccessible(component); }); - describe('index details flyout', () => { + // FLAKY: https://github.com/elastic/kibana/issues/128836 + describe.skip('index details flyout', () => { beforeEach(async () => { httpRequestsMockHelpers.setLoadIndicesResponse([ createNonDataStreamIndex('non-data-stream-test-index'), diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx index 44b37a0f9cc2a..4ddfa550f5d09 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx @@ -44,7 +44,11 @@ export const SourceFieldSection = () => {

- + ; metric: string; + customMetrics: never; + equation: never; + label: never; } export interface CountMetricExpressionParams extends BaseMetricExpressionParams { aggType: Aggregators.COUNT; metric: never; + customMetrics: never; + equation: never; + label: never; } -export type MetricExpressionParams = NonCountMetricExpressionParams | CountMetricExpressionParams; +export type CustomMetricAggTypes = Exclude< + Aggregators, + Aggregators.CUSTOM | Aggregators.RATE | Aggregators.P95 | Aggregators.P99 +>; + +export interface MetricExpressionCustomMetric { + name: string; + aggType: CustomMetricAggTypes; + field?: string; + filter?: string; +} + +export interface CustomMetricExpressionParams extends BaseMetricExpressionParams { + aggType: Aggregators.CUSTOM; + metric: never; + customMetrics: MetricExpressionCustomMetric[]; + equation?: string; + label?: string; +} + +export type MetricExpressionParams = + | NonCountMetricExpressionParams + | CountMetricExpressionParams + | CustomMetricExpressionParams; export const QUERY_INVALID: unique symbol = Symbol('QUERY_INVALID'); diff --git a/x-pack/plugins/infra/common/formatters/telemetry_time_range.ts b/x-pack/plugins/infra/common/formatters/telemetry_time_range.ts new file mode 100644 index 0000000000000..1befe71289ae4 --- /dev/null +++ b/x-pack/plugins/infra/common/formatters/telemetry_time_range.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. + */ + +const ONE_MINUTE = 60000; +const FIVE_MINUTES = ONE_MINUTE * 5; +const TEN_MINUTES = ONE_MINUTE * 10; +const THIRTY_MINUTES = ONE_MINUTE * 30; +const ONE_HOUR = ONE_MINUTE * 60; +const TWO_HOURS = ONE_HOUR * 2; +const EIGHT_HOURS = ONE_HOUR * 8; +const TWELVE_HOURS = ONE_HOUR * 12; +const ONE_DAY = ONE_HOUR * 24; +const TWO_DAYS = ONE_DAY * 2; +const SEVEN_DAYS = ONE_DAY * 7; +const FOURTEEN_DAYS = ONE_DAY * 14; +const THIRTY_DAYS = ONE_DAY * 30; +const SIXTY_DAYS = ONE_DAY * 60; +const NINETY_DAYS = ONE_DAY * 90; +const HALF_YEAR = ONE_DAY * 180; +const ONE_YEAR = ONE_DAY * 365; + +export const telemetryTimeRangeFormatter = (ms: number): string => { + if (ms < ONE_MINUTE) return '1. Less than 1 minute'; + if (ms >= ONE_MINUTE && ms < FIVE_MINUTES) return '2. 1-5 minutes'; + if (ms >= FIVE_MINUTES && ms < TEN_MINUTES) return '3. 5-10 minutes'; + if (ms >= TEN_MINUTES && ms < THIRTY_MINUTES) return '4. 10-30 minutes'; + if (ms >= THIRTY_MINUTES && ms < ONE_HOUR) return '5. 30-60 minutes'; + if (ms >= ONE_HOUR && ms < TWO_HOURS) return '6. 1-2 hours'; + if (ms >= TWO_HOURS && ms < EIGHT_HOURS) return '7. 2-8 hours'; + if (ms >= EIGHT_HOURS && ms < TWELVE_HOURS) return '8. 8-12 hours'; + if (ms >= TWELVE_HOURS && ms < ONE_DAY) return '9. 12-24 hours'; + if (ms >= ONE_DAY && ms < TWO_DAYS) return '10. 1-2 days'; + if (ms >= TWO_DAYS && ms < SEVEN_DAYS) return '11. 2-7 days'; + if (ms >= SEVEN_DAYS && ms < FOURTEEN_DAYS) return '12. 7-14 days'; + if (ms >= FOURTEEN_DAYS && ms < THIRTY_DAYS) return '13. 14-30 days'; + if (ms >= THIRTY_DAYS && ms < SIXTY_DAYS) return '14. 30-60 days'; + if (ms >= SIXTY_DAYS && ms < NINETY_DAYS) return '15. 60-90 days'; + if (ms >= NINETY_DAYS && ms < HALF_YEAR) return '16. 90-180 days'; + if (ms >= HALF_YEAR && ms < ONE_YEAR) return '17. 180-365 days'; + return '18. More than 1 year'; +}; diff --git a/x-pack/plugins/infra/common/http_api/metrics_explorer.ts b/x-pack/plugins/infra/common/http_api/metrics_explorer.ts index de00d521126e3..d735e398e6661 100644 --- a/x-pack/plugins/infra/common/http_api/metrics_explorer.ts +++ b/x-pack/plugins/infra/common/http_api/metrics_explorer.ts @@ -6,6 +6,7 @@ */ import * as rt from 'io-ts'; +import { xor } from 'lodash'; export const METRIC_EXPLORER_AGGREGATIONS = [ 'avg', @@ -17,8 +18,11 @@ export const METRIC_EXPLORER_AGGREGATIONS = [ 'sum', 'p95', 'p99', + 'custom', ] as const; +export const OMITTED_AGGREGATIONS_FOR_CUSTOM_METRICS = ['custom', 'rate', 'p95', 'p99']; + type MetricExplorerAggregations = typeof METRIC_EXPLORER_AGGREGATIONS[number]; const metricsExplorerAggregationKeys = METRIC_EXPLORER_AGGREGATIONS.reduce< @@ -27,12 +31,42 @@ const metricsExplorerAggregationKeys = METRIC_EXPLORER_AGGREGATIONS.reduce< export const metricsExplorerAggregationRT = rt.keyof(metricsExplorerAggregationKeys); +export type MetricExplorerCustomMetricAggregations = Exclude< + MetricsExplorerAggregation, + 'custom' | 'rate' | 'p95' | 'p99' +>; +const metricsExplorerCustomMetricAggregationKeys = xor( + METRIC_EXPLORER_AGGREGATIONS, + OMITTED_AGGREGATIONS_FOR_CUSTOM_METRICS +).reduce>( + (acc, agg) => ({ ...acc, [agg]: null }), + {} as Record +); +export const metricsExplorerCustomMetricAggregationRT = rt.keyof( + metricsExplorerCustomMetricAggregationKeys +); + export const metricsExplorerMetricRequiredFieldsRT = rt.type({ aggregation: metricsExplorerAggregationRT, }); +export const metricsExplorerCustomMetricRT = rt.intersection([ + rt.type({ + name: rt.string, + aggregation: metricsExplorerCustomMetricAggregationRT, + }), + rt.partial({ + field: rt.string, + filter: rt.string, + }), +]); + +export type MetricsExplorerCustomMetric = rt.TypeOf; + export const metricsExplorerMetricOptionalFieldsRT = rt.partial({ field: rt.union([rt.string, rt.undefined]), + custom_metrics: rt.array(metricsExplorerCustomMetricRT), + equation: rt.string, }); export const metricsExplorerMetricRT = rt.intersection([ diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx new file mode 100644 index 0000000000000..ce30172a74f15 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx @@ -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 { Meta, Story } from '@storybook/react/types-6-0'; +import React, { useCallback, useEffect, useState } from 'react'; +import { TimeUnitChar } from '@kbn/observability-plugin/common'; +import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; +import { + Aggregators, + Comparator, + MetricExpressionParams, +} from '../../../../../common/alerting/metrics'; +import { decorateWithGlobalStorybookThemeProviders } from '../../../../test_utils/use_global_storybook_theme'; +import { CustomEquationEditor, CustomEquationEditorProps } from './custom_equation_editor'; +import { aggregationType } from '../expression_row'; +import { MetricExpression } from '../../types'; +import { validateMetricThreshold } from '../validation'; + +export default { + title: 'infra/alerting/CustomEquationEditor', + decorators: [ + (wrappedStory) =>

, + decorateWithGlobalStorybookThemeProviders, + ], + parameters: { + layout: 'padded', + }, + argTypes: { + onChange: { action: 'changed' }, + }, +} as Meta; + +const CustomEquationEditorTemplate: Story = (args) => { + const [expression, setExpression] = useState(args.expression); + const [errors, setErrors] = useState(args.errors); + + const handleExpressionChange = useCallback( + (exp: MetricExpression) => { + setExpression(exp); + args.onChange(exp); + return exp; + }, + [args] + ); + + useEffect(() => { + const validationObject = validateMetricThreshold({ + criteria: [expression as MetricExpressionParams], + }); + setErrors(validationObject.errors[0]); + }, [expression]); + + return ( + + ); +}; + +export const CustomEquationEditorDefault = CustomEquationEditorTemplate.bind({}); +export const CustomEquationEditorWithEquationErrors = CustomEquationEditorTemplate.bind({}); +export const CustomEquationEditorWithFieldError = CustomEquationEditorTemplate.bind({}); + +const BASE_ARGS = { + expression: { + aggType: Aggregators.CUSTOM, + timeSize: 1, + timeUnit: 'm' as TimeUnitChar, + threshold: [1], + comparator: Comparator.GT, + }, + fields: [ + { name: 'system.cpu.user.pct', normalizedType: 'number' }, + { name: 'system.cpu.system.pct', normalizedType: 'number' }, + { name: 'system.cpu.cores', normalizedType: 'number' }, + ], + aggregationTypes: aggregationType, +}; + +CustomEquationEditorDefault.args = { + ...BASE_ARGS, + errors: {}, +}; + +CustomEquationEditorWithEquationErrors.args = { + ...BASE_ARGS, + expression: { + ...BASE_ARGS.expression, + equation: 'Math.round(A / B)', + customMetrics: [ + { name: 'A', aggType: Aggregators.AVERAGE, field: 'system.cpu.user.pct' }, + { name: 'B', aggType: Aggregators.MAX, field: 'system.cpu.cores' }, + ], + }, + errors: { + equation: + 'The equation field only supports the following characters: A-Z, +, -, /, *, (, ), ?, !, &, :, |, >, <, =', + }, +}; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx new file mode 100644 index 0000000000000..866e818688533 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx @@ -0,0 +1,214 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { + EuiFieldText, + EuiFormRow, + EuiFlexItem, + EuiFlexGroup, + EuiButtonEmpty, + EuiSpacer, +} from '@elastic/eui'; +import React, { useState, useCallback, useMemo } from 'react'; +import { omit, range, first, xor, debounce } from 'lodash'; +import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { OMITTED_AGGREGATIONS_FOR_CUSTOM_METRICS } from '../../../../../common/http_api'; +import { + Aggregators, + CustomMetricAggTypes, + MetricExpressionCustomMetric, +} from '../../../../../common/alerting/metrics'; +import { MetricExpression } from '../../types'; +import { CustomMetrics, AggregationTypes, NormalizedFields } from './types'; +import { MetricRowWithAgg } from './metric_row_with_agg'; +import { MetricRowWithCount } from './metric_row_with_count'; +import { + CUSTOM_EQUATION, + EQUATION_HELP_MESSAGE, + LABEL_HELP_MESSAGE, + LABEL_LABEL, +} from '../../i18n_strings'; + +export interface CustomEquationEditorProps { + onChange: (expression: MetricExpression) => void; + expression: MetricExpression; + fields: NormalizedFields; + aggregationTypes: AggregationTypes; + errors: IErrorObject; +} + +const NEW_METRIC = { name: 'A', aggType: Aggregators.AVERAGE as CustomMetricAggTypes }; +const MAX_VARIABLES = 26; +const CHAR_CODE_FOR_A = 65; +const CHAR_CODE_FOR_Z = CHAR_CODE_FOR_A + MAX_VARIABLES; +const VAR_NAMES = range(CHAR_CODE_FOR_A, CHAR_CODE_FOR_Z).map((c) => String.fromCharCode(c)); + +export const CustomEquationEditor = ({ + onChange, + expression, + fields, + aggregationTypes, + errors, +}: CustomEquationEditorProps) => { + const [customMetrics, setCustomMetrics] = useState( + expression?.customMetrics ?? [NEW_METRIC] + ); + const [label, setLabel] = useState(expression?.label || undefined); + const [equation, setEquation] = useState(expression?.equation || undefined); + const debouncedOnChange = useMemo(() => debounce(onChange, 500), [onChange]); + + const handleAddNewRow = useCallback(() => { + setCustomMetrics((previous) => { + const currentVars = previous?.map((m) => m.name) ?? []; + const name = first(xor(VAR_NAMES, currentVars))!; + const nextMetrics = [...(previous || []), { ...NEW_METRIC, name }]; + debouncedOnChange({ ...expression, customMetrics: nextMetrics, equation, label }); + return nextMetrics; + }); + }, [debouncedOnChange, equation, expression, label]); + + const handleDelete = useCallback( + (name: string) => { + setCustomMetrics((previous) => { + const nextMetrics = previous?.filter((row) => row.name !== name) ?? [NEW_METRIC]; + const finalMetrics = (nextMetrics.length && nextMetrics) || [NEW_METRIC]; + debouncedOnChange({ ...expression, customMetrics: finalMetrics, equation, label }); + return finalMetrics; + }); + }, + [equation, expression, debouncedOnChange, label] + ); + + const handleChange = useCallback( + (metric: MetricExpressionCustomMetric) => { + setCustomMetrics((previous) => { + const nextMetrics = previous?.map((m) => (m.name === metric.name ? metric : m)); + debouncedOnChange({ ...expression, customMetrics: nextMetrics, equation, label }); + return nextMetrics; + }); + }, + [equation, expression, debouncedOnChange, label] + ); + + const handleEquationChange = useCallback( + (e: React.ChangeEvent) => { + setEquation(e.target.value); + debouncedOnChange({ ...expression, customMetrics, equation: e.target.value, label }); + }, + [debouncedOnChange, expression, customMetrics, label] + ); + + const handleLabelChange = useCallback( + (e: React.ChangeEvent) => { + setLabel(e.target.value); + debouncedOnChange({ ...expression, customMetrics, equation, label: e.target.value }); + }, + [debouncedOnChange, expression, customMetrics, equation] + ); + + const disableAdd = customMetrics?.length === MAX_VARIABLES; + const disableDelete = customMetrics?.length === 1; + + const filteredAggregationTypes = omit(aggregationTypes, OMITTED_AGGREGATIONS_FOR_CUSTOM_METRICS); + + const metricRows = customMetrics?.map((row) => { + if (row.aggType === Aggregators.COUNT) { + return ( + + ); + } + return ( + + ); + }); + + const placeholder = useMemo(() => { + return customMetrics?.map((row) => row.name).join(' + '); + }, [customMetrics]); + + return ( +
+ + {metricRows} + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/index.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/index.tsx new file mode 100644 index 0000000000000..2c885581b3989 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/index.tsx @@ -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 { CustomEquationEditor } from './custom_equation_editor'; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_controls.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_controls.tsx new file mode 100644 index 0000000000000..3c8efe19a01d7 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_controls.tsx @@ -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 React from 'react'; +import { EuiFlexItem, EuiButtonIcon } from '@elastic/eui'; +import { DELETE_LABEL } from '../../i18n_strings'; + +interface MetricRowControlProps { + onDelete: () => void; + disableDelete: boolean; +} + +export const MetricRowControls = ({ onDelete, disableDelete }: MetricRowControlProps) => { + return ( + <> + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_with_agg.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_with_agg.tsx new file mode 100644 index 0000000000000..1e80f743d7967 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_with_agg.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 { + EuiFormRow, + EuiHorizontalRule, + EuiFlexItem, + EuiFlexGroup, + EuiSelect, + EuiComboBox, + EuiComboBoxOptionOption, +} from '@elastic/eui'; +import React, { useMemo, useCallback } from 'react'; +import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { Aggregators, CustomMetricAggTypes } from '../../../../../common/alerting/metrics'; +import { MetricRowControls } from './metric_row_controls'; +import { NormalizedFields, MetricRowBaseProps } from './types'; + +interface MetricRowWithAggProps extends MetricRowBaseProps { + aggType?: CustomMetricAggTypes; + field?: string; + fields: NormalizedFields; +} + +export const MetricRowWithAgg = ({ + name, + aggType = Aggregators.AVERAGE, + field, + onDelete, + disableDelete, + fields, + aggregationTypes, + onChange, + errors, +}: MetricRowWithAggProps) => { + const handleDelete = useCallback(() => { + onDelete(name); + }, [name, onDelete]); + + const fieldOptions = useMemo( + () => + fields.reduce((acc, fieldValue) => { + if ( + aggType && + aggregationTypes[aggType].validNormalizedTypes.includes(fieldValue.normalizedType) + ) { + acc.push({ label: fieldValue.name }); + } + return acc; + }, [] as Array<{ label: string }>), + [fields, aggregationTypes, aggType] + ); + + const aggOptions = useMemo( + () => + Object.values(aggregationTypes).map((a) => ({ + text: a.text, + value: a.value, + })), + [aggregationTypes] + ); + + const handleFieldChange = useCallback( + (selectedOptions: EuiComboBoxOptionOption[]) => { + onChange({ + name, + field: (selectedOptions.length && selectedOptions[0].label) || undefined, + aggType, + }); + }, + [name, aggType, onChange] + ); + + const handleAggChange = useCallback( + (el: React.ChangeEvent) => { + onChange({ + name, + field, + aggType: el.target.value as CustomMetricAggTypes, + }); + }, + [name, field, onChange] + ); + + const isAggInvalid = get(errors, ['customMetrics', name, 'aggType']) != null; + const isFieldInvalid = get(errors, ['customMetrics', name, 'field']) != null || !field; + + return ( + <> + + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_with_count.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_with_count.tsx new file mode 100644 index 0000000000000..43ac682830bcd --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_with_count.tsx @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { + EuiFieldText, + EuiFormRow, + EuiHorizontalRule, + EuiFlexItem, + EuiFlexGroup, + EuiSelect, +} from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { Aggregators, CustomMetricAggTypes } from '../../../../../common/alerting/metrics'; +import { MetricRowControls } from './metric_row_controls'; +import { MetricRowBaseProps } from './types'; + +interface MetricRowWithCountProps extends MetricRowBaseProps { + agg?: Aggregators; + filter?: string; +} + +export const MetricRowWithCount = ({ + name, + agg, + filter, + onDelete, + disableDelete, + onChange, + aggregationTypes, +}: MetricRowWithCountProps) => { + const aggOptions = useMemo( + () => + Object.values(aggregationTypes) + .filter((aggType) => aggType.value !== Aggregators.CUSTOM) + .map((aggType) => ({ + text: aggType.text, + value: aggType.value, + })), + [aggregationTypes] + ); + + const handleDelete = useCallback(() => { + onDelete(name); + }, [name, onDelete]); + + const handleAggChange = useCallback( + (el: React.ChangeEvent) => { + onChange({ + name, + filter, + aggType: el.target.value as CustomMetricAggTypes, + }); + }, + [name, filter, onChange] + ); + + const handleFilterChange = useCallback( + (el: React.ChangeEvent) => { + onChange({ + name, + filter: el.target.value, + aggType: agg as CustomMetricAggTypes, + }); + }, + [name, agg, onChange] + ); + + return ( + <> + + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/types.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/types.ts new file mode 100644 index 0000000000000..60069c6bb79d2 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/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 { AggregationType, IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; +import { MetricExpressionCustomMetric } from '../../../../../common/alerting/metrics'; +import { MetricExpression } from '../../types'; + +export type CustomMetrics = MetricExpression['customMetrics']; + +export interface AggregationTypes { + [x: string]: AggregationType; +} + +export interface NormalizedField { + name: string; + normalizedType: string; +} + +export type NormalizedFields = NormalizedField[]; + +export interface MetricRowBaseProps { + name: string; + onAdd: () => void; + onDelete: (name: string) => void; + disableDelete: boolean; + disableAdd: boolean; + onChange: (metric: MetricExpressionCustomMetric) => void; + aggregationTypes: AggregationTypes; + errors: IErrorObject; +} diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx index 87bc52322c7d3..8fe00f5a34c73 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx @@ -17,7 +17,10 @@ import { Color } from '../../../../common/color_palette'; import { MetricsExplorerRow, MetricsExplorerAggregation } from '../../../../common/http_api'; import { MetricExplorerSeriesChart } from '../../../pages/metrics/metrics_explorer/components/series_chart'; import { MetricExpression } from '../types'; -import { MetricsExplorerChartType } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; +import { + MetricsExplorerChartType, + MetricsExplorerOptionsMetric, +} from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; import { createFormatterForMetric } from '../../../pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric'; import { calculateDomain } from '../../../pages/metrics/metrics_explorer/components/helpers/calculate_domain'; import { useMetricsExplorerChartData } from '../hooks/use_metrics_explorer_chart_data'; @@ -32,6 +35,7 @@ import { getChartTheme, } from '../../common/criterion_preview_chart/criterion_preview_chart'; import { ThresholdAnnotations } from '../../common/criterion_preview_chart/threshold_annotations'; +import { CUSTOM_EQUATION } from '../i18n_strings'; interface Props { expression: MetricExpression; @@ -58,11 +62,15 @@ export const ExpressionChart: React.FC = ({ const { uiSettings } = useKibanaContextForPlugin().services; - const metric = { + const metric: MetricsExplorerOptionsMetric = { field: expression.metric, aggregation: expression.aggType as MetricsExplorerAggregation, color: Color.color0, }; + + if (metric.aggregation === 'custom') { + metric.label = expression.label || CUSTOM_EQUATION; + } const isDarkMode = uiSettings?.get('theme:darkMode') || false; const dateFormatter = useMemo(() => { const firstSeries = first(data?.series); @@ -79,10 +87,14 @@ export const ExpressionChart: React.FC = ({ /* eslint-disable-next-line react-hooks/exhaustive-deps */ const yAxisFormater = useCallback(createFormatterForMetric(metric), [expression]); - if (loading || !data) { + if (loading) { return ; } + if (!data) { + return ; + } + const criticalThresholds = expression.threshold.slice().sort(); const warningThresholds = expression.warningThreshold?.slice().sort() ?? []; const thresholds = [...criticalThresholds, ...warningThresholds].sort(); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx index 2204dd16fd46a..14ff5b1e60eed 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -20,16 +20,19 @@ import { omit } from 'lodash'; import React, { useCallback, useMemo, useState } from 'react'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { + AggregationType, builtInComparators, IErrorObject, OfExpression, ThresholdExpression, WhenExpression, } from '@kbn/triggers-actions-ui-plugin/public'; -import { Comparator } from '../../../../common/alerting/metrics'; +import { Aggregators, Comparator } from '../../../../common/alerting/metrics'; import { decimalToPct, pctToDecimal } from '../../../../common/utils/corrected_percent_convert'; import { DerivedIndexPattern } from '../../../containers/metrics_source'; import { AGGREGATION_TYPES, MetricExpression } from '../types'; +import { CustomEquationEditor } from './custom_equation'; +import { CUSTOM_EQUATION } from '../i18n_strings'; const customComparators = { ...builtInComparators, @@ -73,6 +76,7 @@ export const ExpressionRow: React.FC = (props) => { const toggleRowState = useCallback(() => setRowState(!isExpanded), [isExpanded]); const { children, setRuleParams, expression, errors, expressionId, remove, fields, canDelete } = props; + const { aggType = AGGREGATION_TYPES.MAX, metric, @@ -92,7 +96,10 @@ export const ExpressionRow: React.FC = (props) => { setRuleParams(expressionId, { ...expression, aggType: at as MetricExpression['aggType'], - metric: at === 'count' ? undefined : expression.metric, + metric: ['custom', 'count'].includes(at) ? undefined : expression.metric, + customMetrics: at === 'custom' ? expression.customMetrics : undefined, + equation: at === 'custom' ? expression.equation : undefined, + label: at === 'custom' ? expression.label : undefined, }); }, [expressionId, expression, setRuleParams] @@ -166,6 +173,13 @@ export const ExpressionRow: React.FC = (props) => { expressionId, ]); + const handleCustomMetricChange = useCallback( + (exp) => { + setRuleParams(expressionId, exp); + }, + [expressionId, setRuleParams] + ); + const criticalThresholdExpression = ( = (props) => { /> ); + const normalizedFields = fields.map((f) => ({ + normalizedType: f.type, + name: f.name, + })); + return ( <> @@ -201,7 +220,7 @@ export const ExpressionRow: React.FC = (props) => { />
- + = (props) => { onChangeSelectedAggType={updateAggType} /> - {aggType !== 'count' && ( + {!['count', 'custom'].includes(aggType) && ( ({ - normalizedType: f.type, - name: f.name, - }))} + fields={normalizedFields} aggType={aggType} errors={errors} onChangeSelectedAggField={updateMetric} @@ -245,6 +261,25 @@ export const ExpressionRow: React.FC = (props) => { )} {!displayWarningThreshold && criticalThresholdExpression} + {!displayWarningThreshold && ( + <> + + + + + + + + )} {displayWarningThreshold && ( <> @@ -280,24 +315,19 @@ export const ExpressionRow: React.FC = (props) => { )} - {!displayWarningThreshold && ( + {aggType === Aggregators.CUSTOM && ( <> - {' '} - + - - - + + )} @@ -358,7 +388,7 @@ const ThresholdElement: React.FC<{ ); }; -export const aggregationType: { [key: string]: any } = { +export const aggregationType: { [key: string]: AggregationType } = { avg: { text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.avg', { defaultMessage: 'Average', @@ -431,4 +461,10 @@ export const aggregationType: { [key: string]: any } = { value: AGGREGATION_TYPES.P99, validNormalizedTypes: ['number', 'histogram'], }, + custom: { + text: CUSTOM_EQUATION, + fieldRequired: false, + value: AGGREGATION_TYPES.CUSTOM, + validNormalizedTypes: ['number', 'histogram'], + }, }; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.test.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.test.ts new file mode 100644 index 0000000000000..ac1b545e4a302 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.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 { EQUATION_REGEX } from './validation'; + +describe('Metric Threshold Validation', () => { + describe('valid equations', () => { + const validExpression = [ + '(A + B) / 100', + '(A - B) * 100', + 'A > 1 ? A : B', + 'A <= 1 ? A : B', + 'A && B || C', + ]; + validExpression.forEach((exp) => { + it(exp, () => { + expect(exp.match(EQUATION_REGEX)).toBeFalsy(); + }); + }); + }); + describe('invalid equations', () => { + const validExpression = ['Math.round(A + B) / 100', '(A^2 - B) * 100']; + validExpression.forEach((exp) => { + it(exp, () => { + expect(exp.match(EQUATION_REGEX)).toBeTruthy(); + }); + }); + }); +}); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx index bc75b2512fbc1..b3d4d423c58b5 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx @@ -7,13 +7,24 @@ import { i18n } from '@kbn/i18n'; import { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; +import { isEmpty } from 'lodash'; import { + Aggregators, Comparator, + CustomMetricExpressionParams, FilterQuery, MetricExpressionParams, QUERY_INVALID, } from '../../../../common/alerting/metrics'; +export const EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\!|\|]+/g; + +const isCustomMetricExpressionParams = ( + subject: MetricExpressionParams +): subject is CustomMetricExpressionParams => { + return subject.aggType === Aggregators.CUSTOM; +}; + export function validateMetricThreshold({ criteria, filterQuery, @@ -36,6 +47,9 @@ export function validateMetricThreshold({ threshold1: string[]; }; metric: string[]; + customMetricsError?: string; + customMetrics: Record; + equation?: string; }; } & { filterQuery?: string[] } = {}; validationResult.errors = errors; @@ -70,6 +84,7 @@ export function validateMetricThreshold({ }, metric: [], filterQuery: [], + customMetrics: {}, }; if (!c.aggType) { errors[id].aggField.push( @@ -136,16 +151,59 @@ export function validateMetricThreshold({ ); } - if (!c.metric && c.aggType !== 'count') { + if (!c.metric && c.aggType !== 'count' && c.aggType !== 'custom') { errors[id].metric.push( i18n.translate('xpack.infra.metrics.alertFlyout.error.metricRequired', { defaultMessage: 'Metric is required.', }) ); } + + if (isCustomMetricExpressionParams(c)) { + if (!c.customMetrics || (c.customMetrics && c.customMetrics.length < 1)) { + errors[id].customMetricsError = i18n.translate( + 'xpack.infra.metrics.alertFlyout.error.customMetricsError', + { + defaultMessage: 'You must define at least 1 custom metric', + } + ); + } else { + c.customMetrics.forEach((metric) => { + const customMetricErrors: { aggType?: string; field?: string } = {}; + if (!metric.aggType) { + customMetricErrors.aggType = i18n.translate( + 'xpack.infra.metrics.alertFlyout.error.customMetrics.aggTypeRequired', + { + defaultMessage: 'Aggregation is required', + } + ); + } + if (metric.aggType !== 'count' && !metric.field) { + customMetricErrors.field = i18n.translate( + 'xpack.infra.metrics.alertFlyout.error.customMetrics.fieldRequired', + { + defaultMessage: 'Field is required', + } + ); + } + if (!isEmpty(customMetricErrors)) { + errors[id].customMetrics[metric.name] = customMetricErrors; + } + }); + } + + if (c.equation && c.equation.match(EQUATION_REGEX)) { + errors[id].equation = i18n.translate( + 'xpack.infra.metrics.alertFlyout.error.equation.invalidCharacters', + { + defaultMessage: + 'The equation field only supports the following characters: A-Z, +, -, /, *, (, ), ?, !, &, :, |, >, <, =', + } + ); + } + } }); return validationResult; } - const isNumber = (value: unknown): value is number => typeof value === 'number'; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts index 1ae1bacfed42e..6fcbd6df75918 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts @@ -7,10 +7,12 @@ import { DataViewBase } from '@kbn/es-query'; import { useMemo } from 'react'; +import { MetricExpressionCustomMetric } from '../../../../common/alerting/metrics'; import { MetricsSourceConfiguration } from '../../../../common/metrics_sources'; import { MetricExpression } from '../types'; import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; import { useMetricsExplorerData } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data'; +import { MetricExplorerCustomMetricAggregations } from '../../../../common/http_api/metrics_explorer'; export const useMetricsExplorerChartData = ( expression: MetricExpression, @@ -20,6 +22,7 @@ export const useMetricsExplorerChartData = ( groupBy?: string | string[] ) => { const { timeSize, timeUnit } = expression || { timeSize: 1, timeUnit: 'm' }; + const options: MetricsExplorerOptions = useMemo( () => ({ limit: 1, @@ -28,14 +31,26 @@ export const useMetricsExplorerChartData = ( groupBy, filterQuery, metrics: [ - { - field: expression.metric, - aggregation: expression.aggType, - }, + expression.aggType === 'custom' + ? { + aggregation: 'custom', + custom_metrics: + expression?.customMetrics?.map(mapMetricThresholdMetricToMetricsExplorerMetric) ?? + [], + equation: expression.equation, + } + : { field: expression.metric, aggregation: expression.aggType }, ], aggregation: expression.aggType || 'avg', }), - [expression.aggType, expression.metric, filterQuery, groupBy] + [ + expression.aggType, + expression.equation, + expression.metric, + expression.customMetrics, + filterQuery, + groupBy, + ] ); const timerange = useMemo( () => ({ @@ -55,3 +70,19 @@ export const useMetricsExplorerChartData = ( null ); }; + +const mapMetricThresholdMetricToMetricsExplorerMetric = (metric: MetricExpressionCustomMetric) => { + if (metric.aggType === 'count') { + return { + name: metric.name, + aggregation: 'count' as MetricExplorerCustomMetricAggregations, + filter: metric.filter, + }; + } + + return { + name: metric.name, + aggregation: metric.aggType as MetricExplorerCustomMetricAggregations, + field: metric.field, + }; +}; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/i18n_strings.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/i18n_strings.ts new file mode 100644 index 0000000000000..a2778bfc6b832 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/i18n_strings.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 { i18n } from '@kbn/i18n'; + +export const EQUATION_HELP_MESSAGE = i18n.translate( + 'xpack.infra.metrics.alertFlyout.customEquationEditor.equationHelpMessage', + { defaultMessage: 'Supports basic math expressions' } +); + +export const LABEL_LABEL = i18n.translate( + 'xpack.infra.metrics.alertFlyout.customEquationEditor.labelLabel', + { defaultMessage: 'Label (optional)' } +); + +export const LABEL_HELP_MESSAGE = i18n.translate( + 'xpack.infra.metrics.alertFlyout.customEquationEditor.labelHelpMessage', + { + defaultMessage: 'Custom label will show on the alert chart and in reason/alert title', + } +); + +export const CUSTOM_EQUATION = i18n.translate('xpack.infra.metrics.alertFlyout.customEquation', { + defaultMessage: 'Custom equation', +}); + +export const DELETE_LABEL = i18n.translate( + 'xpack.infra.metrics.alertFlyout.customEquationEditor.deleteRowButton', + { defaultMessage: 'Delete' } +); + +export const AGGREGATION_LABEL = (name: string) => + i18n.translate('xpack.infra.metrics.alertFlyout.customEquationEditor.aggregationLabel', { + defaultMessage: 'Aggregation {name}', + values: { name }, + }); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts index a88dd1d4548b8..aa9336cb6023d 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts @@ -14,8 +14,14 @@ export interface AlertContextMeta { series?: MetricsExplorerSeries; } -export type MetricExpression = Omit & { +export type MetricExpression = Omit< + MetricExpressionParams, + 'metric' | 'timeSize' | 'timeUnit' | 'metrics' | 'equation' | 'customMetrics' +> & { metric?: MetricExpressionParams['metric']; + customMetrics?: MetricExpressionParams['customMetrics']; + label?: MetricExpressionParams['label']; + equation?: MetricExpressionParams['equation']; timeSize?: MetricExpressionParams['timeSize']; timeUnit?: MetricExpressionParams['timeUnit']; }; @@ -30,6 +36,7 @@ export enum AGGREGATION_TYPES { CARDINALITY = 'cardinality', P95 = 'p95', P99 = 'p99', + CUSTOM = 'custom', } export interface MetricThresholdAlertParams { diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts b/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts index a42e43a71771e..32f7de00ff645 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts @@ -70,7 +70,7 @@ export const getXYVisualizationState = ( showSingleSeries: false, }, valueLabels: 'show', - fittingFunction: 'None', + fittingFunction: 'Zero', curveType: 'LINEAR', yLeftScale: 'linear', axisTitlesVisibilitySettings: { diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_dataset_filtering.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_dataset_filtering.ts index 5bcf6455310b9..6f19fb1c0549c 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_dataset_filtering.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_dataset_filtering.ts @@ -32,9 +32,21 @@ function reducer(state: ReducerState, action: ReducerAction) { selectedDatasets: action.payload.datasets, }; case 'updateDatasetsFilters': - const datasetsToAdd = action.payload.filters - .filter((filter) => !state.selectedDatasets.includes(filter.meta.params.query)) - .map((filter) => filter.meta.params.query); + const datasetsToAdd = action.payload.filters.reduce( + (prevFilters: string[], nextFilter: Filter) => { + const query = + typeof nextFilter.meta.params === 'object' && + 'query' in nextFilter.meta.params && + nextFilter.meta.params?.query; + const queryString = query ? String(query) : ''; + if (!state.selectedDatasets.includes(queryString)) { + prevFilters.push(queryString); + } + return prevFilters; + }, + [] + ); + return { ...state, selectedDatasets: [...state.selectedDatasets, ...datasetsToAdd], @@ -77,11 +89,16 @@ export const useDatasetFiltering = () => { // be re-added via the embeddable as it will be seen as a duplicate to the FilterManager, // and no update will be emitted. useEffect(() => { - const filtersToRemove = reducerState.selectedDatasetsFilters.filter( - (filter) => !reducerState.selectedDatasets.includes(filter.meta.params.query) - ); + const filtersToRemove = reducerState.selectedDatasetsFilters.filter((filter: Filter) => { + const query = + typeof filter.meta.params === 'object' && + 'query' in filter.meta.params && + filter.meta.params?.query; + const queryString = query ? String(query) : ''; + return !reducerState.selectedDatasets.includes(queryString); + }); if (filtersToRemove.length > 0) { - filtersToRemove.forEach((filter) => { + filtersToRemove.forEach((filter: Filter) => { services.data.query.filterManager.removeFilter(filter); }); } diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/cloud_provider_icon_with_title.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/cloud_provider_icon_with_title.tsx deleted file mode 100644 index be28f1ad3b445..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/cloud_provider_icon_with_title.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiToolTip } from '@elastic/eui'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; - -const cloudIcons: Record = { - gcp: 'logoGCP', - aws: 'logoAWS', - azure: 'logoAzure', - unknownProvider: 'cloudSunny', -}; - -export const CloudProviderIconWithTitle = ({ - provider, - title, - text, -}: { - provider?: string | null; - title?: React.ReactNode; - text: string; -}) => { - return ( - - - - - - - - {title ?? ( - - {text} - - )} - - - ); -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx index 65e4b8f3d1c25..de2d046c08d19 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import React, { useCallback, useEffect, useMemo } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { EuiInMemoryTable } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isEqual } from 'lodash'; -import { buildHostsTableColumns } from './hosts_table_columns'; import { NoData } from '../../../../components/empty_states'; import { InfraLoadingPanel } from '../../../../components/loading'; import { useHostsTable } from '../hooks/use_hosts_table'; @@ -41,6 +40,8 @@ export const HostsTable = () => { metrics: HOST_TABLE_METRICS, }); + const { columns, items } = useHostsTable(nodes, { time: unifiedSearchDateRange }); + useEffect(() => { if (hostViewState.loading !== loading || nodes.length !== hostViewState.totalHits) { setHostViewState({ @@ -58,7 +59,6 @@ export const HostsTable = () => { setHostViewState, ]); - const items = useHostsTable(nodes); const noData = items.length === 0; const onTableChange = useCallback( @@ -79,11 +79,6 @@ export const HostsTable = () => { [setProperties, properties.pagination, properties.sorting] ); - const hostsTableColumns = useMemo( - () => buildHostsTableColumns({ time: unifiedSearchDateRange }), - [unifiedSearchDateRange] - ); - if (loading) { return ( { 'data-test-subj': 'hostsView-tableRow', }} items={items} - columns={hostsTableColumns} + columns={columns} onTableChange={onTableChange} /> ); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table_columns.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table_columns.tsx deleted file mode 100644 index d7c0096e129a1..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table_columns.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiBasicTableColumn } from '@elastic/eui'; -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiText } from '@elastic/eui'; -import { encode } from '@kbn/rison'; -import { TimeRange } from '@kbn/es-query'; -import type { SnapshotMetricInput, SnapshotNodeMetric } from '../../../../../common/http_api'; -import { createInventoryMetricFormatter } from '../../inventory_view/lib/create_inventory_metric_formatter'; -import { CloudProviderIconWithTitle } from './cloud_provider_icon_with_title'; -import { TruncateLinkWithTooltip } from './truncate_link_with_tooltip'; - -interface HostNodeRow extends HostMetrics { - os?: string | null; - servicesOnHost?: number | null; - title: { name: string; cloudProvider?: string | null }; - name: string; -} - -export interface HostMetrics { - cpuCores: SnapshotNodeMetric; - diskLatency: SnapshotNodeMetric; - rx: SnapshotNodeMetric; - tx: SnapshotNodeMetric; - memory: SnapshotNodeMetric; - memoryTotal: SnapshotNodeMetric; -} - -const formatMetric = (type: SnapshotMetricInput['type'], value: number | undefined | null) => - value || value === 0 ? createInventoryMetricFormatter({ type })(value) : 'N/A'; - -interface HostBuilderParams { - time: TimeRange; -} - -export const buildHostsTableColumns = ({ - time, -}: HostBuilderParams): Array> => { - const hostLinkSearch = { - _a: encode({ time: { ...time, interval: '>=1m' } }), - }; - - return [ - { - name: i18n.translate('xpack.infra.hostsViewPage.table.nameColumnHeader', { - defaultMessage: 'Name', - }), - field: 'title', - sortable: true, - truncateText: true, - render: (title: HostNodeRow['title']) => ( - - } - /> - ), - }, - { - name: i18n.translate('xpack.infra.hostsViewPage.table.operatingSystemColumnHeader', { - defaultMessage: 'Operating System', - }), - field: 'os', - sortable: true, - render: (os: string) => {os}, - }, - { - name: i18n.translate('xpack.infra.hostsViewPage.table.numberOfCpusColumnHeader', { - defaultMessage: '# of CPUs', - }), - field: 'cpuCores', - sortable: true, - render: (cpuCores: SnapshotNodeMetric) => ( - <>{formatMetric('cpuCores', cpuCores?.value ?? cpuCores?.max)} - ), - align: 'right', - }, - { - name: i18n.translate('xpack.infra.hostsViewPage.table.diskLatencyColumnHeader', { - defaultMessage: 'Disk Latency (avg.)', - }), - field: 'diskLatency.avg', - sortable: true, - render: (avg: number) => <>{formatMetric('diskLatency', avg)}, - align: 'right', - }, - { - name: i18n.translate('xpack.infra.hostsViewPage.table.averageTxColumnHeader', { - defaultMessage: 'TX (avg.)', - }), - field: 'tx.avg', - sortable: true, - render: (avg: number) => <>{formatMetric('tx', avg)}, - align: 'right', - }, - { - name: i18n.translate('xpack.infra.hostsViewPage.table.averageRxColumnHeader', { - defaultMessage: 'RX (avg.)', - }), - field: 'rx.avg', - sortable: true, - render: (avg: number) => <>{formatMetric('rx', avg)}, - align: 'right', - }, - { - name: i18n.translate('xpack.infra.hostsViewPage.table.averageMemoryTotalColumnHeader', { - defaultMessage: 'Memory total (avg.)', - }), - field: 'memoryTotal.avg', - sortable: true, - render: (avg: number) => <>{formatMetric('memoryTotal', avg)}, - align: 'right', - }, - { - name: i18n.translate('xpack.infra.hostsViewPage.table.averageMemoryUsageColumnHeader', { - defaultMessage: 'Memory usage (avg.)', - }), - field: 'memory.avg', - sortable: true, - render: (avg: number) => <>{formatMetric('memory', avg)}, - align: 'right', - }, - ]; -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table_entry_title.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table_entry_title.tsx new file mode 100644 index 0000000000000..9f0a8f9260265 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table_entry_title.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 { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiToolTip, IconType } from '@elastic/eui'; +import { TimeRange } from '@kbn/es-query'; +import { useLinkProps } from '@kbn/observability-plugin/public'; +import { encode } from '@kbn/rison'; +import type { CloudProvider, HostNodeRow } from '../hooks/use_hosts_table'; + +const cloudIcons: Record = { + gcp: 'logoGCP', + aws: 'logoAWS', + azure: 'logoAzure', + unknownProvider: 'cloudSunny', +}; + +interface HostsTableEntryTitleProps { + onClick: () => void; + time: TimeRange; + title: HostNodeRow['title']; +} + +export const HostsTableEntryTitle = ({ onClick, time, title }: HostsTableEntryTitleProps) => { + const { name, cloudProvider } = title; + + const link = useLinkProps({ + app: 'metrics', + pathname: `/detail/host/${name}`, + search: { + _a: encode({ time: { ...time, interval: '>=1m' } }), + }, + }); + + const iconType = (cloudProvider && cloudIcons[cloudProvider]) || cloudIcons.unknownProvider; + const providerName = cloudProvider ?? 'Unknown'; + + return ( + + + + + + + + + + {name} + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/kpi_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/kpi_chart.tsx index 3e82c254b0f7c..9d49e1fad782e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/kpi_chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/kpi_chart.tsx @@ -120,6 +120,6 @@ export const KPIChart = ({ const KPIChartStyled = styled(Chart)` .echMetric { - border-radius: 5px; + border-radius: ${(p) => p.theme.eui.euiBorderRadius}; } `; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/truncate_link_with_tooltip.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/truncate_link_with_tooltip.tsx deleted file mode 100644 index a7b94dd2d92b5..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/truncate_link_with_tooltip.tsx +++ /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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiToolTip, EuiLink } from '@elastic/eui'; -import React from 'react'; -import { LinkDescriptor, useLinkProps } from '@kbn/observability-plugin/public'; - -interface Props { - text: string; - linkProps: LinkDescriptor; -} - -export function TruncateLinkWithTooltip(props: Props) { - const { text, linkProps } = props; - - const link = useLinkProps(linkProps); - - return ( -
- - - {text} - - -
- ); -} diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts index aa45c44cc4e36..d85b367ba6fce 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts @@ -128,8 +128,10 @@ describe('useHostTable hook', () => { }, }, ]; - const result = renderHook(() => useHostsTable(nodes)); + const time = { from: 'now-15m', to: 'now', interval: '>=1m' }; - expect(result.result.current).toStrictEqual(items); + const { result } = renderHook(() => useHostsTable(nodes, { time })); + + expect(result.current.items).toStrictEqual(items); }); }); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.ts deleted file mode 100644 index f53b0b890041c..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useMemo } from 'react'; -import type { SnapshotNode, SnapshotNodeMetric } from '../../../../../common/http_api'; -import { HostMetrics } from '../components/hosts_table_columns'; - -type MappedMetrics = Record; - -export const useHostsTable = (nodes: SnapshotNode[]) => { - const items = useMemo(() => { - return nodes.map(({ metrics, path, name }) => ({ - name, - os: path.at(-1)?.os ?? '-', - title: { - name, - cloudProvider: path.at(-1)?.cloudProvider ?? null, - }, - ...metrics.reduce((data, metric) => { - data[metric.name as keyof HostMetrics] = metric; - return data; - }, {} as MappedMetrics), - })); - }, [nodes]); - - return items; -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx new file mode 100644 index 0000000000000..ac9096504d071 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx @@ -0,0 +1,196 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { EuiBasicTableColumn, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { TimeRange } from '@kbn/es-query'; + +import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; +import { createInventoryMetricFormatter } from '../../inventory_view/lib/create_inventory_metric_formatter'; +import { HostsTableEntryTitle } from '../components/hosts_table_entry_title'; +import type { + SnapshotNode, + SnapshotNodeMetric, + SnapshotMetricInput, +} from '../../../../../common/http_api'; + +/** + * Columns and items types + */ +export type CloudProvider = 'gcp' | 'aws' | 'azure' | 'unknownProvider'; + +type HostMetric = 'cpuCores' | 'diskLatency' | 'rx' | 'tx' | 'memory' | 'memoryTotal'; + +type HostMetrics = Record; + +export interface HostNodeRow extends HostMetrics { + os?: string | null; + servicesOnHost?: number | null; + title: { name: string; cloudProvider?: CloudProvider | null }; + name: string; +} + +// type MappedMetrics = Record; + +interface HostTableParams { + time: TimeRange; +} + +/** + * Helper functions + */ +const formatMetric = (type: SnapshotMetricInput['type'], value: number | undefined | null) => { + return value || value === 0 ? createInventoryMetricFormatter({ type })(value) : 'N/A'; +}; + +const buildItemsList = (nodes: SnapshotNode[]) => { + return nodes.map(({ metrics, path, name }) => ({ + name, + os: path.at(-1)?.os ?? '-', + title: { + name, + cloudProvider: path.at(-1)?.cloudProvider ?? null, + }, + ...metrics.reduce((data, metric) => { + data[metric.name as HostMetric] = metric; + return data; + }, {} as HostMetrics), + })) as HostNodeRow[]; +}; + +/** + * Columns translations + */ +const titleLabel = i18n.translate('xpack.infra.hostsViewPage.table.nameColumnHeader', { + defaultMessage: 'Name', +}); + +const osLabel = i18n.translate('xpack.infra.hostsViewPage.table.operatingSystemColumnHeader', { + defaultMessage: 'Operating System', +}); + +const cpuCountLabel = i18n.translate('xpack.infra.hostsViewPage.table.numberOfCpusColumnHeader', { + defaultMessage: '# of CPUs', +}); + +const diskLatencyLabel = i18n.translate('xpack.infra.hostsViewPage.table.diskLatencyColumnHeader', { + defaultMessage: 'Disk Latency (avg.)', +}); + +const averageTXLabel = i18n.translate('xpack.infra.hostsViewPage.table.averageTxColumnHeader', { + defaultMessage: 'TX (avg.)', +}); + +const averageRXLabel = i18n.translate('xpack.infra.hostsViewPage.table.averageRxColumnHeader', { + defaultMessage: 'RX (avg.)', +}); + +const averageTotalMemoryLabel = i18n.translate( + 'xpack.infra.hostsViewPage.table.averageMemoryTotalColumnHeader', + { + defaultMessage: 'Memory total (avg.)', + } +); + +const averageMemoryUsageLabel = i18n.translate( + 'xpack.infra.hostsViewPage.table.averageMemoryUsageColumnHeader', + { + defaultMessage: 'Memory usage (avg.)', + } +); + +/** + * Build a table columns and items starting from the snapshot nodes. + */ +export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) => { + const { + services: { telemetry }, + } = useKibanaContextForPlugin(); + + const reportHostEntryClick = useCallback( + ({ name, cloudProvider }: HostNodeRow['title']) => { + telemetry.reportHostEntryClicked({ + hostname: name, + cloud_provider: cloudProvider, + }); + }, + [telemetry] + ); + + const items = useMemo(() => buildItemsList(nodes), [nodes]); + + const columns: Array> = useMemo( + () => [ + { + name: titleLabel, + field: 'title', + sortable: true, + truncateText: true, + render: (title: HostNodeRow['title']) => ( + reportHostEntryClick(title)} + /> + ), + }, + { + name: osLabel, + field: 'os', + sortable: true, + render: (os: string) => {os}, + }, + { + name: cpuCountLabel, + field: 'cpuCores', + sortable: true, + render: (cpuCores: SnapshotNodeMetric) => + formatMetric('cpuCores', cpuCores?.value ?? cpuCores?.max), + align: 'right', + }, + { + name: diskLatencyLabel, + field: 'diskLatency.avg', + sortable: true, + render: (avg: number) => formatMetric('diskLatency', avg), + align: 'right', + }, + { + name: averageTXLabel, + field: 'tx.avg', + sortable: true, + render: (avg: number) => formatMetric('tx', avg), + align: 'right', + }, + { + name: averageRXLabel, + field: 'rx.avg', + sortable: true, + render: (avg: number) => formatMetric('rx', avg), + align: 'right', + }, + { + name: averageTotalMemoryLabel, + field: 'memoryTotal.avg', + sortable: true, + render: (avg: number) => formatMetric('memoryTotal', avg), + align: 'right', + }, + { + name: averageMemoryUsageLabel, + field: 'memory.avg', + sortable: true, + render: (avg: number) => formatMetric('memory', avg), + align: 'right', + }, + ], + [reportHostEntryClick, time] + ); + + return { columns, items }; +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts index 671b3484356f1..fa259784a3d72 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts @@ -32,32 +32,34 @@ export const INITAL_VALUE = { export const useHostsView = () => { const { sourceId } = useSourceContext(); - const { buildQuery, dateRangeTimestamp } = useUnifiedSearchContext(); + const { buildQuery, getDateRangeAsTimestamp } = useUnifiedSearchContext(); const [hostViewState, setHostViewState] = useState(INITAL_VALUE); const baseRequest = useMemo(() => { const esQuery = buildQuery(); + const { from, to } = getDateRangeAsTimestamp(); + const snapshotRequest: UseSnapshotRequest = { filterQuery: esQuery ? JSON.stringify(esQuery) : null, metrics: [], groupBy: [], nodeType: 'host', sourceId, - currentTime: dateRangeTimestamp.to, + currentTime: to, includeTimeseries: false, sendRequestImmediately: true, timerange: { interval: '1m', - from: dateRangeTimestamp.from, - to: dateRangeTimestamp.to, + from, + to, ignoreLookback: true, }, // The user might want to click on the submit button without changing the filters - // This makes sure all child componets will re-render. + // This makes sure all child components will re-render. requestTs: Date.now(), }; return snapshotRequest; - }, [buildQuery, dateRangeTimestamp.from, dateRangeTimestamp.to, sourceId]); + }, [buildQuery, getDateRangeAsTimestamp, sourceId]); return { baseRequest, diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts index c6753345d1705..898d384bcdc23 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts @@ -11,17 +11,37 @@ import { buildEsQuery, Filter, Query, TimeRange } from '@kbn/es-query'; import type { SavedQuery } from '@kbn/data-plugin/public'; import { debounce } from 'lodash'; import deepEqual from 'fast-deep-equal'; +import { telemetryTimeRangeFormatter } from '../../../../../common/formatters/telemetry_time_range'; import type { InfraClientStartDeps } from '../../../../types'; import { useMetricsDataViewContext } from './use_data_view'; import { useSyncKibanaTimeFilterTime } from '../../../../hooks/use_kibana_timefilter_time'; -import { useHostsUrlState, INITIAL_DATE_RANGE } from './use_unified_search_url_state'; +import { + useHostsUrlState, + INITIAL_DATE_RANGE, + HostsState, + StringDateRangeTimestamp, +} from './use_unified_search_url_state'; + +const buildQuerySubmittedPayload = ( + hostState: HostsState & { dateRangeTimestamp: StringDateRangeTimestamp } +) => { + const { panelFilters, filters, dateRangeTimestamp, query: queryObj } = hostState; + + return { + control_filters: panelFilters.map((filter) => JSON.stringify(filter)), + filters: filters.map((filter) => JSON.stringify(filter)), + interval: telemetryTimeRangeFormatter(dateRangeTimestamp.to - dateRangeTimestamp.from), + query: queryObj.query, + }; +}; export const useUnifiedSearch = () => { - const { state, dispatch, getRangeInTimestamp, getTime } = useHostsUrlState(); + const { state, dispatch, getTime, getDateRangeAsTimestamp } = useHostsUrlState(); const { metricsDataView } = useMetricsDataViewContext(); const { services } = useKibana(); const { data: { query: queryManager }, + telemetry, } = services; useSyncKibanaTimeFilterTime(INITIAL_DATE_RANGE, { @@ -62,6 +82,14 @@ export const useUnifiedSearch = () => { }; }); + // Track telemetry event on query/filter/date changes + useEffect(() => { + const dateRangeTimestamp = getDateRangeAsTimestamp(); + telemetry.reportHostsViewQuerySubmitted( + buildQuerySubmittedPayload({ ...state, dateRangeTimestamp }) + ); + }, [getDateRangeAsTimestamp, state, telemetry]); + const onSubmit = useCallback( (data?: { query?: Query; @@ -78,12 +106,11 @@ export const useUnifiedSearch = () => { query, filters, dateRange: newDateRange, - dateRangeTimestamp: getRangeInTimestamp(newDateRange), panelFilters, }, }); }, - [getTime, dispatch, getRangeInTimestamp] + [getTime, dispatch] ); // This won't prevent onSubmit from being fired twice when `clear filters` is clicked, @@ -131,9 +158,9 @@ export const useUnifiedSearch = () => { buildQuery, clearSavedQuery, controlPanelFilters: state.panelFilters, - dateRangeTimestamp: state.dateRangeTimestamp, onSubmit: debounceOnSubmit, saveQuery, + getDateRangeAsTimestamp, unifiedSearchQuery: state.query, unifiedSearchDateRange: state.dateRange, unifiedSearchFilters: state.filters, diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts index e50fa2fa76cc4..41e476dbf12c5 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts @@ -6,7 +6,6 @@ */ import { useCallback, useEffect, useReducer } from 'react'; -import { TimeRange } from '@kbn/es-query'; import DateMath from '@kbn/datemath'; import deepEqual from 'fast-deep-equal'; import * as rt from 'io-ts'; @@ -23,25 +22,19 @@ const DEFAULT_QUERY = { query: '', }; const DEFAULT_FROM_MINUTES_VALUE = 15; -const INITIAL_DATE = new Date(); -export const INITIAL_DATE_RANGE = { from: `now-${DEFAULT_FROM_MINUTES_VALUE}m`, to: 'now' }; -const CALCULATED_DATE_RANGE_TO = INITIAL_DATE.getTime(); const DEFAULT_FROM_IN_MILLISECONDS = DEFAULT_FROM_MINUTES_VALUE * 60000; -const CALCULATED_DATE_RANGE_FROM = new Date( - CALCULATED_DATE_RANGE_TO - DEFAULT_FROM_IN_MILLISECONDS -).getTime(); + +export const INITIAL_DATE_RANGE = { from: `now-${DEFAULT_FROM_MINUTES_VALUE}m`, to: 'now' }; + +const getDefaultFromTimestamp = () => Date.now() - DEFAULT_FROM_IN_MILLISECONDS; +const getDefaultToTimestamp = () => Date.now(); const INITIAL_HOSTS_STATE: HostsState = { query: DEFAULT_QUERY, filters: [], panelFilters: [], // for unified search - dateRange: { ...INITIAL_DATE_RANGE }, - // for useSnapshot - dateRangeTimestamp: { - from: CALCULATED_DATE_RANGE_FROM, - to: CALCULATED_DATE_RANGE_TO, - }, + dateRange: INITIAL_DATE_RANGE, }; type Action = @@ -84,15 +77,12 @@ export const useHostsUrlState = () => { const [state, dispatch] = useReducer(reducer, urlState); - const getRangeInTimestamp = useCallback(({ from, to }: TimeRange) => { - const fromTS = DateMath.parse(from)?.valueOf() ?? CALCULATED_DATE_RANGE_FROM; - const toTS = DateMath.parse(to)?.valueOf() ?? CALCULATED_DATE_RANGE_TO; + const getDateRangeAsTimestamp = useCallback(() => { + const from = DateMath.parse(state.dateRange.from)?.valueOf() ?? getDefaultFromTimestamp(); + const to = DateMath.parse(state.dateRange.to)?.valueOf() ?? getDefaultToTimestamp(); - return { - from: fromTS, - to: toTS, - }; - }, []); + return { from, to }; + }, [state.dateRange]); useEffect(() => { if (!deepEqual(state, urlState)) { @@ -102,7 +92,7 @@ export const useHostsUrlState = () => { return { dispatch, - getRangeInTimestamp, + getDateRangeAsTimestamp, getTime, state, }; @@ -144,21 +134,20 @@ const StringDateRangeRT = rt.type({ to: rt.string, }); -const DateRangeRT = rt.type({ - from: rt.number, - to: rt.number, -}); - const HostsStateRT = rt.type({ filters: HostsFiltersRT, panelFilters: HostsFiltersRT, query: HostsQueryStateRT, dateRange: StringDateRangeRT, - dateRangeTimestamp: DateRangeRT, }); export type HostsState = rt.TypeOf; +export interface StringDateRangeTimestamp { + from: number; + to: number; +} + const SetQueryType = rt.partial(HostsStateRT.props); const encodeUrlState = HostsStateRT.encode; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts index 32f0e1d06cff8..bc76dbba39e10 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { get } from 'lodash'; +import { get, isNumber } from 'lodash'; import { SnapshotMetricType } from '../../../../../common/inventory_models/types'; import { InfraFormatterType } from '../../../../lib/lib'; import { @@ -95,7 +95,7 @@ export const createInventoryMetricFormatter = (metric: SnapshotMetricInput) => (val: string | number) => { if (SnapshotCustomMetricInputRT.is(metric)) { const formatter = createFormatterForMetric(metric); - return formatter(val); + return isNumber(val) ? formatter(val) : val; } const metricFormatter = get(METRIC_FORMATTERS, metric.type, METRIC_FORMATTERS.count); if (val == null || !metricFormatter) { diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx index 7b76cf22098fb..d9117da001247 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx @@ -9,6 +9,7 @@ import { EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useCallback } from 'react'; +import { xor } from 'lodash'; import { MetricsExplorerAggregation } from '../../../../../common/http_api/metrics_explorer'; import { MetricsExplorerOptions } from '../hooks/use_metrics_explorer_options'; import { @@ -22,6 +23,8 @@ interface Props { onChange: (aggregation: MetricsExplorerAggregation) => void; } +type MetricsExplorerAggregationWithoutCustom = Exclude; + export const MetricsExplorerAggregationPicker = ({ options, onChange }: Props) => { const AGGREGATION_LABELS = { ['avg']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.avg', { @@ -66,14 +69,18 @@ export const MetricsExplorerAggregationPicker = ({ options, onChange }: Props) = defaultMessage: 'Select an aggregation', }); + const METRIC_EXPLORER_AGGREGATIONS_WITHOUT_CUSTOM = xor(METRIC_EXPLORER_AGGREGATIONS, [ + 'custom', + ]) as MetricsExplorerAggregationWithoutCustom[]; + return ( ({ - text: AGGREGATION_LABELS[k as MetricsExplorerAggregation], + options={METRIC_EXPLORER_AGGREGATIONS_WITHOUT_CUSTOM.map((k) => ({ + text: AGGREGATION_LABELS[k], value: k, }))} onChange={handleChange} diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts index 3857b81825c00..b6f77bfc260a4 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts @@ -5,11 +5,15 @@ * 2.0. */ +import numeral from '@elastic/numeral'; import { MetricsExplorerMetric } from '../../../../../../common/http_api/metrics_explorer'; import { createFormatter } from '../../../../../../common/formatters'; import { InfraFormatterType } from '../../../../../lib/lib'; import { metricToFormat } from './metric_to_format'; export const createFormatterForMetric = (metric?: MetricsExplorerMetric) => { + if (metric?.aggregation === 'custom') { + return (input: number) => numeral(input).format('0.[0000]'); + } if (metric && metric.field) { const format = metricToFormat(metric); if (format === InfraFormatterType.bits && metric.aggregation === 'rate') { diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts index 2c1be3d17f6f8..ccb1d316d73d2 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts @@ -67,13 +67,7 @@ export function useMetricsExplorerData( body: JSON.stringify({ forceInterval: options.forceInterval, dropLastBucket: options.dropLastBucket != null ? options.dropLastBucket : true, - metrics: - options.aggregation === 'count' - ? [{ aggregation: 'count' }] - : options.metrics.map((metric) => ({ - aggregation: metric.aggregation, - field: metric.field, - })), + metrics: options.aggregation === 'count' ? [{ aggregation: 'count' }] : options.metrics, groupBy: options.groupBy, afterKey, limit: options.limit, @@ -117,6 +111,7 @@ export function useMetricsExplorerData( }, onReject: (e: unknown) => { setError(e as Error); + setData(null); setLoading(false); }, }, diff --git a/x-pack/plugins/infra/public/services/telemetry/types.ts b/x-pack/plugins/infra/public/services/telemetry/types.ts index 9f92f9e614810..4ce5dea999111 100644 --- a/x-pack/plugins/infra/public/services/telemetry/types.ts +++ b/x-pack/plugins/infra/public/services/telemetry/types.ts @@ -33,12 +33,12 @@ export interface HostsViewQuerySubmittedSchema { export interface HostEntryClickedParams { hostname: string; - cloud_provider?: string; + cloud_provider?: string | null; } export interface HostEntryClickedSchema { hostname: SchemaValue; - cloud_provider: SchemaValue; + cloud_provider: SchemaValue; } export interface ITelemetryClient { diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts index a001e2a7e9096..cf4f3a0c8b934 100644 --- a/x-pack/plugins/infra/public/types.ts +++ b/x-pack/plugins/infra/public/types.ts @@ -80,6 +80,7 @@ export interface InfraClientStartDeps { share: SharePluginStart; storage: IStorageWrapper; lens: LensPublicStart; + telemetry: ITelemetryClient; } export type InfraClientCoreSetup = CoreSetup; diff --git a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts index d35651d66c95a..ab7b0490aa901 100644 --- a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts +++ b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts @@ -20,6 +20,13 @@ export const DOCUMENT_COUNT_I18N = i18n.translate( } ); +export const CUSTOM_EQUATION_I18N = i18n.translate( + 'xpack.infra.metrics.alerting.threshold.customEquation', + { + defaultMessage: 'Custom equation', + } +); + export const stateToAlertMessage = { [AlertStates.ALERT]: i18n.translate('xpack.infra.metrics.alerting.threshold.alertState', { defaultMessage: 'ALERT', @@ -76,7 +83,7 @@ const thresholdToI18n = ([a, b]: Array) => { }); }; -const formatGroup = (group: string) => (group === UNGROUPED_FACTORY_KEY ? 'all hosts' : group); +const formatGroup = (group: string) => (group === UNGROUPED_FACTORY_KEY ? '' : ` for ${group}`); export const buildFiredAlertReason: (alertResult: { group: string; @@ -89,7 +96,7 @@ export const buildFiredAlertReason: (alertResult: { }) => string = ({ group, metric, comparator, threshold, currentValue, timeSize, timeUnit }) => i18n.translate('xpack.infra.metrics.alerting.threshold.firedAlertReason', { defaultMessage: - '{metric} is {currentValue} in the last {duration} for {group}. Alert when {comparator} {threshold}.', + '{metric} is {currentValue} in the last {duration}{group}. Alert when {comparator} {threshold}.', values: { group: formatGroup(group), metric, @@ -271,3 +278,19 @@ export const tagsActionVariableDescription = i18n.translate( defaultMessage: 'List of tags associated with the entity where this alert triggered.', } ); + +export const originalAlertStateActionVariableDescription = i18n.translate( + 'xpack.infra.metrics.alerting.originalAlertStateActionVariableDescription', + { + defaultMessage: + 'The state of the alert before it recovered. This is only available in the recovery context', + } +); + +export const originalAlertStateWasActionVariableDescription = i18n.translate( + 'xpack.infra.metrics.alerting.originalAlertStateWasWARNINGActionVariableDescription', + { + defaultMessage: + 'Boolean value of the state of the alert before it recovered. This can be used for template conditions. This is only available in the recovery context', + } +); diff --git a/x-pack/plugins/infra/server/lib/alerting/common/utils.ts b/x-pack/plugins/infra/server/lib/alerting/common/utils.ts index 970d3779a2e25..b6c0b5578c6a0 100644 --- a/x-pack/plugins/infra/server/lib/alerting/common/utils.ts +++ b/x-pack/plugins/infra/server/lib/alerting/common/utils.ts @@ -237,7 +237,7 @@ export const flattenAdditionalContext = ( }; export const getContextForRecoveredAlerts = ( - alertHits: AdditionalContext | undefined | null + alertHits: AdditionalContext[] | undefined | null ): AdditionalContext => { const alertHitsSource = alertHits && alertHits.length > 0 ? unflattenObject(alertHits[0]._source) : undefined; diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index b6e0c5dd578e5..ffd0a7e563339 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { ALERT_REASON } from '@kbn/rule-data-utils'; +import { ALERT_REASON, ALERT_ACTION_GROUP } from '@kbn/rule-data-utils'; import { first, get } from 'lodash'; import { ActionGroup, @@ -15,6 +15,7 @@ import { AlertInstanceState as AlertState, } from '@kbn/alerting-plugin/common'; import { Alert, RuleTypeState } from '@kbn/alerting-plugin/server'; +import { getOriginalActionGroup } from '../../../utils/get_original_action_group'; import { AlertStates, InventoryMetricThresholdParams } from '../../../../common/alerting/metrics'; import { createFormatter } from '../../../../common/formatters'; import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label'; @@ -45,6 +46,11 @@ type InventoryMetricThresholdAllowedActionGroups = ActionGroupIdsOf< typeof FIRED_ACTIONS | typeof WARNING_ACTIONS >; +export const FIRED_ACTIONS_ID = 'metrics.inventory_threshold.fired'; +export const WARNING_ACTIONS_ID = 'metrics.inventory_threshold.warning'; + +type InventoryThrehsoldActionGroup = typeof FIRED_ACTIONS_ID | typeof WARNING_ACTIONS_ID; + export type InventoryMetricThresholdRuleTypeState = RuleTypeState; // no specific state used export type InventoryMetricThresholdAlertState = AlertState; // no specific state used export type InventoryMetricThresholdAlertContext = AlertContext; // no specific instance context used @@ -57,6 +63,7 @@ type InventoryMetricThresholdAlert = Alert< type InventoryMetricThresholdAlertFactory = ( id: string, reason: string, + actionGroup: InventoryThrehsoldActionGroup, additionalContext?: AdditionalContext | null, threshold?: number | undefined, value?: number | undefined @@ -90,11 +97,17 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = getAlertUuid, getAlertByAlertUuid, } = services; - const alertFactory: InventoryMetricThresholdAlertFactory = (id, reason, additionalContext) => + const alertFactory: InventoryMetricThresholdAlertFactory = ( + id, + reason, + actionGroup, + additionalContext + ) => alertWithLifecycle({ id, fields: { [ALERT_REASON]: reason, + [ALERT_ACTION_GROUP]: actionGroup, ...flattenAdditionalContext(additionalContext), }, }); @@ -107,7 +120,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = logger.error(e.message); const actionGroupId = FIRED_ACTIONS.id; // Change this to an Error action group when able const reason = buildInvalidQueryAlertReason(params.filterQueryText); - const alert = alertFactory(UNGROUPED_FACTORY_KEY, reason); + const alert = alertFactory(UNGROUPED_FACTORY_KEY, reason, actionGroupId); const indexedStartedDate = getAlertStartedDate(UNGROUPED_FACTORY_KEY) ?? startedAt.toISOString(); const alertUuid = getAlertUuid(UNGROUPED_FACTORY_KEY); @@ -212,11 +225,11 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = } if (reason) { const actionGroupId = - nextState === AlertStates.WARNING ? WARNING_ACTIONS.id : FIRED_ACTIONS.id; + nextState === AlertStates.WARNING ? WARNING_ACTIONS_ID : FIRED_ACTIONS_ID; const additionalContext = results && results.length > 0 ? results[0][group].context : null; - const alert = alertFactory(group, reason, additionalContext); + const alert = alertFactory(group, reason, actionGroupId, additionalContext); const indexedStartedDate = getAlertStartedDate(group) ?? startedAt.toISOString(); const alertUuid = getAlertUuid(group); @@ -255,6 +268,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = const alertUuid = getAlertUuid(recoveredAlertId); const alertHits = alertUuid ? await getAlertByAlertUuid(alertUuid) : undefined; const additionalContext = getContextForRecoveredAlerts(alertHits); + const originalActionGroup = getOriginalActionGroup(alertHits); alert.setContext({ alertDetailsUrl: getAlertDetailsUrl(libs.basePath, spaceId, alertUuid), @@ -270,6 +284,9 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = timestamp: indexedStartedDate, spaceId, }), + originalAlertState: translateActionGroupToAlertState(originalActionGroup), + originalAlertStateWasALERT: originalActionGroup === FIRED_ACTIONS_ID, + originalAlertStateWasWARNING: originalActionGroup === WARNING_ACTIONS_ID, ...additionalContext, }); } @@ -335,14 +352,12 @@ const mapToConditionsLookup = ( {} ); -export const FIRED_ACTIONS_ID = 'metrics.inventory_threshold.fired'; export const FIRED_ACTIONS: ActionGroup = { id: FIRED_ACTIONS_ID, name: i18n.translate('xpack.infra.metrics.alerting.inventory.threshold.fired', { defaultMessage: 'Alert', }), }; -export const WARNING_ACTIONS_ID = 'metrics.inventory_threshold.warning'; export const WARNING_ACTIONS = { id: WARNING_ACTIONS_ID, name: i18n.translate('xpack.infra.metrics.alerting.threshold.warning', { @@ -350,6 +365,17 @@ export const WARNING_ACTIONS = { }), }; +const translateActionGroupToAlertState = ( + actionGroupId: string | undefined +): string | undefined => { + if (actionGroupId === FIRED_ACTIONS.id) { + return stateToAlertMessage[AlertStates.ALERT]; + } + if (actionGroupId === WARNING_ACTIONS.id) { + return stateToAlertMessage[AlertStates.WARNING]; + } +}; + const formatMetric = (metric: SnapshotMetricType, value: number) => { const metricFormatter = get(METRIC_FORMATTERS, metric, METRIC_FORMATTERS.count); if (isNaN(value)) { diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts index a5ba2c32ada6e..b0b8a13562feb 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts @@ -33,6 +33,8 @@ import { labelsActionVariableDescription, metricActionVariableDescription, orchestratorActionVariableDescription, + originalAlertStateActionVariableDescription, + originalAlertStateWasActionVariableDescription, reasonActionVariableDescription, tagsActionVariableDescription, thresholdActionVariableDescription, @@ -124,6 +126,15 @@ export async function registerMetricInventoryThresholdRuleType( { name: 'orchestrator', description: orchestratorActionVariableDescription }, { name: 'labels', description: labelsActionVariableDescription }, { name: 'tags', description: tagsActionVariableDescription }, + { name: 'originalAlertState', description: originalAlertStateActionVariableDescription }, + { + name: 'originalAlertStateWasALERT', + description: originalAlertStateWasActionVariableDescription, + }, + { + name: 'originalAlertStateWasWARNING', + description: originalAlertStateWasActionVariableDescription, + }, ], }, getSummarizedAlerts: libs.metricsRules.createGetSummarizedAlerts(), diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts index d078cee95e45e..a458104a7400a 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts @@ -32,12 +32,14 @@ export const createBucketSelector = ( const isCount = condition.aggType === Aggregators.COUNT; const isRate = condition.aggType === Aggregators.RATE; const bucketPath = isCount - ? 'currentPeriod>_count' + ? "currentPeriod['all']>_count" : isRate ? `aggregatedValue` : isPercentile - ? `currentPeriod>aggregatedValue[${condition.aggType === Aggregators.P95 ? '95' : '99'}]` - : 'currentPeriod>aggregatedValue'; + ? `currentPeriod[\'all\']>aggregatedValue[${ + condition.aggType === Aggregators.P95 ? '95' : '99' + }]` + : "currentPeriod['all']>aggregatedValue"; const shouldWarn = hasWarn ? { @@ -74,7 +76,7 @@ export const createBucketSelector = ( bucket_script: { buckets_path: { lastPeriod: 'lastPeriod>_count', - currentPeriod: 'currentPeriod>_count', + currentPeriod: "currentPeriod['all']>_count", }, script: 'params.lastPeriod > 0 && params.currentPeriod < 1 ? 1 : 0', }, @@ -83,7 +85,7 @@ export const createBucketSelector = ( bucket_script: { buckets_path: { lastPeriod: 'lastPeriod>_count', - currentPeriod: 'currentPeriod>_count', + currentPeriod: "currentPeriod['all']>_count", }, script: 'params.lastPeriod < 1 && params.currentPeriod > 0 ? 1 : 0', }, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_rate_aggregation.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_rate_aggregation.ts index 2fdb8f5c6b834..e6b24e334a745 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_rate_aggregation.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_rate_aggregation.ts @@ -21,8 +21,8 @@ export const createRateAggsBucketScript = ( [id]: { bucket_script: { buckets_path: { - first: `currentPeriod>${id}_first_bucket.maxValue`, - second: `currentPeriod>${id}_second_bucket.maxValue`, + first: `currentPeriod['all']>${id}_first_bucket.maxValue`, + second: `currentPeriod['all']>${id}_second_bucket.maxValue`, }, script: `params.second > 0.0 && params.first > 0.0 && params.second > params.first ? (params.second - params.first) / ${intervalInSeconds}: null`, }, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_rule.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_rule.ts index b9c7817eb5be4..7125f18aa2a8d 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_rule.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_rule.ts @@ -11,7 +11,7 @@ import type { Logger } from '@kbn/logging'; import { MetricExpressionParams } from '../../../../../common/alerting/metrics'; import { InfraSource } from '../../../../../common/source_configuration/source_configuration'; import { getIntervalInSeconds } from '../../../../../common/utils/get_interval_in_seconds'; -import { DOCUMENT_COUNT_I18N } from '../../common/messages'; +import { CUSTOM_EQUATION_I18N, DOCUMENT_COUNT_I18N } from '../../common/messages'; import { createTimerange } from './create_timerange'; import { getData } from './get_data'; import { checkMissingGroups, MissingGroupsRecord } from './check_missing_group'; @@ -101,7 +101,14 @@ export const evaluateRule = async ( ) => { return { currentPeriod: { - filter: { - range: { - [TIMESTAMP_FIELD]: { - gte: moment(timeframe.start).toISOString(), - lte: moment(timeframe.end).toISOString(), + filters: { + filters: { + all: { + range: { + [TIMESTAMP_FIELD]: { + gte: moment(timeframe.start).toISOString(), + lte: moment(timeframe.end).toISOString(), + }, + }, }, }, }, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index 409dede158515..92fbc186dce5f 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -878,8 +878,6 @@ describe('The metric threshold alert type', () => { expect(reasons[1]).toContain('Alert when >= 3'); expect(reasons[0]).toContain('in the last 1 min'); expect(reasons[1]).toContain('in the last 1 min'); - expect(reasons[0]).toContain('for all hosts'); - expect(reasons[1]).toContain('for all hosts'); }); }); describe('querying with the count aggregator', () => { @@ -1659,9 +1657,7 @@ describe('The metric threshold alert type', () => { const { action } = mostRecentAction(instanceID); expect(action.group).toBe('*'); - expect(action.reason).toBe( - 'test.metric.1 is 2.5 in the last 1 min for all hosts. Alert when > 2.49.' - ); + expect(action.reason).toBe('test.metric.1 is 2.5 in the last 1 min. Alert when > 2.49.'); }); test('reports expected warning values to the action context for percentage metric', async () => { @@ -1675,9 +1671,7 @@ describe('The metric threshold alert type', () => { const { action } = mostRecentAction(instanceID); expect(action.group).toBe('*'); - expect(action.reason).toBe( - 'system.cpu.user.pct is 82% in the last 1 min for all hosts. Alert when > 81%.' - ); + expect(action.reason).toBe('system.cpu.user.pct is 82% in the last 1 min. Alert when > 81%.'); }); }); }); @@ -1823,18 +1817,19 @@ declare global { } } -const baseNonCountCriterion: Pick< - NonCountMetricExpressionParams, - 'aggType' | 'metric' | 'timeSize' | 'timeUnit' -> = { +const baseNonCountCriterion = { aggType: Aggregators.AVERAGE, metric: 'test.metric.1', timeSize: 1, timeUnit: 'm', -}; + threshold: [0], + comparator: Comparator.GT, +} as NonCountMetricExpressionParams; -const baseCountCriterion: Pick = { +const baseCountCriterion = { aggType: Aggregators.COUNT, timeSize: 1, timeUnit: 'm', -}; + threshold: [0], + comparator: Comparator.GT, +} as CountMetricExpressionParams; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index 07af8bbcafe22..cd0b0f48f36d4 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { ALERT_REASON } from '@kbn/rule-data-utils'; +import { ALERT_ACTION_GROUP, ALERT_REASON } from '@kbn/rule-data-utils'; import { isEqual } from 'lodash'; import { ActionGroupIdsOf, @@ -16,6 +16,7 @@ import { } from '@kbn/alerting-plugin/common'; import { Alert, RuleTypeState } from '@kbn/alerting-plugin/server'; import { TimeUnitChar } from '@kbn/observability-plugin/common/utils/formatters/duration'; +import { getOriginalActionGroup } from '../../../utils/get_original_action_group'; import { AlertStates, Comparator } from '../../../../common/alerting/metrics'; import { createFormatter } from '../../../../common/formatters'; import { InfraBackendLibs } from '../../infra_types'; @@ -53,8 +54,18 @@ export type MetricThresholdRuleTypeState = RuleTypeState & { export type MetricThresholdAlertState = AlertState; // no specific instace state used export type MetricThresholdAlertContext = AlertContext; // no specific instace state used +export const FIRED_ACTIONS_ID = 'metrics.threshold.fired'; +export const WARNING_ACTIONS_ID = 'metrics.threshold.warning'; +export const NO_DATA_ACTIONS_ID = 'metrics.threshold.nodata'; + +type MetricThresholdActionGroup = + | typeof FIRED_ACTIONS_ID + | typeof WARNING_ACTIONS_ID + | typeof NO_DATA_ACTIONS_ID + | typeof RecoveredActionGroup.id; + type MetricThresholdAllowedActionGroups = ActionGroupIdsOf< - typeof FIRED_ACTIONS | typeof WARNING_ACTIONS + typeof FIRED_ACTIONS | typeof WARNING_ACTIONS | typeof NO_DATA_ACTIONS >; type MetricThresholdAlert = Alert< @@ -66,6 +77,7 @@ type MetricThresholdAlert = Alert< type MetricThresholdAlertFactory = ( id: string, reason: string, + actionGroup: MetricThresholdActionGroup, additionalContext?: AdditionalContext | null, threshold?: number | undefined, value?: number | undefined @@ -101,11 +113,17 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => const { alertWithLifecycle, savedObjectsClient, getAlertUuid, getAlertByAlertUuid } = services; - const alertFactory: MetricThresholdAlertFactory = (id, reason, additionalContext) => + const alertFactory: MetricThresholdAlertFactory = ( + id, + reason, + actionGroup, + additionalContext + ) => alertWithLifecycle({ id, fields: { [ALERT_REASON]: reason, + [ALERT_ACTION_GROUP]: actionGroup, ...flattenAdditionalContext(additionalContext), }, }); @@ -127,9 +145,9 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => } catch (e) { logger.error(e.message); const timestamp = startedAt.toISOString(); - const actionGroupId = FIRED_ACTIONS.id; // Change this to an Error action group when able + const actionGroupId = FIRED_ACTIONS_ID; // Change this to an Error action group when able const reason = buildInvalidQueryAlertReason(params.filterQueryText); - const alert = alertFactory(UNGROUPED_FACTORY_KEY, reason); + const alert = alertFactory(UNGROUPED_FACTORY_KEY, reason, actionGroupId); const alertUuid = getAlertUuid(UNGROUPED_FACTORY_KEY); alert.scheduleActions(actionGroupId, { @@ -258,14 +276,14 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => if (reason) { const timestamp = startedAt.toISOString(); - const actionGroupId = + const actionGroupId: MetricThresholdActionGroup = nextState === AlertStates.OK ? RecoveredActionGroup.id : nextState === AlertStates.NO_DATA - ? NO_DATA_ACTIONS.id + ? NO_DATA_ACTIONS_ID : nextState === AlertStates.WARNING - ? WARNING_ACTIONS.id - : FIRED_ACTIONS.id; + ? WARNING_ACTIONS_ID + : FIRED_ACTIONS_ID; const additionalContext = hasAdditionalContext(params.groupBy, validGroupByForContext) ? alertResults && alertResults.length > 0 @@ -273,7 +291,7 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => : null : null; - const alert = alertFactory(`${group}`, reason, additionalContext); + const alert = alertFactory(`${group}`, reason, actionGroupId, additionalContext); const alertUuid = getAlertUuid(group); scheduledActionsCount++; @@ -313,6 +331,7 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => const alertHits = alertUuid ? await getAlertByAlertUuid(alertUuid) : undefined; const additionalContext = getContextForRecoveredAlerts(alertHits); + const originalActionGroup = getOriginalActionGroup(alertHits); alert.setContext({ alertDetailsUrl: getAlertDetailsUrl(libs.basePath, spaceId, alertUuid), @@ -323,6 +342,12 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => timestamp: startedAt.toISOString(), threshold: mapToConditionsLookup(criteria, (c) => c.threshold), viewInAppUrl: getViewInMetricsAppUrl(libs.basePath, spaceId), + + originalAlertState: translateActionGroupToAlertState(originalActionGroup), + originalAlertStateWasALERT: originalActionGroup === FIRED_ACTIONS.id, + originalAlertStateWasWARNING: originalActionGroup === WARNING_ACTIONS.id, + // eslint-disable-next-line @typescript-eslint/naming-convention + originalAlertStateWasNO_DATA: originalActionGroup === NO_DATA_ACTIONS.id, ...additionalContext, }); } @@ -360,6 +385,20 @@ export const NO_DATA_ACTIONS = { }), }; +const translateActionGroupToAlertState = ( + actionGroupId: string | undefined +): string | undefined => { + if (actionGroupId === FIRED_ACTIONS.id) { + return stateToAlertMessage[AlertStates.ALERT]; + } + if (actionGroupId === WARNING_ACTIONS.id) { + return stateToAlertMessage[AlertStates.WARNING]; + } + if (actionGroupId === NO_DATA_ACTIONS.id) { + return stateToAlertMessage[AlertStates.NO_DATA]; + } +}; + const mapToConditionsLookup = ( list: any[], mapFn: (value: any, index: number, array: any[]) => unknown diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts index 55e2379bcf19a..b9d069db244fb 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts @@ -23,6 +23,8 @@ import { labelsActionVariableDescription, metricActionVariableDescription, orchestratorActionVariableDescription, + originalAlertStateActionVariableDescription, + originalAlertStateWasActionVariableDescription, reasonActionVariableDescription, tagsActionVariableDescription, thresholdActionVariableDescription, @@ -68,12 +70,42 @@ export async function registerMetricThresholdRuleType( ...baseCriterion, metric: schema.string(), aggType: oneOfLiterals(METRIC_EXPLORER_AGGREGATIONS), + customMetrics: schema.never(), + equation: schema.never(), + label: schema.never(), }); const countCriterion = schema.object({ ...baseCriterion, aggType: schema.literal('count'), metric: schema.never(), + customMetrics: schema.never(), + equation: schema.never(), + label: schema.never(), + }); + + const customCriterion = schema.object({ + ...baseCriterion, + aggType: schema.literal('custom'), + metric: schema.never(), + customMetrics: schema.arrayOf( + schema.oneOf([ + schema.object({ + name: schema.string(), + aggType: oneOfLiterals(['avg', 'sum', 'max', 'min', 'cardinality']), + field: schema.string(), + filter: schema.never(), + }), + schema.object({ + name: schema.string(), + aggType: schema.literal('count'), + filter: schema.maybe(schema.string()), + field: schema.never(), + }), + ]) + ), + equation: schema.maybe(schema.string()), + label: schema.maybe(schema.string()), }); alertingPlugin.registerType({ @@ -84,7 +116,9 @@ export async function registerMetricThresholdRuleType( validate: { params: schema.object( { - criteria: schema.arrayOf(schema.oneOf([countCriterion, nonCountCriterion])), + criteria: schema.arrayOf( + schema.oneOf([countCriterion, nonCountCriterion, customCriterion]) + ), groupBy: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), filterQuery: schema.maybe( schema.string({ @@ -124,6 +158,19 @@ export async function registerMetricThresholdRuleType( { name: 'orchestrator', description: orchestratorActionVariableDescription }, { name: 'labels', description: labelsActionVariableDescription }, { name: 'tags', description: tagsActionVariableDescription }, + { name: 'originalAlertState', description: originalAlertStateActionVariableDescription }, + { + name: 'originalAlertStateWasALERT', + description: originalAlertStateWasActionVariableDescription, + }, + { + name: 'originalAlertStateWasWARNING', + description: originalAlertStateWasActionVariableDescription, + }, + { + name: 'originalAlertStateWasNO_DATA', + description: originalAlertStateWasActionVariableDescription, + }, ], }, producer: 'infrastructure', diff --git a/x-pack/plugins/infra/server/lib/create_custom_metrics_aggregations.ts b/x-pack/plugins/infra/server/lib/create_custom_metrics_aggregations.ts new file mode 100644 index 0000000000000..862ab7c2d2961 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/create_custom_metrics_aggregations.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 { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { isEmpty } from 'lodash'; +import { MetricExpressionCustomMetric } from '../../common/alerting/metrics'; +import { MetricsExplorerCustomMetric } from '../../common/http_api'; + +const isMetricExpressionCustomMetric = ( + subject: MetricsExplorerCustomMetric | MetricExpressionCustomMetric +): subject is MetricExpressionCustomMetric => { + return (subject as MetricExpressionCustomMetric).aggType != null; +}; + +export const createCustomMetricsAggregations = ( + id: string, + customMetrics: Array, + equation?: string +) => { + const bucketsPath: { [id: string]: string } = {}; + const metricAggregations = customMetrics.reduce((acc, metric) => { + const key = `${id}_${metric.name}`; + const aggregation = isMetricExpressionCustomMetric(metric) + ? metric.aggType + : metric.aggregation; + + if (aggregation === 'count') { + bucketsPath[metric.name] = `${key}>_count`; + return { + ...acc, + [key]: { + filter: metric.filter + ? toElasticsearchQuery(fromKueryExpression(metric.filter)) + : { match_all: {} }, + }, + }; + } + + if (aggregation && metric.field) { + bucketsPath[metric.name] = key; + return { + ...acc, + [key]: { + [aggregation]: { field: metric.field }, + }, + }; + } + + return acc; + }, {}); + + if (isEmpty(metricAggregations)) { + return {}; + } + + return { + ...metricAggregations, + [id]: { + bucket_script: { + buckets_path: bucketsPath, + script: { + source: convertEquationToPainless(bucketsPath, equation), + lang: 'painless', + }, + }, + }, + }; +}; + +const convertEquationToPainless = (bucketsPath: { [id: string]: string }, equation?: string) => { + const workingEquation = equation || Object.keys(bucketsPath).join(' + '); + return Object.keys(bucketsPath).reduce((acc, key) => { + return acc.replace(key, `params.${key}`); + }, workingEquation); +}; diff --git a/x-pack/plugins/infra/server/lib/metrics/index.ts b/x-pack/plugins/infra/server/lib/metrics/index.ts index b234c5df357cd..0625ae4828625 100644 --- a/x-pack/plugins/infra/server/lib/metrics/index.ts +++ b/x-pack/plugins/infra/server/lib/metrics/index.ts @@ -63,42 +63,46 @@ export const query = async ( }, }; - const response = await search<{}, MetricsESResponse>(params); + try { + const response = await search<{}, MetricsESResponse>(params); - if (response.hits.total.value === 0) { - return EMPTY_RESPONSE; - } + if (response.hits.total.value === 0) { + return EMPTY_RESPONSE; + } - if (!response.aggregations) { - throw new Error('Aggregations should be present.'); - } + if (!response.aggregations) { + throw new Error('Aggregations should be present.'); + } - const { bucketSize } = calculateBucketSize({ ...options.timerange, interval }); + const { bucketSize } = calculateBucketSize({ ...options.timerange, interval }); - if (hasGroupBy) { - const aggregations = decodeOrThrow(CompositeResponseRT)(response.aggregations); - const { groupings } = aggregations; - const limit = options.limit ?? DEFAULT_LIMIT; - const returnAfterKey = !!groupings.after_key && groupings.buckets.length === limit; - const afterKey = returnAfterKey ? groupings.after_key : null; + if (hasGroupBy) { + const aggregations = decodeOrThrow(CompositeResponseRT)(response.aggregations); + const { groupings } = aggregations; + const limit = options.limit ?? DEFAULT_LIMIT; + const returnAfterKey = !!groupings.after_key && groupings.buckets.length === limit; + const afterKey = returnAfterKey ? groupings.after_key : null; + + return { + series: getSeriesFromCompositeAggregations(groupings, options, bucketSize * 1000), + info: { + afterKey, + interval: rawOptions.includeTimeseries ? bucketSize : undefined, + }, + }; + } + const aggregations = decodeOrThrow(AggregationResponseRT)(response.aggregations); return { - series: getSeriesFromCompositeAggregations(groupings, options, bucketSize * 1000), + series: getSeriesFromHistogram(aggregations, options, bucketSize * 1000), info: { - afterKey, - interval: rawOptions.includeTimeseries ? bucketSize : undefined, + afterKey: null, + interval: bucketSize, }, }; + } catch (e) { + throw e; } - - const aggregations = decodeOrThrow(AggregationResponseRT)(response.aggregations); - return { - series: getSeriesFromHistogram(aggregations, options, bucketSize * 1000), - info: { - afterKey: null, - interval: bucketSize, - }, - }; }; const getSeriesFromHistogram = ( diff --git a/x-pack/plugins/infra/server/lib/metrics/types.ts b/x-pack/plugins/infra/server/lib/metrics/types.ts index 5399d182f4185..0c87e8eca47d9 100644 --- a/x-pack/plugins/infra/server/lib/metrics/types.ts +++ b/x-pack/plugins/infra/server/lib/metrics/types.ts @@ -68,6 +68,7 @@ export const BucketRT = rt.record( MetricValueTypeRT, TermsWithMetrics, rt.record(rt.string, rt.string), + rt.type({ doc_count: rt.number }), ]) ); diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/convert_metric_to_metrics_api_metric.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/convert_metric_to_metrics_api_metric.ts index a1c9791bc20a4..299bc75f03114 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/convert_metric_to_metrics_api_metric.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/convert_metric_to_metrics_api_metric.ts @@ -5,8 +5,10 @@ * 2.0. */ +import { isEmpty } from 'lodash'; import { networkTraffic } from '../../../../common/inventory_models/shared/metrics/snapshot/network_traffic'; import { MetricsAPIMetric, MetricsExplorerMetric } from '../../../../common/http_api'; +import { createCustomMetricsAggregations } from '../../../lib/create_custom_metrics_aggregations'; export const convertMetricToMetricsAPIMetric = ( metric: MetricsExplorerMetric, @@ -63,4 +65,18 @@ export const convertMetricToMetricsAPIMetric = ( }, }; } + + if (metric.aggregation === 'custom' && metric.custom_metrics) { + const customMetricAggregations = createCustomMetricsAggregations( + id, + metric.custom_metrics, + metric.equation + ); + if (!isEmpty(customMetricAggregations)) { + return { + id, + aggregations: customMetricAggregations, + }; + } + } }; diff --git a/x-pack/plugins/infra/server/utils/get_original_action_group.ts b/x-pack/plugins/infra/server/utils/get_original_action_group.ts new file mode 100644 index 0000000000000..8054935a03e10 --- /dev/null +++ b/x-pack/plugins/infra/server/utils/get_original_action_group.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 { ALERT_ACTION_GROUP } from '@kbn/rule-data-utils'; + +export const getOriginalActionGroup = ( + alertHits: Array<{ [id: string]: any }> | null | undefined +) => { + const source = alertHits && alertHits.length > 0 ? alertHits[0]._source : undefined; + return source?.[ALERT_ACTION_GROUP]; +}; diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/util.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/util.ts index 4c310b5efa5d3..1d3301bd050e6 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/util.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/util.ts @@ -107,7 +107,7 @@ export function getOperationParams( }, {}); } -function getTypeI18n(type: string) { +export function getTypeI18n(type: string) { if (type === 'number') { return i18n.translate('xpack.lens.formula.number', { defaultMessage: 'number' }); } diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts index 0951f950310cb..364103ed3e365 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts @@ -23,6 +23,7 @@ import { findMathNodes, findVariables, getOperationParams, + getTypeI18n, getValueOrName, groupArgsByType, isMathNode, @@ -121,6 +122,8 @@ export interface ErrorWrapper { severity?: 'error' | 'warning'; } +const DEFAULT_RETURN_TYPE = getTypeI18n('number'); + function getNodeLocation(node: TinymathFunction): TinymathLocation[] { return [node.location].filter(nonNullable); } @@ -131,11 +134,11 @@ function getArgumentType(arg: TinymathAST, operations: Record void) => void ): Promise { - const featureCollection = (this._descriptor as GeojsonFileSourceDescriptor).__featureCollection; + const featureCollection = this.getFeatureCollection(); return getFeatureCollectionBounds(featureCollection, false); } async getGeoJsonWithMeta(): Promise { return { - data: (this._descriptor as GeojsonFileSourceDescriptor).__featureCollection, + data: this.getFeatureCollection(), meta: {}, }; } @@ -130,6 +130,10 @@ export class GeoJsonFileSource extends AbstractVectorSource { areResultsTrimmed: (this._descriptor as GeojsonFileSourceDescriptor).areResultsTrimmed, }; } + + getFeatureCollection() { + return (this._descriptor as GeojsonFileSourceDescriptor).__featureCollection; + } } registerSource({ diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts index 584bdd0160b8c..dcd6a727238d5 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts @@ -33,12 +33,16 @@ import { getDataFilters, getTimeFilters, getQueryableUniqueIndexPatternIds, + getSpatialFiltersLayer, } from './map_selectors'; import { LayerDescriptor, VectorLayerDescriptor } from '../../common/descriptor_types'; +import { buildGeoShapeFilter } from '../../common/elasticsearch_util'; import { ILayer } from '../classes/layers/layer'; import { Filter } from '@kbn/es-query'; import { ESSearchSource } from '../classes/sources/es_search_source'; +import { GeoJsonFileSource } from '../classes/sources/geojson_file_source'; +import { getDefaultMapSettings } from '../reducers/map/default_map_settings'; describe('getDataFilters', () => { const mapExtent = { @@ -282,3 +286,62 @@ describe('getQueryableUniqueIndexPatternIds', () => { ).toEqual(['foo', 'fbr']); }); }); + +describe('getSpatialFiltersLayer', () => { + test('should include filters and embeddable search filters', () => { + const embeddableSearchContext = { + filters: [ + buildGeoShapeFilter({ + geometry: { + coordinates: [ + [ + [1, 0], + [1, 1], + [0, 1], + [0, 0], + [1, 0], + ], + ], + type: 'Polygon', + }, + geometryLabel: 'myShape', + geoFieldNames: ['geo.coordinates'], + }), + ], + }; + const geoJsonVectorLayer = getSpatialFiltersLayer.resultFunc( + [ + buildGeoShapeFilter({ + geometry: { + coordinates: [ + [ + [-101.21639, 48.1413], + [-101.21639, 41.84905], + [-90.95149, 41.84905], + [-90.95149, 48.1413], + [-101.21639, 48.1413], + ], + ], + type: 'Polygon', + }, + geometryLabel: 'myShape', + geoFieldNames: ['geo.coordinates'], + }), + ], + embeddableSearchContext, + getDefaultMapSettings() + ); + expect(geoJsonVectorLayer.isVisible()).toBe(true); + expect( + (geoJsonVectorLayer.getSource() as GeoJsonFileSource).getFeatureCollection().features.length + ).toBe(2); + }); + + test('should not show layer when showSpatialFilters is false', () => { + const geoJsonVectorLayer = getSpatialFiltersLayer.resultFunc([], undefined, { + ...getDefaultMapSettings(), + showSpatialFilters: false, + }); + expect(geoJsonVectorLayer.isVisible()).toBe(false); + }); +}); diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index 1d46ef3015046..e8aae1c61dda1 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -281,11 +281,15 @@ export const getDataFilters = createSelector( export const getSpatialFiltersLayer = createSelector( getFilters, + getEmbeddableSearchContext, getMapSettings, - (filters, settings) => { + (filters, embeddableSearchContext, settings) => { const featureCollection: FeatureCollection = { type: 'FeatureCollection', - features: extractFeaturesFromFilters(filters), + features: extractFeaturesFromFilters([ + ...filters, + ...(embeddableSearchContext?.filters ?? []), + ]), }; const geoJsonSourceDescriptor = GeoJsonFileSource.createDescriptor({ __featureCollection: featureCollection, diff --git a/x-pack/plugins/ml/common/types/anomalies.ts b/x-pack/plugins/ml/common/types/anomalies.ts index 7fd71a81dfa88..1bc5ac86ff79a 100644 --- a/x-pack/plugins/ml/common/types/anomalies.ts +++ b/x-pack/plugins/ml/common/types/anomalies.ts @@ -228,6 +228,10 @@ export interface AnomalyRecordDoc { * Indicates a reduction of anomaly score if the bucket contains fewer samples than historically expected. */ incomplete_bucket_penalty?: boolean; + /** + * Indicates whether the prior distribution of the observed time series is multi-modal or has a single mode. + */ + multimodal_distribution?: boolean; }; } diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details_utils.tsx b/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details_utils.tsx index c0229af5683ac..fd798f53fd68b 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details_utils.tsx +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details_utils.tsx @@ -488,6 +488,31 @@ export const AnomalyExplanationDetails: FC<{ anomaly: AnomaliesTableRecord }> = description: explanation.high_variance_penalty ? yes : no, }); } + if (explanation.multimodal_distribution !== undefined) { + impactDetails.push({ + title: ( + + + + + + + ), + description: explanation.multimodal_distribution ? yes : no, + }); + } if (explanation.incomplete_bucket_penalty !== undefined) { impactDetails.push({ title: ( diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx index 39dbd1d168179..ac21c17b6187e 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx @@ -7,7 +7,7 @@ import { cloneDeep } from 'lodash'; import moment from 'moment'; -import rison, { RisonValue } from '@kbn/rison'; +import rison from '@kbn/rison'; import React, { FC, useEffect, useMemo, useState } from 'react'; import { APP_ID as MAPS_APP_ID } from '@kbn/maps-plugin/common'; import { @@ -561,13 +561,15 @@ export const LinksMenuUI = (props: LinksMenuProps) => { }, }); - const appStateProps: RisonValue = { + const appStateProps = { index: dataViewId, filters: getFiltersForDSLQuery(job.datafeed_config.query, dataViewId, job.job_id), + ...(query !== null + ? { + query, + } + : {}), }; - if (query !== null) { - appStateProps.query = query; - } const _a = rison.encode(appStateProps); // Need to encode the _a parameter as it will contain characters such as '+' if using the regex. diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.test.tsx b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.test.tsx index c1857979e7d53..28cf8e7c6ffd2 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.test.tsx +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.test.tsx @@ -12,8 +12,12 @@ import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { euiLightVars as euiThemeLight } from '@kbn/ui-theme'; +import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock'; + import { ScatterplotMatrix } from './scatterplot_matrix'; +const mockFilterManager = createFilterManagerMock(); + const mockEsSearch = jest.fn((body) => ({ hits: { hits: [{ fields: { x: [1], y: [2] } }, { fields: { x: [2], y: [3] } }] }, })); @@ -21,6 +25,26 @@ jest.mock('../../contexts/kibana', () => ({ useMlApiContext: () => ({ esSearch: mockEsSearch, }), + useMlKibana: () => ({ + services: { + application: { + navigateToApp: jest.fn(), + }, + data: { + query: { + filterManager: mockFilterManager, + timefilter: { + timefilter: { + getTime: jest.fn(() => { + return { from: '', to: '' }; + }), + getRefreshInterval: jest.fn(), + }, + }, + }, + }, + }, + }), })); const mockEuiTheme = euiThemeLight; diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx index e822d2ebd91d7..6e718e0f0ccd8 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import React, { useMemo, useEffect, useState, FC } from 'react'; - +import React, { useMemo, useEffect, useState, FC, useCallback } from 'react'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import rison from '@kbn/rison'; import { EuiCallOut, @@ -17,12 +17,14 @@ import { EuiFlexItem, EuiFormRow, EuiIconTip, + EuiLink, EuiSelect, EuiSpacer, EuiSwitch, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { Query } from '@kbn/data-plugin/common/query'; import { DataView } from '@kbn/data-views-plugin/public'; import { stringHash } from '@kbn/ml-string-hash'; @@ -31,7 +33,7 @@ import { isRuntimeMappings } from '../../../../common/util/runtime_field_utils'; import { RuntimeMappings } from '../../../../common/types/fields'; import { getCombinedRuntimeMappings } from '../data_grid'; -import { useMlApiContext } from '../../contexts/kibana'; +import { useMlApiContext, useMlKibana } from '../../contexts/kibana'; import { getProcessedFields } from '../data_grid'; import { useCurrentEuiTheme } from '../color_range_legend'; @@ -101,6 +103,7 @@ export interface ScatterplotMatrixProps { searchQuery?: estypes.QueryDslQueryContainer; runtimeMappings?: RuntimeMappings; indexPattern?: DataView; + query?: Query; } export const ScatterplotMatrix: FC = ({ @@ -112,9 +115,13 @@ export const ScatterplotMatrix: FC = ({ searchQuery, runtimeMappings, indexPattern, + query, }) => { const { esSearch } = useMlApiContext(); - + const kibana = useMlKibana(); + const { + services: { application, data }, + } = kibana; // dynamicSize is optionally used for outlier charts where the scatterplot marks // are sized according to outlier_score const [dynamicSize, setDynamicSize] = useState(false); @@ -142,6 +149,8 @@ export const ScatterplotMatrix: FC = ({ { items: any[]; backgroundItems: any[]; columns: string[]; messages: string[] } | undefined >(); + const { euiTheme } = useCurrentEuiTheme(); + // formats the array of field names for EuiComboBox const fieldOptions = useMemo( () => @@ -172,7 +181,77 @@ export const ScatterplotMatrix: FC = ({ setDynamicSize(!dynamicSize); }; - const { euiTheme } = useCurrentEuiTheme(); + const getCustomVisualizationLink = useCallback(() => { + const { columns } = splom!; + const outlierScoreField = + resultsField !== undefined ? `${resultsField}.${OUTLIER_SCORE_FIELD}` : undefined; + const vegaSpec = getScatterplotMatrixVegaLiteSpec( + true, + [], + [], + columns, + euiTheme, + resultsField, + color, + legendType, + dynamicSize + ); + + vegaSpec.$schema = 'https://vega.github.io/schema/vega-lite/v5.json'; + vegaSpec.title = `Scatterplot matrix for ${index}`; + + const fieldsToFetch = [ + ...columns, + // Add outlier_score field in fetch if it's available so custom visualization can use it + ...(outlierScoreField ? [outlierScoreField] : []), + // Add field to color code by in fetch so custom visualization can use it - usually for classfication jobs + ...(color ? [color] : []), + ]; + + vegaSpec.data = { + url: { + '%context%': true, + ...(indexPattern?.timeFieldName + ? { ['%timefield%']: `${indexPattern?.timeFieldName}` } + : {}), + index, + body: { + fields: fieldsToFetch, + size: fetchSize, + _source: false, + }, + }, + format: { property: 'hits.hits' }, + }; + + const globalState = encodeURIComponent( + rison.encode({ + filters: data.query.filterManager.getFilters(), + refreshInterval: data.query.timefilter.timefilter.getRefreshInterval(), + time: data.query.timefilter.timefilter.getTime(), + }) + ); + + const appState = encodeURIComponent( + rison.encode({ + filters: [], + linked: false, + query, + uiState: {}, + vis: { + aggs: [], + params: { + spec: JSON.stringify(vegaSpec, null, 2), + }, + }, + }) + ); + + const basePath = `/create?type=vega&_g=${globalState}&_a=${appState}`; + + return { path: basePath }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [splom]); useEffect(() => { if (fields.length === 0) { @@ -316,6 +395,7 @@ export const ScatterplotMatrix: FC = ({ const { items, backgroundItems, columns } = splom; return getScatterplotMatrixVegaLiteSpec( + false, items, backgroundItems, columns, @@ -442,6 +522,29 @@ export const ScatterplotMatrix: FC = ({ )} + {splom ? ( + + { + const customVisLink = getCustomVisualizationLink(); + await application.navigateToApp('visualize#', { + path: customVisLink.path, + openInNewTab: false, + }); + }} + data-test-subj="mlSplomoExploreInCustomVisualizationLink" + > + + + + ) : null}
{splom.messages.length > 0 && ( diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.test.ts b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.test.ts index 0fbe08dd24af7..dfa2389c6e0a5 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.test.ts +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.test.ts @@ -26,7 +26,7 @@ import { describe('getColorSpec()', () => { it('should return only user selection conditions and the default color for non-outlier specs', () => { - const colorSpec = getColorSpec(euiThemeLight); + const colorSpec = getColorSpec(false, euiThemeLight); expect(colorSpec).toEqual({ condition: [{ selection: USER_SELECTION }, { selection: SINGLE_POINT_CLICK }], @@ -35,7 +35,7 @@ describe('getColorSpec()', () => { }); it('should return user selection condition and conditional spec for outliers', () => { - const colorSpec = getColorSpec(euiThemeLight, 'outlier_score'); + const colorSpec = getColorSpec(false, euiThemeLight, 'outlier_score'); expect(colorSpec).toEqual({ condition: { @@ -53,7 +53,13 @@ describe('getColorSpec()', () => { it('should return user selection condition and a field based spec for non-outlier specs with legendType supplied', () => { const colorName = 'the-color-field'; - const colorSpec = getColorSpec(euiThemeLight, undefined, colorName, LEGEND_TYPES.NOMINAL); + const colorSpec = getColorSpec( + false, + euiThemeLight, + undefined, + colorName, + LEGEND_TYPES.NOMINAL + ); expect(colorSpec).toEqual({ condition: { @@ -70,10 +76,18 @@ describe('getColorSpec()', () => { }); describe('getScatterplotMatrixVegaLiteSpec()', () => { + const forCustomLink = false; + it('should return the default spec for non-outliers without a legend', () => { const data = [{ x: 1, y: 1 }]; - const vegaLiteSpec = getScatterplotMatrixVegaLiteSpec(data, [], ['x', 'y'], euiThemeLight); + const vegaLiteSpec = getScatterplotMatrixVegaLiteSpec( + forCustomLink, + data, + [], + ['x', 'y'], + euiThemeLight + ); const specForegroundLayer = vegaLiteSpec.spec.layer[0]; // A valid Vega Lite spec shouldn't throw an error when compiled. @@ -103,6 +117,7 @@ describe('getScatterplotMatrixVegaLiteSpec()', () => { const data = [{ x: 1, y: 1 }]; const vegaLiteSpec = getScatterplotMatrixVegaLiteSpec( + forCustomLink, data, [], ['x', 'y'], @@ -151,6 +166,7 @@ describe('getScatterplotMatrixVegaLiteSpec()', () => { const data = [{ x: 1, y: 1 }]; const vegaLiteSpec = getScatterplotMatrixVegaLiteSpec( + forCustomLink, data, [], ['x', 'y'], @@ -196,6 +212,7 @@ describe('getScatterplotMatrixVegaLiteSpec()', () => { const data = [{ ['x.a']: 1, ['y[a]']: 1 }]; const vegaLiteSpec = getScatterplotMatrixVegaLiteSpec( + forCustomLink, data, [], ['x.a', 'y[a]'], diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.ts b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.ts index de29332f57ef6..31ec6403b8fd0 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.ts +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.ts @@ -29,8 +29,10 @@ export const COLOR_SELECTION = euiPaletteColorBlind()[2]; export const COLOR_RANGE_OUTLIER = [euiPaletteColorBlind()[1], euiPaletteColorBlind()[2]]; export const COLOR_RANGE_NOMINAL = euiPaletteColorBlind({ rotations: 2 }); export const COLOR_RANGE_QUANTITATIVE = euiPalettePositive(5); +const CUSTOM_VIS_FIELDS_PATH = 'fields'; export const getColorSpec = ( + forCustomVisLink: boolean, euiTheme: typeof euiThemeLight, escapedOutlierScoreField?: string, color?: string, @@ -58,7 +60,10 @@ export const getColorSpec = ( return { condition: { selection: USER_SELECTION, - field: getEscapedVegaFieldName(color ?? '00FF00'), + field: `${forCustomVisLink ? `${CUSTOM_VIS_FIELDS_PATH}.` : ''}${getEscapedVegaFieldName( + color ?? '00FF00' + // When creating the custom link - this field is returned in an array so we need to access it + )}${forCustomVisLink ? '[0]' : ''}`, type: legendType, scale: { range: @@ -76,6 +81,7 @@ export const getColorSpec = ( }; const getVegaSpecLayer = ( + forCustomVisLink: boolean, isBackground: boolean, values: VegaValue[], colorSpec: any, @@ -117,7 +123,8 @@ const getVegaSpecLayer = ( }; return { - data: { values: [...values] }, + // Don't need to add static data for custom vis links + ...(forCustomVisLink ? {} : { data: { values: [...values] } }), mark: { ...(outliers && dynamicSize ? { @@ -133,7 +140,9 @@ const getVegaSpecLayer = ( ? { transform: [ { - calculate: `datum['${escapedOutlierScoreField}'] >= mlOutlierScoreThreshold.cutoff`, + calculate: `datum${ + forCustomVisLink ? `.${CUSTOM_VIS_FIELDS_PATH}` : '' + }['${escapedOutlierScoreField}'] >= mlOutlierScoreThreshold.cutoff`, as: 'is_outlier', }, ], @@ -154,7 +163,9 @@ const getVegaSpecLayer = ( opacity: { condition: { value: 1, - test: `(datum['${escapedOutlierScoreField}'] >= mlOutlierScoreThreshold.cutoff)`, + test: `(datum${ + forCustomVisLink ? `.${CUSTOM_VIS_FIELDS_PATH}` : '' + }['${escapedOutlierScoreField}'] >= mlOutlierScoreThreshold.cutoff)`, }, value: 0.5, }, @@ -162,19 +173,27 @@ const getVegaSpecLayer = ( : {}), ...(outliers ? { - order: { field: escapedOutlierScoreField }, + order: { + field: `${ + forCustomVisLink ? `${CUSTOM_VIS_FIELDS_PATH}.` : '' + }${escapedOutlierScoreField}`, + }, size: { ...(!dynamicSize ? { condition: { value: 40, - test: `(datum['${escapedOutlierScoreField}'] >= mlOutlierScoreThreshold.cutoff)`, + test: `(datum${ + forCustomVisLink ? `.${CUSTOM_VIS_FIELDS_PATH}` : '' + }['${escapedOutlierScoreField}'] >= mlOutlierScoreThreshold.cutoff)`, }, value: 8, } : { type: LEGEND_TYPES.QUANTITATIVE, - field: escapedOutlierScoreField, + field: `${ + forCustomVisLink ? `${CUSTOM_VIS_FIELDS_PATH}.` : '' + }${escapedOutlierScoreField}`, scale: { type: 'linear', range: [8, 200], @@ -197,7 +216,14 @@ const getVegaSpecLayer = ( tooltip: [ ...(color !== undefined ? // @ts-ignore - [{ type: colorSpec.condition.type, field: getEscapedVegaFieldName(color) }] + [ + { + type: colorSpec.condition.type, + field: `${ + forCustomVisLink ? `${CUSTOM_VIS_FIELDS_PATH}.` : '' + }${getEscapedVegaFieldName(color)}`, + }, + ] : []), ...vegaColumns.map((d) => ({ type: LEGEND_TYPES.QUANTITATIVE, @@ -207,7 +233,9 @@ const getVegaSpecLayer = ( ? [ { type: LEGEND_TYPES.QUANTITATIVE, - field: escapedOutlierScoreField, + field: `${ + forCustomVisLink ? `${CUSTOM_VIS_FIELDS_PATH}.` : '' + }${escapedOutlierScoreField}`, format: '.3f', }, ] @@ -215,21 +243,22 @@ const getVegaSpecLayer = ( ], }, ...(isBackground ? {} : selection), - width: SCATTERPLOT_SIZE, - height: SCATTERPLOT_SIZE, + ...(forCustomVisLink ? {} : { width: SCATTERPLOT_SIZE }), + ...(forCustomVisLink ? {} : { height: SCATTERPLOT_SIZE }), }; }; // Escapes the characters .[] in field names with double backslashes // since VEGA treats dots/brackets in field names as nested values. // See https://vega.github.io/vega-lite/docs/field.html for details. -function getEscapedVegaFieldName(fieldName: string) { - return fieldName.replace(/([\.|\[|\]])/g, '\\$1'); +function getEscapedVegaFieldName(fieldName: string, prependString: string = '') { + return `${prependString}${fieldName.replace(/([\.|\[|\]])/g, '\\$1')}`; } type VegaValue = Record; export const getScatterplotMatrixVegaLiteSpec = ( + forCustomVisLink: boolean, values: VegaValue[], backgroundValues: VegaValue[], columns: string[], @@ -240,12 +269,15 @@ export const getScatterplotMatrixVegaLiteSpec = ( dynamicSize?: boolean ): TopLevelSpec => { const vegaValues = values; - const vegaColumns = columns.map(getEscapedVegaFieldName); + const vegaColumns = columns.map((column) => + getEscapedVegaFieldName(column, forCustomVisLink ? 'fields.' : '') + ); const outliers = resultsField !== undefined; const escapedOutlierScoreField = `${resultsField}\\.${OUTLIER_SCORE_FIELD}`; const colorSpec = getColorSpec( + forCustomVisLink, euiTheme, resultsField && escapedOutlierScoreField, color, @@ -282,6 +314,7 @@ export const getScatterplotMatrixVegaLiteSpec = ( spec: { layer: [ getVegaSpecLayer( + forCustomVisLink, false, vegaValues, colorSpec, @@ -298,6 +331,7 @@ export const getScatterplotMatrixVegaLiteSpec = ( if (backgroundValues.length) { schema.spec.layer.unshift( getVegaSpecLayer( + forCustomVisLink, true, backgroundValues, colorSpec, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx index c10c3e67be443..6cb860557b719 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx @@ -225,6 +225,7 @@ export const ExplorationPageWrapper: FC = ({ = React.memo(({ jobId }) = index={jobConfig?.dest.index} resultsField={jobConfig?.dest.results_field} searchQuery={searchQuery} + query={query} /> )} {showLegacyFeatureInfluenceFormatCallout && ( diff --git a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx index fcd413b301108..4b482e2de1ed8 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx @@ -55,6 +55,7 @@ const getMockedTimefilter = () => { enableAutoRefreshSelector: jest.fn(), getRefreshInterval: jest.fn(), setRefreshInterval: jest.fn(), + getActiveBounds: jest.fn(), getTime: jest.fn(), isAutoRefreshSelectorEnabled: jest.fn(), isTimeRangeSelectorEnabled: jest.fn(), @@ -68,7 +69,7 @@ const getMockedTimefilter = () => { }; }; -const getMockedDatePickeDependencies = () => { +const getMockedDatePickerDependencies = () => { return { data: { query: { @@ -138,7 +139,7 @@ describe('TimeSeriesExplorerUrlStateManager', () => { render( - + diff --git a/x-pack/plugins/ml/public/ui_actions/apply_entity_filters_action.tsx b/x-pack/plugins/ml/public/ui_actions/apply_entity_filters_action.tsx index 151bee3c8ac90..ec823fd13ea46 100644 --- a/x-pack/plugins/ml/public/ui_actions/apply_entity_filters_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/apply_entity_filters_action.tsx @@ -78,7 +78,11 @@ export function createApplyEntityFieldFiltersAction( const filter = filterManager .getFilters() .find( - (f) => f.meta.key === field.fieldName && f.meta.params.query === field.fieldValue + (f) => + f.meta.key === field.fieldName && + typeof f.meta.params === 'object' && + 'query' in f.meta.params && + f.meta.params.query === field.fieldValue ); if (filter) { filterManager.removeFilter(filter); diff --git a/x-pack/plugins/observability/public/data/slo/slo.ts b/x-pack/plugins/observability/public/data/slo/slo.ts index 6d22027b908fd..047083c28986c 100644 --- a/x-pack/plugins/observability/public/data/slo/slo.ts +++ b/x-pack/plugins/observability/public/data/slo/slo.ts @@ -92,7 +92,7 @@ export const sloList: FindSLOResponse = { }, { ...baseSlo, - id: 'c0f8d669-9177-4706-9098-f397a88173a6', + id: 'c0f8d669-9277-4706-9098-f397a88173a6', summary: buildViolatedSummary(), timeWindow: buildRollingTimeWindow({ duration: '7d' }), }, diff --git a/x-pack/plugins/observability/public/hooks/slo/use_create_slo.ts b/x-pack/plugins/observability/public/hooks/slo/use_create_slo.ts index 31f8202c3c77e..1b810117fa288 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_create_slo.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_create_slo.ts @@ -41,6 +41,9 @@ export function useCreateOrUpdateSlo(): UseCreateOrUpdateSlo { setSuccess(true); } catch (e) { setError(e); + } finally { + setSuccess(false); + setLoading(false); } }, [http] @@ -58,6 +61,9 @@ export function useCreateOrUpdateSlo(): UseCreateOrUpdateSlo { setSuccess(true); } catch (e) { setError(e); + } finally { + setSuccess(false); + setLoading(false); } }, [http] diff --git a/x-pack/plugins/observability/public/hooks/use_create_data_view.ts b/x-pack/plugins/observability/public/hooks/use_create_data_view.ts new file mode 100644 index 0000000000000..e2257ef71ab06 --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/use_create_data_view.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 { useEffect, useState } from 'react'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { useKibana } from '../utils/kibana_react'; + +interface UseCreateDataViewProps { + indexPatternString: string | undefined; +} + +export function useCreateDataView({ indexPatternString }: UseCreateDataViewProps) { + const { dataViews } = useKibana().services; + + const [stateDataView, setStateDataView] = useState(); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + const createDataView = () => + dataViews.create({ + title: indexPatternString, + allowNoIndex: true, + }); + + if (indexPatternString) { + setIsLoading(true); + createDataView() + .then((value) => { + setStateDataView(value); + }) + .finally(() => { + setIsLoading(false); + }); + } + }, [indexPatternString, dataViews]); + + return { dataView: stateDataView, loading: isLoading }; +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/query_builder.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/query_builder.stories.tsx new file mode 100644 index 0000000000000..d2e52fa27bd90 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/query_builder.stories.tsx @@ -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 React from 'react'; +import { ComponentStory } from '@storybook/react'; + +import { FormProvider, useForm } from 'react-hook-form'; +import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator'; +import { QueryBuilder as Component, Props } from './query_builder'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants'; + +export default { + component: Component, + title: 'app/SLO/EditPage/CustomKQL/QueryBuilder', + decorators: [KibanaReactStorybookDecorator], +}; + +const Template: ComponentStory = (props: Props) => { + const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); + return ( + + + + ); +}; + +const defaultProps = { + dataTestSubj: 'dataTestSubj', + indexPatternString: 'log*', + name: 'name' as const, + placeholder: 'Enter something if you dare', +}; + +export const QueryBuilder = Template.bind({}); +QueryBuilder.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/query_builder.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/query_builder.tsx new file mode 100644 index 0000000000000..bf21c120cd01d --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/query_builder.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { Control, Controller, FieldPath } from 'react-hook-form'; +import { EuiFormRow } from '@elastic/eui'; +import { CreateSLOInput } from '@kbn/slo-schema'; +import { QueryStringInput } from '@kbn/unified-search-plugin/public'; +import { useKibana } from '../../../../utils/kibana_react'; +import { useCreateDataView } from '../../../../hooks/use_create_data_view'; + +export interface Props { + control: Control; + dataTestSubj: string; + indexPatternString: string | undefined; + label: string; + name: FieldPath; + placeholder: string; +} + +export function QueryBuilder({ + control, + dataTestSubj, + indexPatternString, + label, + name, + placeholder, +}: Props) { + const { data, dataViews, docLinks, http, notifications, storage, uiSettings, unifiedSearch } = + useKibana().services; + + const { dataView } = useCreateDataView({ indexPatternString }); + + return ( + + ( + { + field.onChange(value.query); + }} + /> + )} + /> + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx index 1c0191057aa11..455e36d2e818e 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx @@ -144,7 +144,7 @@ export function SloEditForm({ slo }: Props) { {watch('indicator.type') === 'sli.kql.custom' ? ( - + ) : null} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx index 257185a787728..29d3386f30ad4 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx @@ -6,18 +6,20 @@ */ import React from 'react'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFormLabel, EuiSuggest } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Control, Controller } from 'react-hook-form'; +import { Control, UseFormWatch } from 'react-hook-form'; import type { CreateSLOInput } from '@kbn/slo-schema'; import { IndexSelection } from './custom_kql/index_selection'; +import { QueryBuilder } from './custom_kql/query_builder'; export interface Props { control: Control; + watch: UseFormWatch; } -export function SloEditFormDefinitionCustomKql({ control }: Props) { +export function SloEditFormDefinitionCustomKql({ control, watch }: Props) { return ( @@ -25,88 +27,64 @@ export function SloEditFormDefinitionCustomKql({ control }: Props) { - - {i18n.translate('xpack.observability.slos.sloEdit.sloDefinition.customKql.queryFilter', { - defaultMessage: 'Query filter', - })} - - ( - KQL} - status="unchanged" - aria-label="Filter query" - data-test-subj="sloFormCustomKqlFilterQueryInput" - placeholder={i18n.translate( - 'xpack.observability.slos.sloEdit.sloDefinition.customKql.customFilter', - { - defaultMessage: 'Custom filter to apply on the index', - } - )} - suggestions={[]} - {...field} - /> + dataTestSubj="sloFormCustomKqlFilterQueryInput" + indexPatternString={watch('indicator.params.index')} + label={i18n.translate( + 'xpack.observability.slos.sloEdit.sloDefinition.customKql.queryFilter', + { + defaultMessage: 'Query filter', + } + )} + name="indicator.params.filter" + placeholder={i18n.translate( + 'xpack.observability.slos.sloEdit.sloDefinition.customKql.customFilter', + { + defaultMessage: 'Custom filter to apply on the index', + } )} /> - - {i18n.translate('xpack.observability.slos.sloEdit.sloDefinition.customKql.goodQuery', { - defaultMessage: 'Good query', - })} - - ( - KQL} - status="unchanged" - aria-label="Good filter" - data-test-subj="sloFormCustomKqlGoodQueryInput" - placeholder={i18n.translate( - 'xpack.observability.slos.sloEdit.sloDefinition.customKql.goodQueryPlaceholder', - { - defaultMessage: 'Define the good events', - } - )} - suggestions={[]} - {...field} - /> + dataTestSubj="sloFormCustomKqlGoodQueryInput" + indexPatternString={watch('indicator.params.index')} + label={i18n.translate( + 'xpack.observability.slos.sloEdit.sloDefinition.customKql.goodQuery', + { + defaultMessage: 'Good query', + } + )} + name="indicator.params.good" + placeholder={i18n.translate( + 'xpack.observability.slos.sloEdit.sloDefinition.customKql.goodQueryPlaceholder', + { + defaultMessage: 'Define the good events', + } )} /> - - {i18n.translate('xpack.observability.slos.sloEdit.sloDefinition.customKql.totalQuery', { - defaultMessage: 'Total query', - })} - - ( - KQL} - status="unchanged" - aria-label="Total filter" - data-test-subj="sloFormCustomKqlTotalQueryInput" - placeholder={i18n.translate( - 'xpack.observability.slos.sloEdit.sloDefinition.customKql.totalQueryPlaceholder', - { - defaultMessage: 'Define the total events', - } - )} - suggestions={[]} - {...field} - /> + dataTestSubj="sloFormCustomKqlTotalQueryInput" + indexPatternString={watch('indicator.params.index')} + label={i18n.translate( + 'xpack.observability.slos.sloEdit.sloDefinition.customKql.totalQuery', + { + defaultMessage: 'Total query', + } + )} + name="indicator.params.total" + placeholder={i18n.translate( + 'xpack.observability.slos.sloEdit.sloDefinition.customKql.totalQueryPlaceholder', + { + defaultMessage: 'Define the total events', + } )} /> diff --git a/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx b/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx index 64e1568904896..e9baaf105e10a 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx @@ -56,7 +56,23 @@ const mockBasePathPrepend = jest.fn(); const mockKibana = () => { useKibanaMock.mockReturnValue({ services: { - application: { navigateToUrl: mockNavigate }, + application: { + navigateToUrl: mockNavigate, + }, + data: { + dataViews: { + find: jest.fn().mockReturnValue([]), + get: jest.fn().mockReturnValue([]), + }, + }, + dataViews: { + create: jest.fn().mockResolvedValue(42), + }, + docLinks: { + links: { + query: {}, + }, + }, http: { basePath: { prepend: mockBasePathPrepend, @@ -64,8 +80,19 @@ const mockKibana = () => { }, notifications: { toasts: { - addSuccess: mockAddSuccess, addError: mockAddError, + addSuccess: mockAddSuccess, + }, + }, + storage: { + get: () => {}, + }, + uiSettings: { + get: () => {}, + }, + unifiedSearch: { + autocomplete: { + hasQuerySuggestions: () => {}, }, }, }, diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index e91d42fb5b438..ac7785cd4921c 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -24,11 +24,11 @@ export function SloList() { const [sort, setSort] = useState('name'); const [indicatorTypeFilter, setIndicatorTypeFilter] = useState([]); - const [deleting, setIsDeleting] = useState(false); + const [isCloningOrDeleting, setIsCloningOrDeleting] = useState(false); const [shouldReload, setShouldReload] = useState(false); const { - loading, + loading: isLoadingSloList, error, sloList: { results: sloList = [], total, perPage }, } = useFetchSloList({ @@ -42,16 +42,19 @@ export function SloList() { useEffect(() => { if (shouldReload) { setShouldReload(false); - setIsDeleting(false); } - }, [shouldReload]); - const handleDeleted = () => { - setShouldReload(true); + if (!isLoadingSloList) { + setIsCloningOrDeleting(false); + } + }, [isLoadingSloList, shouldReload]); + + const handleCloningOrDeleting = () => { + setIsCloningOrDeleting(true); }; - const handleDeleting = () => { - setIsDeleting(true); + const handleClonedOrDeleted = () => { + setShouldReload(true); }; const handlePageClick = (pageNumber: number) => { @@ -79,7 +82,7 @@ export function SloList() { diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.stories.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.stories.tsx index 17d7c74ccc066..9eaec700ba3cf 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.stories.tsx @@ -29,6 +29,10 @@ const Template: ComponentStory = (props: SloListItemProps) => const defaultProps = { slo: buildSlo(), historicalSummary: historicalSummaryData[HEALTHY_ROLLING_SLO], + onCloned: () => {}, + onCloning: () => {}, + onDeleted: () => {}, + onDeleting: () => {}, }; export const SloListItem = Template.bind({}); diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx index 0ea2f14a97352..03d5b5326e3a6 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { EuiButtonIcon, EuiContextMenuItem, @@ -20,15 +20,22 @@ import { i18n } from '@kbn/i18n'; import { HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { useKibana } from '../../../utils/kibana_react'; +import { useCreateOrUpdateSlo } from '../../../hooks/slo/use_create_slo'; import { SloSummary } from './slo_summary'; import { SloDeleteConfirmationModal } from './slo_delete_confirmation_modal'; import { SloBadges } from './badges/slo_badges'; +import { + transformSloResponseToCreateSloInput, + transformValuesToCreateSLOInput, +} from '../../slo_edit/helpers/process_slo_form_values'; import { paths } from '../../../config'; export interface SloListItemProps { slo: SLOWithSummaryResponse; historicalSummary?: HistoricalSummaryResponse[]; historicalSummaryLoading: boolean; + onCloned: () => void; + onCloning: () => void; onDeleted: () => void; onDeleting: () => void; } @@ -37,6 +44,8 @@ export function SloListItem({ slo, historicalSummary = [], historicalSummaryLoading, + onCloned, + onCloning, onDeleted, onDeleting, }: SloListItemProps) { @@ -45,6 +54,8 @@ export function SloListItem({ http: { basePath }, } = useKibana().services; + const { createSlo, loading: isCloning, success: isCloned } = useCreateOrUpdateSlo(); + const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false); const [isDeleting, setIsDeleting] = useState(false); @@ -57,6 +68,15 @@ export function SloListItem({ navigateToUrl(basePath.prepend(paths.observability.sloEdit(slo.id))); }; + const handleClone = () => { + const newSlo = transformValuesToCreateSLOInput( + transformSloResponseToCreateSloInput({ ...slo, name: `[Copy] ${slo.name}` })! + ); + + createSlo(newSlo); + setIsActionsPopoverOpen(false); + }; + const handleDelete = () => { setDeleteConfirmationModalOpen(true); setIsDeleting(true); @@ -73,12 +93,23 @@ export function SloListItem({ onDeleted(); }; + useEffect(() => { + if (isCloning) { + onCloning(); + } + + if (isCloned) { + onCloned(); + } + }, [isCloned, isCloning, onCloned, onCloning]); + return ( {/* CONTENT */} @@ -124,12 +155,32 @@ export function SloListItem({ + {i18n.translate('xpack.observability.slos.slo.item.actions.edit', { defaultMessage: 'Edit', })} , - + + {i18n.translate('xpack.observability.slos.slo.item.actions.clone', { + defaultMessage: 'Clone', + })} + , + {i18n.translate('xpack.observability.slos.slo.item.actions.delete', { defaultMessage: 'Delete', })} diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.stories.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.stories.tsx index 560353e483960..d51a454d002b9 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.stories.tsx @@ -24,6 +24,8 @@ const defaultProps: Props = { sloList: sloList.results, loading: false, error: false, + onCloned: () => {}, + onCloning: () => {}, onDeleted: () => {}, onDeleting: () => {}, }; diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.tsx index 71ba38092e6f7..c911d8ab19774 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.tsx @@ -17,11 +17,21 @@ export interface Props { sloList: SLOWithSummaryResponse[]; loading: boolean; error: boolean; + onCloned: () => void; + onCloning: () => void; onDeleted: () => void; onDeleting: () => void; } -export function SloListItems({ sloList, loading, error, onDeleted, onDeleting }: Props) { +export function SloListItems({ + sloList, + loading, + error, + onCloned, + onCloning, + onDeleted, + onDeleting, +}: Props) { const [sloIds, setSloIds] = useState([]); useEffect(() => { setSloIds(sloList.map((slo) => slo.id)); @@ -45,6 +55,8 @@ export function SloListItems({ sloList, loading, error, onDeleted, onDeleting }: slo={slo} historicalSummary={historicalSummaryBySlo[slo.id]} historicalSummaryLoading={historicalSummaryLoading} + onCloned={onCloned} + onCloning={onCloning} onDeleted={onDeleted} onDeleting={onDeleting} /> diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_summary.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_summary.tsx index e9d90c76ae6a8..029e18e4f38db 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_summary.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_summary.tsx @@ -64,9 +64,9 @@ export function SloSummary({ slo, historicalSummary = [], historicalSummaryLoadi - + - + ({ @@ -29,14 +33,26 @@ jest.mock('../../utils/kibana_react'); jest.mock('../../hooks/use_breadcrumbs'); jest.mock('../../hooks/use_license'); jest.mock('../../hooks/slo/use_fetch_slo_list'); +jest.mock('../../hooks/slo/use_create_slo'); +jest.mock('../../hooks/slo/use_delete_slo'); jest.mock('../../hooks/slo/use_fetch_historical_summary'); const useKibanaMock = useKibana as jest.Mock; const useLicenseMock = useLicense as jest.Mock; const useFetchSloListMock = useFetchSloList as jest.Mock; +const useCreateOrUpdateSloMock = useCreateOrUpdateSlo as jest.Mock; +const useDeleteSloMock = useDeleteSlo as jest.Mock; const useFetchHistoricalSummaryMock = useFetchHistoricalSummary as jest.Mock; +const mockCreateSlo = jest.fn(); +useCreateOrUpdateSloMock.mockReturnValue({ createSlo: mockCreateSlo }); + +const mockDeleteSlo = jest.fn(); +useDeleteSloMock.mockReturnValue({ deleteSlo: mockDeleteSlo }); + const mockNavigate = jest.fn(); +const mockAddSuccess = jest.fn(); +const mockAddError = jest.fn(); const mockKibana = () => { useKibanaMock.mockReturnValue({ @@ -48,6 +64,12 @@ const mockKibana = () => { prepend: jest.fn(), }, }, + notifications: { + toasts: { + addSuccess: mockAddSuccess, + addError: mockAddError, + }, + }, }, }); }; @@ -90,9 +112,12 @@ describe('SLOs Page', () => { }); describe('when the correct license is found', () => { + beforeEach(() => { + useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); + }); + it('renders nothing when the API is loading', async () => { useFetchSloListMock.mockReturnValue({ loading: true, sloList: emptySloList }); - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); const { container } = render(, config); @@ -101,16 +126,15 @@ describe('SLOs Page', () => { it('renders the SLOs Welcome Prompt when the API has finished loading and there are no results', async () => { useFetchSloListMock.mockReturnValue({ loading: false, sloList: emptySloList }); - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(, config); expect(screen.queryByTestId('slosPageWelcomePrompt')).toBeTruthy(); }); - it('renders the SLOs page when the API has finished loading and there are results', async () => { + it('should have a create new SLO button', () => { useFetchSloListMock.mockReturnValue({ loading: false, sloList }); - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); + useFetchHistoricalSummaryMock.mockReturnValue({ loading: false, data: historicalSummaryData, @@ -118,8 +142,96 @@ describe('SLOs Page', () => { render(, config); - expect(screen.queryByTestId('slosPage')).toBeTruthy(); - expect(screen.queryByTestId('sloList')).toBeTruthy(); + expect(screen.getByText('Create new SLO')).toBeTruthy(); + }); + + describe('when API has returned results', () => { + it('renders the SLO list with SLO items', async () => { + useFetchSloListMock.mockReturnValue({ loading: false, sloList }); + + useFetchHistoricalSummaryMock.mockReturnValue({ + loading: false, + data: historicalSummaryData, + }); + + render(, config); + + expect(screen.queryByTestId('slosPage')).toBeTruthy(); + expect(screen.queryByTestId('sloList')).toBeTruthy(); + expect(screen.queryAllByTestId('sloItem')).toBeTruthy(); + expect(screen.queryAllByTestId('sloItem').length).toBe(sloList.results.length); + }); + + it('allows editing an SLO', async () => { + useFetchSloListMock.mockReturnValue({ loading: false, sloList }); + + useFetchHistoricalSummaryMock.mockReturnValue({ + loading: false, + data: historicalSummaryData, + }); + + render(, config); + + screen.getAllByLabelText('Actions').at(0)?.click(); + + await waitForEuiPopoverOpen(); + + const button = screen.getByTestId('sloActionsEdit'); + + expect(button).toBeTruthy(); + + button.click(); + + expect(mockNavigate).toBeCalled(); + }); + + it('allows deleting an SLO', async () => { + useFetchSloListMock.mockReturnValue({ loading: false, sloList }); + + useFetchHistoricalSummaryMock.mockReturnValue({ + loading: false, + data: historicalSummaryData, + }); + + render(, config); + + screen.getAllByLabelText('Actions').at(0)?.click(); + + await waitForEuiPopoverOpen(); + + const button = screen.getByTestId('sloActionsDelete'); + + expect(button).toBeTruthy(); + + button.click(); + + screen.getByTestId('confirmModalConfirmButton').click(); + + expect(mockDeleteSlo).toBeCalledWith(sloList.results.at(0)?.id); + }); + + it('allows cloning an SLO', async () => { + useFetchSloListMock.mockReturnValue({ loading: false, sloList }); + + useFetchHistoricalSummaryMock.mockReturnValue({ + loading: false, + data: historicalSummaryData, + }); + + render(, config); + + screen.getAllByLabelText('Actions').at(0)?.click(); + + await waitForEuiPopoverOpen(); + + const button = screen.getByTestId('sloActionsClone'); + + expect(button).toBeTruthy(); + + button.click(); + + expect(mockCreateSlo).toBeCalled(); + }); }); }); }); diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index 8838457b3a344..17a21f41301b6 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -42,6 +42,7 @@ import { SecurityPluginStart } from '@kbn/security-plugin/public'; import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; import { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { LicensingPluginStart } from '@kbn/licensing-plugin/public'; +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { RuleDetailsLocatorDefinition } from './locators/rule_details'; import { observabilityAppId, observabilityFeatureId, casesPath } from '../common'; import { createLazyObservabilityPageTemplate } from './components/shared'; @@ -107,6 +108,7 @@ export interface ObservabilityPublicPluginsStart { spaces: SpacesPluginStart; triggersActionsUi: TriggersAndActionsUIPublicPluginStart; usageCollection: UsageCollectionSetup; + unifiedSearch: UnifiedSearchPublicPluginStart; home?: HomePublicPluginStart; } diff --git a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx index a0f6c1f7a3192..bf84059db3b6a 100644 --- a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx +++ b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx @@ -11,17 +11,35 @@ export function KibanaReactStorybookDecorator(Story: ComponentType) { return ( {}, + }, charts: { theme: { - useChartsTheme: () => {}, useChartsBaseTheme: () => {}, + useChartsTheme: () => {}, + }, + }, + data: {}, + dataViews: { + create: () => {}, + }, + docLinks: { + links: { + query: {}, + }, + }, + http: { + basePath: { + prepend: (_: string) => '', }, }, - application: { navigateToUrl: () => {} }, - http: { basePath: { prepend: (_: string) => '' } }, - docLinks: { links: { query: {} } }, - notifications: { toasts: {} }, - storage: { get: () => {} }, + notifications: { + toasts: {}, + }, + storage: { + get: () => {}, + }, uiSettings: { get: (setting: string) => { if (setting === 'dateFormat') { @@ -29,6 +47,7 @@ export function KibanaReactStorybookDecorator(Story: ComponentType) { } }, }, + unifiedSearch: {}, }} > diff --git a/x-pack/plugins/observability/server/domain/services/compute_error_budget.test.ts b/x-pack/plugins/observability/server/domain/services/compute_error_budget.test.ts index ec8aec44e78ec..b10d4abb0e33c 100644 --- a/x-pack/plugins/observability/server/domain/services/compute_error_budget.test.ts +++ b/x-pack/plugins/observability/server/domain/services/compute_error_budget.test.ts @@ -169,12 +169,12 @@ describe('computeErrorBudget', () => { it('computes the error budget with rounded values', () => { const slo = createSLO(); const dateRange = toDateRange(slo.timeWindow); - const errorBudget = computeErrorBudget(slo, { good: 333, total: 777, dateRange }); + const errorBudget = computeErrorBudget(slo, { good: 770, total: 777, dateRange }); expect(errorBudget).toEqual({ initial: 0.001, - consumed: 571.428571, // i.e. 57,142% consumed - remaining: 0, + consumed: 9.009009, // i.e. 900.90% consumed + remaining: -8.009009, // i.e. -800.90% remaining isEstimated: false, }); }); @@ -187,7 +187,7 @@ describe('computeErrorBudget', () => { expect(errorBudget).toEqual({ initial: 0.001, consumed: 1000, // i.e. 100,000% consumed - remaining: 0, + remaining: -999, isEstimated: false, }); }); diff --git a/x-pack/plugins/observability/server/domain/services/compute_error_budget.ts b/x-pack/plugins/observability/server/domain/services/compute_error_budget.ts index 61da9597c2db9..3302ac35678ee 100644 --- a/x-pack/plugins/observability/server/domain/services/compute_error_budget.ts +++ b/x-pack/plugins/observability/server/domain/services/compute_error_budget.ts @@ -95,7 +95,7 @@ export function toErrorBudget( return { initial: toHighPrecision(initial), consumed: toHighPrecision(consumed), - remaining: Math.max(toHighPrecision(1 - consumed), 0), + remaining: toHighPrecision(1 - consumed), isEstimated, }; } diff --git a/x-pack/plugins/observability/server/services/slo/__snapshots__/historical_summary_client.test.ts.snap b/x-pack/plugins/observability/server/services/slo/__snapshots__/historical_summary_client.test.ts.snap index a8950a810a9b7..323abf7c24d9f 100644 --- a/x-pack/plugins/observability/server/services/slo/__snapshots__/historical_summary_client.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/__snapshots__/historical_summary_client.test.ts.snap @@ -2,7 +2,7 @@ exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 1`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.004019, "initial": 0.05, @@ -16,7 +16,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 2`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.023374, "initial": 0.05, @@ -30,7 +30,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 3`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.042725, "initial": 0.05, @@ -44,7 +44,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 4`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.06208, "initial": 0.05, @@ -58,7 +58,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 5`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.081433, "initial": 0.05, @@ -72,7 +72,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 6`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.100784, "initial": 0.05, @@ -86,7 +86,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 7`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.120137, "initial": 0.05, @@ -100,7 +100,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 8`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.139494, "initial": 0.05, @@ -114,7 +114,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 9`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.15887, "initial": 0.05, @@ -128,7 +128,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 10`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.1782, "initial": 0.05, @@ -142,7 +142,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 11`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.197546, "initial": 0.05, @@ -156,7 +156,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 12`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.216933, "initial": 0.05, @@ -170,7 +170,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 13`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.236292, "initial": 0.05, @@ -184,7 +184,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 14`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.25563, "initial": 0.05, @@ -198,7 +198,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 15`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.274977, "initial": 0.05, @@ -212,7 +212,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 16`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.294298, "initial": 0.05, @@ -226,7 +226,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 17`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.313653, "initial": 0.05, @@ -240,7 +240,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Occurrences SLOs returns the summary 18`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.333025, "initial": 0.05, @@ -254,7 +254,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 1`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.001344, "initial": 0.05, @@ -268,7 +268,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 2`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.002688, "initial": 0.05, @@ -282,7 +282,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 3`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.004032, "initial": 0.05, @@ -296,7 +296,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 4`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.005376, "initial": 0.05, @@ -310,7 +310,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 5`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.00672, "initial": 0.05, @@ -324,7 +324,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 6`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.008065, "initial": 0.05, @@ -338,7 +338,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 7`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.009409, "initial": 0.05, @@ -352,7 +352,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 8`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.010753, "initial": 0.05, @@ -366,7 +366,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 9`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.012097, "initial": 0.05, @@ -380,7 +380,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 10`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.013441, "initial": 0.05, @@ -394,7 +394,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 11`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.014785, "initial": 0.05, @@ -408,7 +408,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 12`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.016129, "initial": 0.05, @@ -422,7 +422,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 13`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.017473, "initial": 0.05, @@ -436,7 +436,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 14`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.018817, "initial": 0.05, @@ -450,7 +450,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 15`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.020161, "initial": 0.05, @@ -464,7 +464,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 16`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.021505, "initial": 0.05, @@ -478,7 +478,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 17`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.022849, "initial": 0.05, @@ -492,7 +492,7 @@ Object { exports[`FetchHistoricalSummary Calendar Aligned and Timeslices SLOs returns the summary 18`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.024194, "initial": 0.05, @@ -506,7 +506,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 1`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -520,7 +520,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 2`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -534,7 +534,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 3`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -548,7 +548,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 4`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -562,7 +562,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 5`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -576,7 +576,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 6`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -590,7 +590,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 7`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -604,7 +604,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 8`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -618,7 +618,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 9`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -632,7 +632,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 10`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -646,7 +646,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 11`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -660,7 +660,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 12`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -674,7 +674,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 13`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -688,7 +688,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 14`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -702,7 +702,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 15`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -716,7 +716,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 16`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -730,7 +730,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 17`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -744,7 +744,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 18`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -758,7 +758,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 19`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -772,7 +772,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 20`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -786,7 +786,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 21`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -800,7 +800,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 22`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -814,7 +814,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 23`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -828,7 +828,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 24`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -842,7 +842,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 25`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -856,7 +856,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 26`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -870,7 +870,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 27`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -884,7 +884,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 28`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -898,7 +898,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 29`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -912,7 +912,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 30`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -926,7 +926,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 1`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -940,7 +940,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 2`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -954,7 +954,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 3`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -968,7 +968,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 4`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -982,7 +982,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 5`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -996,7 +996,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 6`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1010,7 +1010,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 7`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1024,7 +1024,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 8`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1038,7 +1038,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 9`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1052,7 +1052,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 10`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1066,7 +1066,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 11`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1080,7 +1080,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 12`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1094,7 +1094,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 13`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1108,7 +1108,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 14`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1122,7 +1122,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 15`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1136,7 +1136,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 16`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1150,7 +1150,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 17`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1164,7 +1164,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 18`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1178,7 +1178,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 19`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1192,7 +1192,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 20`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1206,7 +1206,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 21`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1220,7 +1220,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 22`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1234,7 +1234,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 23`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1248,7 +1248,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 24`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1262,7 +1262,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 25`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1276,7 +1276,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 26`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1290,7 +1290,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 27`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1304,7 +1304,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 28`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1318,7 +1318,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 29`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, @@ -1332,7 +1332,7 @@ Object { exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 30`] = ` Object { - "date": Any, + "date": Any, "errorBudget": Object { "consumed": 0.6, "initial": 0.05, diff --git a/x-pack/plugins/observability/server/services/slo/__snapshots__/summary_client.test.ts.snap b/x-pack/plugins/observability/server/services/slo/__snapshots__/summary_client.test.ts.snap index e7b4a068d7ae0..56bb33309ea7d 100644 --- a/x-pack/plugins/observability/server/services/slo/__snapshots__/summary_client.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/__snapshots__/summary_client.test.ts.snap @@ -19,7 +19,7 @@ Object { "consumed": 100, "initial": 0.001, "isEstimated": false, - "remaining": 0, + "remaining": -99, }, "sliValue": 0.9, "status": "VIOLATED", @@ -32,7 +32,7 @@ Object { "consumed": 2, "initial": 0.05, "isEstimated": false, - "remaining": 0, + "remaining": -1, }, "sliValue": 0.9, "status": "VIOLATED", diff --git a/x-pack/plugins/observability/server/services/slo/historical_summary_client.test.ts b/x-pack/plugins/observability/server/services/slo/historical_summary_client.test.ts index b55ca69ed3bb2..766af6be9417f 100644 --- a/x-pack/plugins/observability/server/services/slo/historical_summary_client.test.ts +++ b/x-pack/plugins/observability/server/services/slo/historical_summary_client.test.ts @@ -104,9 +104,14 @@ describe('FetchHistoricalSummary', () => { let esClientMock: ElasticsearchClientMock; beforeEach(() => { + jest.useFakeTimers().setSystemTime(new Date('2023-01-18T15:00:00.000Z')); esClientMock = elasticsearchServiceMock.createElasticsearchClient(); }); + afterAll(() => { + jest.useRealTimers(); + }); + describe('Rolling and Occurrences SLOs', () => { it('returns the summary', async () => { const slo = createSLO({ diff --git a/x-pack/plugins/osquery/public/components/app.tsx b/x-pack/plugins/osquery/public/components/app.tsx index 2864410593cba..51b441a8a9c93 100644 --- a/x-pack/plugins/osquery/public/components/app.tsx +++ b/x-pack/plugins/osquery/public/components/app.tsx @@ -8,11 +8,15 @@ import React from 'react'; import { EuiLoadingElastic, + EuiLoadingSpinner, EuiPage, EuiPageBody, EuiPageContent_Deprecated as EuiPageContent, } from '@elastic/eui'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import useObservable from 'react-use/lib/useObservable'; +import { of } from 'rxjs'; import { Container, Wrapper } from './layouts'; import { OsqueryAppRoutes } from '../routes'; import { useOsqueryIntegrationStatus } from '../common/hooks'; @@ -20,8 +24,9 @@ import { OsqueryAppEmptyState } from './empty_state'; import { MainNavigation } from './main_navigation'; const OsqueryAppComponent = () => { + const { customBranding } = useKibana().services; const { data: osqueryIntegration, isFetched } = useOsqueryIntegrationStatus(); - + const hasCustomBranding = useObservable(customBranding?.hasCustomBranding$ || of(false), false); if (!isFetched) { return ( @@ -33,7 +38,11 @@ const OsqueryAppComponent = () => { color="subdued" hasShadow={false} > - + {hasCustomBranding ? ( + + ) : ( + + )} diff --git a/x-pack/plugins/osquery/server/handlers/action/create_queries.test.ts b/x-pack/plugins/osquery/server/handlers/action/create_queries.test.ts new file mode 100644 index 0000000000000..eabdf998d7b1a --- /dev/null +++ b/x-pack/plugins/osquery/server/handlers/action/create_queries.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 { createDynamicQueries, createQueries } from './create_queries'; +import type { Ecs } from '@kbn/core/server'; +import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; + +describe('create queries', () => { + const defualtQueryParams = { + interval: 3600, + platform: 'linux', + version: '1.0.0', + ecs_mapping: {}, + removed: false, + snapshot: true, + }; + const mockedQueriesParams = { + queries: [ + { + query: 'SELECT * FROM processes where pid={{process.pid}};', + id: 'process_with_params', + ...defualtQueryParams, + }, + { + query: 'SELECT * FROM processes where pid={{process.not-existing}};', + id: 'process_wrong_params', + ...defualtQueryParams, + }, + { + query: 'SELECT * FROM processes;', + id: 'process_no_params', + ...defualtQueryParams, + }, + ], + agent_ids: ['929be3ee-13ee-4219-bcc2-5aa1593e8193'], + alert_ids: ['72ae3004b99b747e26c81ae7e4bd978677ec5973234674fef6e4993fa54c9acc'], + }; + const mockedSingleQueryParams = { + query: 'SELECT * FROM processes where pid={{process.pid}};', + interval: 3600, + id: 'process_with_params', + platform: 'linux', + }; + + // Info: getting queries by index (eg. [1], [0]) because can't compare whole query object due to unique action_id generated. + describe('dynamic', () => { + const pid = 123; + it('if queries length it should return replaced list of queries', async () => { + const queries = await createDynamicQueries( + mockedQueriesParams, + { + process: { + pid, + }, + } as Ecs, + {} as OsqueryAppContext + ); + expect(queries[0].query).toBe(`SELECT * FROM processes where pid=${pid};`); + expect(queries[1].error).toBe( + "This query hasn't been called due to parameter used and its value not found in the alert." + ); + expect(queries[1].query).toBe('SELECT * FROM processes where pid={{process.not-existing}};'); + expect(queries[2].query).toBe('SELECT * FROM processes;'); + }); + it('if single query it should return one replaced query ', async () => { + const queries = await createDynamicQueries( + mockedSingleQueryParams, + { + process: { + pid, + }, + } as Ecs, + {} as OsqueryAppContext + ); + expect(queries[0].query).toBe(`SELECT * FROM processes where pid=${pid};`); + }); + it('if single query with not existing parameter it should return query as it is', async () => { + const queries = await createDynamicQueries( + mockedSingleQueryParams, + { + process: {}, + } as Ecs, + {} as OsqueryAppContext + ); + expect(queries[0].query).toBe('SELECT * FROM processes where pid={{process.pid}};'); + expect(queries[0].error).toBe(undefined); + }); + }); + describe('normal', () => { + const TEST_AGENT = 'test-agent'; + it('if queries length it should return not replaced list of queries with agents', async () => { + const queries = await createQueries( + mockedQueriesParams, + [TEST_AGENT], + {} as OsqueryAppContext + ); + expect(queries[0].query).toBe('SELECT * FROM processes where pid={{process.pid}};'); + expect(queries[0].agents).toContain(TEST_AGENT); + expect(queries[2].query).toBe('SELECT * FROM processes;'); + expect(queries[2].agents).toContain(TEST_AGENT); + }); + + it('if single query should return not replaced query with agents', async () => { + const queries = await createQueries( + mockedSingleQueryParams, + [TEST_AGENT], + {} as OsqueryAppContext + ); + expect(queries[0].query).toBe('SELECT * FROM processes where pid={{process.pid}};'); + expect(queries[0].agents).toContain(TEST_AGENT); + }); + }); +}); diff --git a/x-pack/plugins/rule_registry/scripts/generate_ecs_fieldmap/index.js b/x-pack/plugins/rule_registry/scripts/generate_ecs_fieldmap/index.js index 9f28f87b492c5..b8ed10cda9375 100644 --- a/x-pack/plugins/rule_registry/scripts/generate_ecs_fieldmap/index.js +++ b/x-pack/plugins/rule_registry/scripts/generate_ecs_fieldmap/index.js @@ -54,23 +54,21 @@ async function generate() { {} ); - await Promise.all([ - writeFile( - outputFieldMapFilename, - ` + await writeFile( + outputFieldMapFilename, + ` /* This file is generated by x-pack/plugins/rule_registry/scripts/generate_ecs_fieldmap/index.js, do not manually edit */ - export const ecsFieldMap = ${JSON.stringify(fields, null, 2)} as const + export const ecsFieldMap = ${JSON.stringify(fields, null, 2)} as const - export type EcsFieldMap = typeof ecsFieldMap; - `, - { encoding: 'utf-8' } - ).then(() => { - return exec(`node scripts/eslint --fix ${outputFieldMapFilename}`); - }), - ]); + export type EcsFieldMap = typeof ecsFieldMap; + `, + { encoding: 'utf-8' } + ); + + await exec(`node scripts/eslint --fix ${outputFieldMapFilename}`); } generate().catch((err) => { diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts index ff0ec174b0846..8dfa02b0f93af 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts @@ -594,8 +594,8 @@ export class AlertsClient { let activeAlertCount = 0; let recoveredAlertCount = 0; ( - (responseAlertSum.aggregations?.count as estypes.AggregationsMultiBucketAggregateBase) - .buckets as estypes.AggregationsStringTermsBucketKeys[] + ((responseAlertSum.aggregations?.count as estypes.AggregationsMultiBucketAggregateBase) + ?.buckets as estypes.AggregationsStringTermsBucketKeys[]) ?? [] ).forEach((b) => { if (b.key === ALERT_STATUS_ACTIVE) { activeAlertCount = b.doc_count; diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts index 7e16fcdd8cdf3..e3678e1455527 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts @@ -75,7 +75,7 @@ export interface LifecycleAlertServices< alertWithLifecycle: LifecycleAlertService; getAlertStartedDate: (alertInstanceId: string) => string | null; getAlertUuid: (alertInstanceId: string) => string; - getAlertByAlertUuid: (alertUuid: string) => { [x: string]: any } | null; + getAlertByAlertUuid: (alertUuid: string) => Promise | null>; } export type LifecycleRuleExecutor< diff --git a/x-pack/plugins/rule_registry/server/utils/lifecycle_alert_services.mock.ts b/x-pack/plugins/rule_registry/server/utils/lifecycle_alert_services.mock.ts index 721110db4d6af..9324bcfd76cb4 100644 --- a/x-pack/plugins/rule_registry/server/utils/lifecycle_alert_services.mock.ts +++ b/x-pack/plugins/rule_registry/server/utils/lifecycle_alert_services.mock.ts @@ -37,5 +37,5 @@ export const createLifecycleAlertServicesMock = < alertWithLifecycle: ({ id }) => alertServices.alertFactory.create(id), getAlertStartedDate: jest.fn((id: string) => null), getAlertUuid: jest.fn((id: string) => 'mock-alert-uuid'), - getAlertByAlertUuid: jest.fn((id: string) => null), + getAlertByAlertUuid: jest.fn((id: string) => Promise.resolve(null)), }); diff --git a/x-pack/plugins/security/public/authentication/components/authentication_state_page/authentication_state_page.tsx b/x-pack/plugins/security/public/authentication/components/authentication_state_page/authentication_state_page.tsx index c94137961a502..ee71e5a65fc4e 100644 --- a/x-pack/plugins/security/public/authentication/components/authentication_state_page/authentication_state_page.tsx +++ b/x-pack/plugins/security/public/authentication/components/authentication_state_page/authentication_state_page.tsx @@ -7,12 +7,13 @@ import './authentication_state_page.scss'; -import { EuiIcon, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { EuiIcon, EuiImage, EuiSpacer, EuiTitle } from '@elastic/eui'; import React from 'react'; interface Props { className?: string; title: React.ReactNode; + logo?: string; } export const AuthenticationStatePage: React.FC = (props) => ( @@ -21,7 +22,11 @@ export const AuthenticationStatePage: React.FC = (props) => (
- + {props.logo ? ( + + ) : ( + + )}

{props.title}

diff --git a/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.test.ts b/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.test.ts index b36330bb9f1b1..966efccf0c3ba 100644 --- a/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.test.ts +++ b/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.test.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - jest.mock('./logged_out_page'); import type { AppMount } from '@kbn/core/public'; @@ -56,7 +55,7 @@ describe('loggedOutApp', () => { expect(mockRenderApp).toHaveBeenCalledWith( coreStartMock.i18n, { element: appMountParams.element, theme$: appMountParams.theme$ }, - { basePath: coreStartMock.http.basePath } + { basePath: coreStartMock.http.basePath, customBranding: coreStartMock.customBranding } ); }); }); diff --git a/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.ts b/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.ts index 7abad1d6388fc..1141a9b3ffaca 100644 --- a/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.ts +++ b/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.ts @@ -36,7 +36,7 @@ export const loggedOutApp = Object.freeze({ return renderLoggedOutPage( coreStart.i18n, { element, theme$ }, - { basePath: coreStart.http.basePath } + { basePath: coreStart.http.basePath, customBranding: coreStart.customBranding } ); }, }); diff --git a/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.test.tsx b/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.test.tsx index db23c3231c6ae..48f6d3782f619 100644 --- a/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.test.tsx +++ b/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.test.tsx @@ -8,6 +8,7 @@ import { EuiButton } from '@elastic/eui'; import React from 'react'; +import { customBrandingServiceMock } from '@kbn/core-custom-branding-browser-mocks'; import { coreMock } from '@kbn/core/public/mocks'; import { mountWithIntl } from '@kbn/test-jest-helpers'; @@ -23,7 +24,10 @@ describe('LoggedOutPage', () => { it('points to a base path if `next` parameter is not provided', async () => { const basePathMock = coreMock.createStart({ basePath: '/mock-base-path' }).http.basePath; - const wrapper = mountWithIntl(); + const customBranding = customBrandingServiceMock.createStartContract(); + const wrapper = mountWithIntl( + + ); expect(wrapper.find(EuiButton).prop('href')).toBe('/mock-base-path/'); }); @@ -33,8 +37,11 @@ describe('LoggedOutPage', () => { '/mock-base-path/app/home#/?_g=()' )}`; + const customBranding = customBrandingServiceMock.createStartContract(); const basePathMock = coreMock.createStart({ basePath: '/mock-base-path' }).http.basePath; - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl( + + ); expect(wrapper.find(EuiButton).prop('href')).toBe('/mock-base-path/app/home#/?_g=()'); }); diff --git a/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx b/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx index ac0efab5d58a4..7b077f60cc1e0 100644 --- a/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx +++ b/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx @@ -8,8 +8,14 @@ import { EuiButton } from '@elastic/eui'; import React from 'react'; import ReactDOM from 'react-dom'; +import useObservable from 'react-use/lib/useObservable'; -import type { AppMountParameters, CoreStart, IBasePath } from '@kbn/core/public'; +import type { + AppMountParameters, + CoreStart, + CustomBrandingStart, + IBasePath, +} from '@kbn/core/public'; import { FormattedMessage } from '@kbn/i18n-react'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; @@ -18,9 +24,11 @@ import { AuthenticationStatePage } from '../components'; interface Props { basePath: IBasePath; + customBranding: CustomBrandingStart; } -export function LoggedOutPage({ basePath }: Props) { +export function LoggedOutPage({ basePath, customBranding }: Props) { + const customBrandingValue = useObservable(customBranding.customBranding$); return ( } + logo={customBrandingValue?.logo} > diff --git a/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap b/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap index 9c88774a82263..3cd7e8a92cb93 100644 --- a/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap +++ b/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap @@ -395,3 +395,91 @@ exports[`LoginPage page renders as expected 1`] = `
`; + +exports[`LoginPage page renders with custom branding 1`] = ` +
+
+
+ + + + + +

+ +

+
+ +
+
+
+ + + + + +
+
+`; diff --git a/x-pack/plugins/security/public/authentication/login/login_app.test.ts b/x-pack/plugins/security/public/authentication/login/login_app.test.ts index ed27b118024d3..fd62978aa98b1 100644 --- a/x-pack/plugins/security/public/authentication/login/login_app.test.ts +++ b/x-pack/plugins/security/public/authentication/login/login_app.test.ts @@ -64,6 +64,7 @@ describe('loginApp', () => { { element: appMountParams.element, theme$: appMountParams.theme$ }, { http: coreStartMock.http, + customBranding: coreStartMock.customBranding, notifications: coreStartMock.notifications, fatalErrors: coreStartMock.fatalErrors, loginAssistanceMessage: 'some-message', diff --git a/x-pack/plugins/security/public/authentication/login/login_app.ts b/x-pack/plugins/security/public/authentication/login/login_app.ts index 41fa37a4072af..df944a259e4d7 100644 --- a/x-pack/plugins/security/public/authentication/login/login_app.ts +++ b/x-pack/plugins/security/public/authentication/login/login_app.ts @@ -40,6 +40,7 @@ export const loginApp = Object.freeze({ coreStart.i18n, { element, theme$ }, { + customBranding: coreStart.customBranding, http: coreStart.http, notifications: coreStart.notifications, fatalErrors: coreStart.fatalErrors, diff --git a/x-pack/plugins/security/public/authentication/login/login_page.test.tsx b/x-pack/plugins/security/public/authentication/login/login_page.test.tsx index faf13036ae81e..a9c5d8f57527b 100644 --- a/x-pack/plugins/security/public/authentication/login/login_page.test.tsx +++ b/x-pack/plugins/security/public/authentication/login/login_page.test.tsx @@ -9,7 +9,9 @@ import { EuiFlexItem } from '@elastic/eui'; import { act } from '@testing-library/react'; import { shallow } from 'enzyme'; import React from 'react'; +import { of } from 'rxjs'; +import { customBrandingServiceMock } from '@kbn/core-custom-branding-browser-mocks'; import { coreMock } from '@kbn/core/public/mocks'; import { nextTick } from '@kbn/test-jest-helpers'; @@ -27,6 +29,7 @@ const createLoginState = (options?: Partial) => { enabled: false, providers: [{ type: 'basic', name: 'basic1', usesLoginForm: true }], }, + customBranding: {}, ...options, } as LoginState; }; @@ -41,6 +44,7 @@ describe('LoginPage', () => { httpMock.get.mockReset(); httpMock.addLoadingCountSource.mockReset(); }; + const customBrandingMock = customBrandingServiceMock.createStartContract(); beforeEach(() => { Object.defineProperty(window, 'location', { @@ -54,11 +58,37 @@ describe('LoginPage', () => { describe('page', () => { it('renders as expected', async () => { const coreStartMock = coreMock.createStart(); + customBrandingMock.customBranding$ = of({}); httpMock.get.mockResolvedValue(createLoginState()); const wrapper = shallow( + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + resetHttpMock(); // so the calls don't show in the BasicLoginForm snapshot + }); + + expect(wrapper).toMatchSnapshot(); + }); + + it('renders with custom branding', async () => { + const coreStartMock = coreMock.createStart(); + customBrandingMock.customBranding$ = of({ logo: 'logo' }); + httpMock.get.mockResolvedValue(createLoginState()); + + const wrapper = shallow( + { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); @@ -121,6 +152,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); @@ -142,6 +174,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); @@ -165,6 +198,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); @@ -188,6 +222,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); @@ -219,6 +254,7 @@ describe('LoginPage', () => { fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" sameSiteCookies="Lax" + customBranding={customBrandingMock} /> ); @@ -250,6 +286,7 @@ describe('LoginPage', () => { fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" sameSiteCookies="None" + customBranding={customBrandingMock} /> ); @@ -276,6 +313,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); @@ -299,6 +337,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); @@ -322,6 +361,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); @@ -349,6 +389,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="This is an *important* message" + customBranding={customBrandingMock} /> ); @@ -371,6 +412,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); @@ -395,6 +437,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); @@ -420,6 +463,7 @@ describe('LoginPage', () => { notifications={coreStartMock.notifications} fatalErrors={coreStartMock.fatalErrors} loginAssistanceMessage="" + customBranding={customBrandingMock} /> ); diff --git a/x-pack/plugins/security/public/authentication/login/login_page.tsx b/x-pack/plugins/security/public/authentication/login/login_page.tsx index 4d23d52048575..06cb3507e6b89 100644 --- a/x-pack/plugins/security/public/authentication/login/login_page.tsx +++ b/x-pack/plugins/security/public/authentication/login/login_page.tsx @@ -12,6 +12,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, + EuiImage, EuiSpacer, EuiText, EuiTitle, @@ -19,11 +20,14 @@ import { import classNames from 'classnames'; import React, { Component } from 'react'; import ReactDOM from 'react-dom'; +import type { Subscription } from 'rxjs'; import { BehaviorSubject } from 'rxjs'; +import type { CustomBranding } from '@kbn/core-custom-branding-common'; import type { AppMountParameters, CoreStart, + CustomBrandingStart, FatalErrorsStart, HttpStart, NotificationsStart, @@ -48,10 +52,12 @@ interface Props { fatalErrors: FatalErrorsStart; loginAssistanceMessage: string; sameSiteCookies?: ConfigType['sameSiteCookies']; + customBranding: CustomBrandingStart; } interface State { loginState: LoginState | null; + customBranding: CustomBranding; } const loginFormMessages: Record> = { @@ -83,14 +89,22 @@ const loginFormMessages: Record { - state = { loginState: null } as State; + state = { loginState: null, customBranding: {} } as State; + private customBrandingSubscription?: Subscription; public async componentDidMount() { const loadingCount$ = new BehaviorSubject(1); + this.customBrandingSubscription = this.props.customBranding.customBranding$.subscribe( + (next) => { + this.setState({ ...this.state, customBranding: next }); + } + ); this.props.http.addLoadingCountSource(loadingCount$.asObservable()); try { - this.setState({ loginState: await this.props.http.get('/internal/security/login_state') }); + this.setState({ + loginState: await this.props.http.get('/internal/security/login_state'), + }); } catch (err) { this.props.fatalErrors.add(err as Error); } @@ -99,6 +113,10 @@ export class LoginPage extends Component { loadingCount$.complete(); } + public componentWillUnmount() { + this.customBrandingSubscription?.unsubscribe(); + } + public render() { const loginState = this.state.loginState; if (!loginState) { @@ -122,14 +140,18 @@ export class LoginPage extends Component { ['loginWelcome__contentDisabledForm']: !loginIsSupported, }); + const customLogo = this.state.customBranding?.logo; + const logo = customLogo ? ( + + ) : ( + + ); return (
- - - + {logo}

{ return [ new KibanaFeature({ @@ -132,10 +139,12 @@ function getProps({ action, role, canManageSpaces = true, + spacesEnabled = true, }: { action: 'edit' | 'clone'; role?: Role; canManageSpaces?: boolean; + spacesEnabled?: boolean; }) { const rolesAPIClient = rolesAPIClientMock.create(); rolesAPIClient.getRole.mockResolvedValue(role); @@ -162,6 +171,9 @@ function getProps({ const { fatalErrors } = coreMock.createSetup(); const { http, docLinks, notifications } = coreMock.createStart(); http.get.mockImplementation(async (path: any) => { + if (!spacesEnabled) { + throw { response: { status: 404 } }; // eslint-disable-line no-throw-literal + } if (path === '/api/spaces/space') { return buildSpaces(); } @@ -183,6 +195,7 @@ function getProps({ fatalErrors, uiCapabilities: buildUICapabilities(canManageSpaces), history: scopedHistoryMock.create(), + spacesApiUi, }; } @@ -411,6 +424,194 @@ describe('', () => { }); }); + describe('with spaces disabled', () => { + it('can render readonly view when not enough privileges', async () => { + coreStart.application.capabilities = { + ...coreStart.application.capabilities, + roles: { + save: false, + }, + }; + + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expectReadOnlyFormButtons(wrapper); + }); + + it('can render a reserved role', async () => { + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(1); + expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1); + expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); + expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expectReadOnlyFormButtons(wrapper); + }); + + it('can render a user defined role', async () => { + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0); + expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1); + expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); + expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expectSaveFormButtons(wrapper); + }); + + it('can render when creating a new role', async () => { + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1); + expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); + expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe( + false + ); + expectSaveFormButtons(wrapper); + }); + + it('redirects back to roles when creating a new role without privileges', async () => { + coreStart.application.capabilities = { + ...coreStart.application.capabilities, + roles: { + save: false, + }, + }; + + const props = getProps({ action: 'edit', spacesEnabled: false }); + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(props.history.push).toHaveBeenCalledWith('/'); + }); + + it('can render when cloning an existing role', async () => { + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1); + expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); + expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe( + false + ); + expectSaveFormButtons(wrapper); + }); + + it('renders a partial read-only view when there is a transform error', async () => { + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find(TransformErrorSection)).toHaveLength(1); + expectReadOnlyFormButtons(wrapper); + }); + }); + it('registers fatal error if features endpoint fails unexpectedly', async () => { const error = { response: { status: 500 } }; const getFeatures = jest.fn().mockRejectedValue(error); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index 36d6815493c98..c0459cd157e09 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -470,6 +470,7 @@ export const EditRolePage: FunctionComponent = ({ renders without crashing 1`] = ` }, ] } + spacesApiUi={ + Object { + "components": Object { + "getCopyToSpaceFlyout": [Function], + "getEmbeddableLegacyUrlConflict": [Function], + "getLegacyUrlConflict": [Function], + "getShareToSpaceFlyout": [Function], + "getSpaceAvatar": [Function], + "getSpaceList": [Function], + "getSpacesContextProvider": [Function], + }, + "redirectLegacyUrl": [Function], + "useSpaces": [Function], + } + } uiCapabilities={ Object { "catalogue": Object {}, diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.test.tsx index 5b1d06a741ad2..62627073943f0 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.test.tsx @@ -8,13 +8,22 @@ import { shallow } from 'enzyme'; import React from 'react'; +import { coreMock } from '@kbn/core/public/mocks'; +import { spacesManagerMock } from '@kbn/spaces-plugin/public/spaces_manager/mocks'; +import { getUiApi } from '@kbn/spaces-plugin/public/ui_api'; + import type { Role } from '../../../../../../common/model'; import { KibanaPrivileges } from '../../../model'; import { RoleValidator } from '../../validate_role'; import { KibanaPrivilegesRegion } from './kibana_privileges_region'; +import { SimplePrivilegeSection } from './simple_privilege_section'; import { SpaceAwarePrivilegeSection } from './space_aware_privilege_section'; import { TransformErrorSection } from './transform_error_section'; +const spacesManager = spacesManagerMock.create(); +const { getStartServices } = coreMock.createSetup(); +const spacesApiUi = getUiApi({ spacesManager, getStartServices }); + const buildProps = () => { return { role: { @@ -62,12 +71,15 @@ const buildProps = () => { onChange: jest.fn(), validator: new RoleValidator(), canCustomizeSubFeaturePrivileges: true, + spacesEnabled: true, + spacesApiUi, }; }; describe('', () => { it('renders without crashing', () => { - expect(shallow()).toMatchSnapshot(); + const props = buildProps(); + expect(shallow()).toMatchSnapshot(); }); it('renders the space-aware privilege form', () => { @@ -76,6 +88,12 @@ describe('', () => { expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1); }); + it('renders simple privilege form when spaces is disabled', () => { + const props = buildProps(); + const wrapper = shallow(); + expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1); + }); + it('renders the transform error section when the role has a transform error', () => { const props = buildProps(); (props.role as Role)._transform_error = ['kibana']; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx index 612bcf05aab56..62a1a021c0aae 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx @@ -14,11 +14,13 @@ import type { Role } from '../../../../../../common/model'; import type { KibanaPrivileges } from '../../../model'; import { CollapsiblePanel } from '../../collapsible_panel'; import type { RoleValidator } from '../../validate_role'; +import { SimplePrivilegeSection } from './simple_privilege_section'; import { SpaceAwarePrivilegeSection } from './space_aware_privilege_section'; import { TransformErrorSection } from './transform_error_section'; interface Props { role: Role; + spacesEnabled: boolean; canCustomizeSubFeaturePrivileges: boolean; spaces?: Space[]; uiCapabilities: Capabilities; @@ -42,6 +44,7 @@ export class KibanaPrivilegesRegion extends Component { const { kibanaPrivileges, role, + spacesEnabled, canCustomizeSubFeaturePrivileges, spaces = [], uiCapabilities, @@ -55,17 +58,29 @@ export class KibanaPrivilegesRegion extends Component { return ; } + if (spacesApiUi && spacesEnabled) { + return ( + + ); + } + return ( - ); }; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/__snapshots__/simple_privilege_section.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/__snapshots__/simple_privilege_section.test.tsx.snap new file mode 100644 index 0000000000000..b490dc7cefe26 --- /dev/null +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/__snapshots__/simple_privilege_section.test.tsx.snap @@ -0,0 +1,171 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders without crashing 1`] = ` + + + + +

+ +

+
+
+ + + } + labelType="label" + > + + + + + +

+ +

+
+ , + "inputDisplay": , + "value": "none", + }, + Object { + "dropdownDisplay": + + + + +

+ +

+
+
, + "inputDisplay": , + "value": "read", + }, + Object { + "dropdownDisplay": + + + + +

+ +

+
+
, + "inputDisplay": , + "value": "all", + }, + Object { + "dropdownDisplay": + + + + +

+ +

+
+
, + "inputDisplay": , + "value": "custom", + }, + ] + } + valueOfSelected="none" + /> +
+
+
+
+`; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/index.ts new file mode 100644 index 0000000000000..bea5a3d2d592f --- /dev/null +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/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 { SimplePrivilegeSection } from './simple_privilege_section'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/privilege_selector.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/privilege_selector.tsx new file mode 100644 index 0000000000000..72061958ecc35 --- /dev/null +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/privilege_selector.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 { EuiSelect } from '@elastic/eui'; +import type { ChangeEvent } from 'react'; +import React, { Component } from 'react'; + +import { NO_PRIVILEGE_VALUE } from '../constants'; + +interface Props { + ['data-test-subj']: string; + availablePrivileges: string[]; + onChange: (privilege: string) => void; + value: string | null; + allowNone?: boolean; + disabled?: boolean; + compressed?: boolean; +} + +export class PrivilegeSelector extends Component { + public state = {}; + + public render() { + const { availablePrivileges, value, disabled, allowNone, compressed } = this.props; + + const options = []; + + if (allowNone) { + options.push({ value: NO_PRIVILEGE_VALUE, text: 'none' }); + } + + options.push( + ...availablePrivileges.map((p) => ({ + value: p, + text: p, + })) + ); + + return ( + + ); + } + + public onChange = (e: ChangeEvent) => { + this.props.onChange(e.target.value); + }; +} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx new file mode 100644 index 0000000000000..8f5efff64aadd --- /dev/null +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx @@ -0,0 +1,248 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { EuiButtonGroupProps } from '@elastic/eui'; +import { EuiButtonGroup, EuiComboBox, EuiSuperSelect } from '@elastic/eui'; +import React from 'react'; + +import { mountWithIntl, shallowWithIntl } from '@kbn/test-jest-helpers'; + +import type { Role } from '../../../../../../../common/model'; +import { KibanaPrivileges, SecuredFeature } from '../../../../model'; +import { SimplePrivilegeSection } from './simple_privilege_section'; +import { UnsupportedSpacePrivilegesWarning } from './unsupported_space_privileges_warning'; + +const buildProps = (customProps: any = {}) => { + const features = [ + new SecuredFeature({ + id: 'feature1', + name: 'Feature 1', + app: ['app'], + category: { id: 'foo', label: 'foo' }, + privileges: { + all: { + app: ['app'], + savedObject: { + all: ['foo'], + read: [], + }, + ui: ['app-ui'], + }, + read: { + app: ['app'], + savedObject: { + all: [], + read: [], + }, + ui: ['app-ui'], + }, + }, + }), + ] as SecuredFeature[]; + + const kibanaPrivileges = new KibanaPrivileges( + { + features: { + feature1: { + all: ['*'], + read: ['read'], + }, + }, + global: {}, + space: {}, + reserved: {}, + }, + features + ); + + const role = { + name: '', + elasticsearch: { + cluster: ['manage'], + indices: [], + run_as: [], + }, + kibana: [], + ...customProps.role, + }; + + return { + editable: true, + kibanaPrivileges, + features, + onChange: jest.fn(), + canCustomizeSubFeaturePrivileges: true, + ...customProps, + role, + }; +}; + +describe('', () => { + it('renders without crashing', () => { + expect(shallowWithIntl()).toMatchSnapshot(); + }); + + it('displays "none" when no privilege is selected', () => { + const props = buildProps(); + const wrapper = shallowWithIntl(); + const selector = wrapper.find(EuiSuperSelect); + expect(selector.props()).toMatchObject({ + valueOfSelected: 'none', + }); + expect(wrapper.find(UnsupportedSpacePrivilegesWarning)).toHaveLength(0); + }); + + it('displays "custom" when feature privileges are customized', () => { + const props = buildProps({ + role: { + elasticsearch: {}, + kibana: [ + { + spaces: ['*'], + base: [], + feature: { + feature1: ['foo'], + }, + }, + ], + }, + }); + const wrapper = shallowWithIntl(); + const selector = wrapper.find(EuiSuperSelect); + expect(selector.props()).toMatchObject({ + valueOfSelected: 'custom', + }); + expect(wrapper.find(UnsupportedSpacePrivilegesWarning)).toHaveLength(0); + }); + + it('displays the selected privilege', () => { + const props = buildProps({ + role: { + elasticsearch: {}, + kibana: [ + { + spaces: ['*'], + base: ['read'], + feature: {}, + }, + ], + }, + }); + const wrapper = shallowWithIntl(); + const selector = wrapper.find(EuiSuperSelect); + expect(selector.props()).toMatchObject({ + valueOfSelected: 'read', + }); + expect(wrapper.find(UnsupportedSpacePrivilegesWarning)).toHaveLength(0); + }); + + it('displays the reserved privilege', () => { + const props = buildProps({ + role: { + elasticsearch: {}, + kibana: [ + { + spaces: ['*'], + base: [], + feature: {}, + _reserved: ['foo'], + }, + ], + }, + }); + const wrapper = shallowWithIntl(); + const selector = wrapper.find(EuiComboBox); + expect(selector.props()).toMatchObject({ + isDisabled: true, + selectedOptions: [{ label: 'foo' }], + }); + expect(wrapper.find(UnsupportedSpacePrivilegesWarning)).toHaveLength(0); + }); + + it('fires its onChange callback when the privilege changes', () => { + const props = buildProps(); + const wrapper = mountWithIntl(); + const selector = wrapper.find(EuiSuperSelect); + (selector.props() as any).onChange('all'); + + expect(props.onChange).toHaveBeenCalledWith({ + name: '', + elasticsearch: { + cluster: ['manage'], + indices: [], + run_as: [], + }, + kibana: [{ feature: {}, base: ['all'], spaces: ['*'] }], + }); + expect(wrapper.find(UnsupportedSpacePrivilegesWarning)).toHaveLength(0); + }); + + it('allows feature privileges to be customized', () => { + const props = buildProps({ + onChange: (role: Role) => { + wrapper.setProps({ + role, + }); + }, + }); + const wrapper = mountWithIntl(); + const selector = wrapper.find(EuiSuperSelect); + (selector.props() as any).onChange('custom'); + + wrapper.update(); + + const featurePrivilegeToggles = wrapper.find(EuiButtonGroup); + expect(featurePrivilegeToggles).toHaveLength(1); + expect(featurePrivilegeToggles.find('input')).toHaveLength(3); + + (featurePrivilegeToggles.props() as EuiButtonGroupProps).onChange('feature1_all', null); + + wrapper.update(); + + expect(wrapper.props().role).toEqual({ + elasticsearch: { + cluster: ['manage'], + indices: [], + run_as: [], + }, + kibana: [ + { + base: [], + feature: { + feature1: ['all'], + }, + spaces: ['*'], + }, + ], + name: '', + }); + + expect(wrapper.find(UnsupportedSpacePrivilegesWarning)).toHaveLength(0); + }); + + it('renders a warning when space privileges are found', () => { + const props = buildProps({ + role: { + elasticsearch: {}, + kibana: [ + { + spaces: ['*'], + base: ['read'], + feature: {}, + }, + { + spaces: ['marketing'], + base: ['read'], + feature: {}, + }, + ], + }, + }); + const wrapper = mountWithIntl(); + expect(wrapper.find(UnsupportedSpacePrivilegesWarning)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx new file mode 100644 index 0000000000000..f886de819e144 --- /dev/null +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx @@ -0,0 +1,334 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiComboBox, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiSuperSelect, + EuiText, +} from '@elastic/eui'; +import React, { Component, Fragment } from 'react'; + +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { Role, RoleKibanaPrivilege } from '../../../../../../../common/model'; +import { copyRole } from '../../../../../../../common/model'; +import type { KibanaPrivileges } from '../../../../model'; +import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; +import { CUSTOM_PRIVILEGE_VALUE, NO_PRIVILEGE_VALUE } from '../constants'; +import { FeatureTable } from '../feature_table'; +import { PrivilegeFormCalculator } from '../privilege_form_calculator'; +import { UnsupportedSpacePrivilegesWarning } from './unsupported_space_privileges_warning'; + +interface Props { + role: Role; + kibanaPrivileges: KibanaPrivileges; + onChange: (role: Role) => void; + editable: boolean; + canCustomizeSubFeaturePrivileges: boolean; +} + +interface State { + isCustomizingGlobalPrivilege: boolean; + globalPrivsIndex: number; +} + +export class SimplePrivilegeSection extends Component { + constructor(props: Props) { + super(props); + + const globalPrivs = this.locateGlobalPrivilege(props.role); + const globalPrivsIndex = this.locateGlobalPrivilegeIndex(props.role); + + this.state = { + isCustomizingGlobalPrivilege: Boolean( + globalPrivs && Object.keys(globalPrivs.feature).length > 0 + ), + globalPrivsIndex, + }; + } + public render() { + const kibanaPrivilege = this.getDisplayedBasePrivilege(); + + const reservedPrivileges = this.props.role.kibana[this.state.globalPrivsIndex]?._reserved ?? []; + + const title = ( + + ); + + const description = ( +

+ +

+ ); + + return ( + + + + + {description} + + + + + {reservedPrivileges.length > 0 ? ( + ({ label: rp }))} + isDisabled + /> + ) : ( + + ), + dropdownDisplay: ( + <> + + + + +

+ +

+
+ + ), + }, + { + value: 'read', + inputDisplay: ( + + ), + dropdownDisplay: ( + <> + + + + +

+ +

+
+ + ), + }, + { + value: 'all', + inputDisplay: ( + + ), + dropdownDisplay: ( + <> + + + + +

+ +

+
+ + ), + }, + { + value: CUSTOM_PRIVILEGE_VALUE, + inputDisplay: ( + + ), + dropdownDisplay: ( + <> + + + + +

+ +

+
+ + ), + }, + ]} + hasDividers + valueOfSelected={kibanaPrivilege} + /> + )} +
+ {this.state.isCustomizingGlobalPrivilege && ( + + + isGlobalPrivilegeDefinition(k) + )} + canCustomizeSubFeaturePrivileges={this.props.canCustomizeSubFeaturePrivileges} + allSpacesSelected + disabled={!this.props.editable} + /> + + )} + {this.maybeRenderSpacePrivilegeWarning()} +
+
+
+ ); + } + + public getDisplayedBasePrivilege = () => { + if (this.state.isCustomizingGlobalPrivilege) { + return CUSTOM_PRIVILEGE_VALUE; + } + + const { role } = this.props; + + const form = this.locateGlobalPrivilege(role); + + return form && form.base.length > 0 ? form.base[0] : NO_PRIVILEGE_VALUE; + }; + + public onKibanaPrivilegeChange = (privilege: string) => { + const role = copyRole(this.props.role); + + const form = this.locateGlobalPrivilege(role) || this.createGlobalPrivilegeEntry(role); + + if (privilege === NO_PRIVILEGE_VALUE) { + // Remove global entry if no privilege value + role.kibana = role.kibana.filter((entry) => !isGlobalPrivilegeDefinition(entry)); + } else if (privilege === CUSTOM_PRIVILEGE_VALUE) { + // Remove base privilege if customizing feature privileges + form.base = []; + } else { + form.base = [privilege]; + form.feature = {}; + } + + this.props.onChange(role); + this.setState({ + isCustomizingGlobalPrivilege: privilege === CUSTOM_PRIVILEGE_VALUE, + globalPrivsIndex: role.kibana.indexOf(form), + }); + }; + + public onFeaturePrivilegeChange = (featureId: string, privileges: string[]) => { + const role = copyRole(this.props.role); + const form = this.locateGlobalPrivilege(role) || this.createGlobalPrivilegeEntry(role); + if (privileges.length > 0) { + form.feature[featureId] = [...privileges]; + } else { + delete form.feature[featureId]; + } + this.props.onChange(role); + }; + + private onChangeAllFeaturePrivileges = (privileges: string[]) => { + const role = copyRole(this.props.role); + + const form = this.locateGlobalPrivilege(role) || this.createGlobalPrivilegeEntry(role); + if (privileges.length > 0) { + this.props.kibanaPrivileges.getSecuredFeatures().forEach((feature) => { + form.feature[feature.id] = [...privileges]; + }); + } else { + form.feature = {}; + } + this.props.onChange(role); + }; + + private maybeRenderSpacePrivilegeWarning = () => { + const kibanaPrivileges = this.props.role.kibana; + const hasSpacePrivileges = kibanaPrivileges.some( + (privilege) => !isGlobalPrivilegeDefinition(privilege) + ); + + if (hasSpacePrivileges) { + return ( + + + + ); + } + return null; + }; + + private locateGlobalPrivilegeIndex = (role: Role) => { + return role.kibana.findIndex((privileges) => isGlobalPrivilegeDefinition(privileges)); + }; + + private locateGlobalPrivilege = (role: Role) => { + const spacePrivileges = role.kibana; + return spacePrivileges.find((privileges) => isGlobalPrivilegeDefinition(privileges)); + }; + + private createGlobalPrivilegeEntry(role: Role): RoleKibanaPrivilege { + const newEntry = { + spaces: ['*'], + base: [], + feature: {}, + }; + + role.kibana.push(newEntry); + + return newEntry; + } +} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/unsupported_space_privileges_warning.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/unsupported_space_privileges_warning.tsx new file mode 100644 index 0000000000000..b3350a314349a --- /dev/null +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/unsupported_space_privileges_warning.tsx @@ -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 { EuiCallOut } from '@elastic/eui'; +import React, { Component } from 'react'; + +import { FormattedMessage } from '@kbn/i18n-react'; + +export class UnsupportedSpacePrivilegesWarning extends Component<{}, {}> { + public render() { + return ; + } + + private getMessage = () => { + return ( + + ); + }; +} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx index cc96446a33f2d..60887dee56d97 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx @@ -78,7 +78,7 @@ export class SpaceAwarePrivilegeSection extends Component { public render() { const { uiCapabilities } = this.props; - if (!uiCapabilities.spaces.manage) { + if (!uiCapabilities.spaces?.manage) { return ( ElasticMockedFonts
"`; + +exports[`UnauthenticatedPage renders as expected with custom title 1`] = `"My Company NameMockedFonts"`; diff --git a/x-pack/plugins/security/server/authentication/authentication_service.test.ts b/x-pack/plugins/security/server/authentication/authentication_service.test.ts index 55357be756e7e..5509b68d14c1f 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.test.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.test.ts @@ -12,9 +12,11 @@ import { mockCanRedirectRequest } from './authentication_service.test.mocks'; import { errors } from '@elastic/elasticsearch'; +import { customBrandingServiceMock } from '@kbn/core-custom-branding-server-mocks'; import type { AuthenticationHandler, AuthToolkit, + CustomBrandingSetup, ElasticsearchServiceSetup, HttpServiceSetup, HttpServiceStart, @@ -62,6 +64,7 @@ describe('AuthenticationService', () => { config: ConfigType; license: jest.Mocked; buildNumber: number; + customBranding: jest.Mocked; }; let mockStartAuthenticationParams: { audit: jest.Mocked; @@ -93,6 +96,7 @@ describe('AuthenticationService', () => { }), license: licenseMock.create(), buildNumber: 100500, + customBranding: customBrandingServiceMock.createSetupContract(), }; mockCanRedirectRequest.mockReturnValue(false); diff --git a/x-pack/plugins/security/server/authentication/authentication_service.ts b/x-pack/plugins/security/server/authentication/authentication_service.ts index 881bf0741e361..cbd71b890761f 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.ts @@ -6,6 +6,7 @@ */ import type { + CustomBrandingSetup, ElasticsearchServiceSetup, HttpServiceSetup, HttpServiceStart, @@ -37,6 +38,7 @@ import { renderUnauthenticatedPage } from './unauthenticated_page'; interface AuthenticationServiceSetupParams { http: Pick; + customBranding: CustomBrandingSetup; elasticsearch: Pick; config: ConfigType; license: SecurityLicense; @@ -97,7 +99,14 @@ export class AuthenticationService { constructor(private readonly logger: Logger) {} - setup({ config, http, license, buildNumber, elasticsearch }: AuthenticationServiceSetupParams) { + setup({ + config, + http, + license, + buildNumber, + elasticsearch, + customBranding, + }: AuthenticationServiceSetupParams) { this.license = license; // If we cannot automatically authenticate users we should redirect them straight to the login @@ -201,8 +210,16 @@ export class AuthenticationService { ? this.authenticator.getRequestOriginalURL(request) : `${http.basePath.get(request)}/`; if (!isLoginPageAvailable) { + const customBrandingValue = await customBranding.getBrandingFor(request, { + unauthenticated: true, + }); return toolkit.render({ - body: renderUnauthenticatedPage({ buildNumber, basePath: http.basePath, originalURL }), + body: renderUnauthenticatedPage({ + buildNumber, + basePath: http.basePath, + originalURL, + customBranding: customBrandingValue, + }), headers: { 'Content-Security-Policy': http.csp.header }, }); } diff --git a/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx b/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx index aca2a5dd77e6c..d65c032911a03 100644 --- a/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx +++ b/x-pack/plugins/security/server/authentication/unauthenticated_page.test.tsx @@ -28,6 +28,25 @@ describe('UnauthenticatedPage', () => { originalURL="/some/url?some-query=some-value#some-hash" buildNumber={100500} basePath={mockCoreSetup.http.basePath} + customBranding={{}} + /> + ); + + expect(body).toMatchSnapshot(); + }); + + it('renders as expected with custom title', async () => { + const mockCoreSetup = coreMock.createSetup(); + (mockCoreSetup.http.basePath.prepend as jest.Mock).mockImplementation( + (path) => `/mock-basepath${path}` + ); + + const body = renderToStaticMarkup( + ); diff --git a/x-pack/plugins/security/server/authentication/unauthenticated_page.tsx b/x-pack/plugins/security/server/authentication/unauthenticated_page.tsx index 908c382dba755..a03401f929e92 100644 --- a/x-pack/plugins/security/server/authentication/unauthenticated_page.tsx +++ b/x-pack/plugins/security/server/authentication/unauthenticated_page.tsx @@ -10,6 +10,7 @@ import { EuiButton } from '@elastic/eui/lib/components/button'; import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; +import type { CustomBranding } from '@kbn/core-custom-branding-common'; import type { IBasePath } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -20,9 +21,10 @@ interface Props { originalURL: string; buildNumber: number; basePath: IBasePath; + customBranding: CustomBranding; } -export function UnauthenticatedPage({ basePath, originalURL, buildNumber }: Props) { +export function UnauthenticatedPage({ basePath, originalURL, buildNumber, customBranding }: Props) { return ( , ]} + customBranding={customBranding} /> ); } diff --git a/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap b/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap index 03ea6f4504733..060229539b9ad 100644 --- a/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap +++ b/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap @@ -1,3 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`ResetSessionPage renders as expected 1`] = `"ElasticMockedFonts"`; + +exports[`ResetSessionPage renders as expected with custom page title 1`] = `"My Company NameMockedFonts"`; diff --git a/x-pack/plugins/security/server/authorization/authorization_service.test.ts b/x-pack/plugins/security/server/authorization/authorization_service.test.ts index 3a1e239e38964..0f9955b0cd7e2 100644 --- a/x-pack/plugins/security/server/authorization/authorization_service.test.ts +++ b/x-pack/plugins/security/server/authorization/authorization_service.test.ts @@ -81,6 +81,7 @@ it(`#setup returns exposed services`, () => { features: mockFeaturesSetup, getSpacesService: mockGetSpacesService, getCurrentUser: jest.fn(), + customBranding: mockCoreSetup.customBranding, }); expect(authz.actions.version).toBe('version:some-version'); @@ -143,6 +144,7 @@ describe('#start', () => { .fn() .mockReturnValue({ getSpaceId: jest.fn(), namespaceToSpaceId: jest.fn() }), getCurrentUser: jest.fn(), + customBranding: mockCoreSetup.customBranding, }); const featuresStart = featuresPluginMock.createStart(); @@ -215,6 +217,7 @@ it('#stop unsubscribes from license and ES updates.', async () => { .fn() .mockReturnValue({ getSpaceId: jest.fn(), namespaceToSpaceId: jest.fn() }), getCurrentUser: jest.fn(), + customBranding: mockCoreSetup.customBranding, }); const featuresStart = featuresPluginMock.createStart(); diff --git a/x-pack/plugins/security/server/authorization/authorization_service.tsx b/x-pack/plugins/security/server/authorization/authorization_service.tsx index 0f725e1cca1c7..e190ec994c83c 100644 --- a/x-pack/plugins/security/server/authorization/authorization_service.tsx +++ b/x-pack/plugins/security/server/authorization/authorization_service.tsx @@ -12,6 +12,7 @@ import type { Observable, Subscription } from 'rxjs'; import type { CapabilitiesSetup, + CustomBrandingSetup, HttpServiceSetup, IClusterClient, KibanaRequest, @@ -64,6 +65,7 @@ interface AuthorizationServiceSetupParams { kibanaIndexName: string; getSpacesService(): SpacesService | undefined; getCurrentUser(request: KibanaRequest): AuthenticatedUser | null; + customBranding: CustomBrandingSetup; } interface AuthorizationServiceStartParams { @@ -117,6 +119,7 @@ export class AuthorizationService { kibanaIndexName, getSpacesService, getCurrentUser, + customBranding, }: AuthorizationServiceSetupParams): AuthorizationServiceSetupInternal { this.logger = loggers.get('authorization'); this.applicationName = `${APPLICATION_PREFIX}${kibanaIndexName}`; @@ -176,8 +179,11 @@ export class AuthorizationService { initAPIAuthorization(http, authz, loggers.get('api-authorization')); initAppAuthorization(http, authz, loggers.get('app-authorization'), features); - http.registerOnPreResponse((request, preResponse, toolkit) => { + http.registerOnPreResponse(async (request, preResponse, toolkit) => { if (preResponse.statusCode === 403 && canRedirectRequest(request)) { + const customBrandingValue = await customBranding.getBrandingFor(request, { + unauthenticated: false, + }); const next = `${http.basePath.get(request)}${request.url.pathname}${request.url.search}`; const body = renderToString( ); diff --git a/x-pack/plugins/security/server/authorization/reset_session_page.test.tsx b/x-pack/plugins/security/server/authorization/reset_session_page.test.tsx index 8111246f0776d..15b656ca5d41d 100644 --- a/x-pack/plugins/security/server/authorization/reset_session_page.test.tsx +++ b/x-pack/plugins/security/server/authorization/reset_session_page.test.tsx @@ -28,6 +28,25 @@ describe('ResetSessionPage', () => { logoutUrl="/path/to/logout" buildNumber={100500} basePath={mockCoreSetup.http.basePath} + customBranding={{}} + /> + ); + + expect(body).toMatchSnapshot(); + }); + + it('renders as expected with custom page title', async () => { + const mockCoreSetup = coreMock.createSetup(); + (mockCoreSetup.http.basePath.prepend as jest.Mock).mockImplementation( + (path) => `/mock-basepath${path}` + ); + + const body = renderToStaticMarkup( + ); diff --git a/x-pack/plugins/security/server/authorization/reset_session_page.tsx b/x-pack/plugins/security/server/authorization/reset_session_page.tsx index cd6f49eef755e..61555d2b4dba5 100644 --- a/x-pack/plugins/security/server/authorization/reset_session_page.tsx +++ b/x-pack/plugins/security/server/authorization/reset_session_page.tsx @@ -9,6 +9,7 @@ import { EuiButton, EuiButtonEmpty } from '@elastic/eui/lib/components/button'; import React from 'react'; +import type { CustomBranding } from '@kbn/core-custom-branding-common'; import type { IBasePath } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -19,10 +20,12 @@ export function ResetSessionPage({ logoutUrl, buildNumber, basePath, + customBranding, }: { logoutUrl: string; buildNumber: number; basePath: IBasePath; + customBranding: CustomBranding; }) { return ( , ]} + customBranding={customBranding} /> ); } diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index ff3a5c3efaf3c..7e29008ae754b 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -267,6 +267,7 @@ export class SecurityPlugin config, license, buildNumber: this.initializerContext.env.packageInfo.buildNum, + customBranding: core.customBranding, }); registerSecurityUsageCollector({ usageCollection, config, license }); @@ -297,6 +298,7 @@ export class SecurityPlugin getSpacesService: () => spaces?.spacesService, features, getCurrentUser: (request) => this.getAuthentication().getCurrentUser(request), + customBranding: core.customBranding, }); this.userProfileService.setup({ authz: this.authorizationSetup, license }); diff --git a/x-pack/plugins/security/server/prompt_page.test.tsx b/x-pack/plugins/security/server/prompt_page.test.tsx index ef59cacd31bfc..268a7ac640e71 100644 --- a/x-pack/plugins/security/server/prompt_page.test.tsx +++ b/x-pack/plugins/security/server/prompt_page.test.tsx @@ -29,6 +29,7 @@ describe('PromptPage', () => { basePath={mockCoreSetup.http.basePath} title="Some Title" body={
Some Body
} + customBranding={{}} actions={[Action#1, Action#2]} /> ); @@ -49,6 +50,7 @@ describe('PromptPage', () => { scriptPaths={['/some/script1.js', '/some/script2.js']} title="Some Title" body={
Some Body
} + customBranding={{}} actions={[Action#1, Action#2]} /> ); diff --git a/x-pack/plugins/security/server/prompt_page.tsx b/x-pack/plugins/security/server/prompt_page.tsx index 38bd77b444e83..ad049d40c025f 100644 --- a/x-pack/plugins/security/server/prompt_page.tsx +++ b/x-pack/plugins/security/server/prompt_page.tsx @@ -22,6 +22,7 @@ import type { ReactNode } from 'react'; import React from 'react'; import { renderToString } from 'react-dom/server'; +import type { CustomBranding } from '@kbn/core-custom-branding-common'; import { Fonts } from '@kbn/core-rendering-server-internal'; import type { IBasePath } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; @@ -45,6 +46,7 @@ interface Props { title: ReactNode; body: ReactNode; actions: ReactNode; + customBranding: CustomBranding; } export function PromptPage({ @@ -54,6 +56,7 @@ export function PromptPage({ title, body, actions, + customBranding, }: Props) { const content = ( @@ -92,7 +95,7 @@ export function PromptPage({ return ( - Elastic + {customBranding.pageTitle ? customBranding.pageTitle : 'Elastic'} {/* eslint-disable-next-line react/no-danger */}