diff --git a/.github/ISSUE_TEMPLATE/v8_breaking_change.md b/.github/ISSUE_TEMPLATE/v8_breaking_change.md index 67d2ee2d3286b..9361cf7a52b65 100644 --- a/.github/ISSUE_TEMPLATE/v8_breaking_change.md +++ b/.github/ISSUE_TEMPLATE/v8_breaking_change.md @@ -13,7 +13,8 @@ assignees: '' **************************************** Please add a team label to denote the team that the -breaking change is applicable to. +breaking change is applicable to. If the work requires +changes to Upgrade Assistant itself, please tag Team:Elasticsearch UI. --> @@ -23,6 +24,10 @@ breaking change is applicable to. 8.0 +**Is this a Kibana or Elasticsearch breaking change?** + + + **Describe the change. How will it manifest to users?** **How many users will be affected?** @@ -30,7 +35,9 @@ breaking change is applicable to. -**Can the change be registered with the [Kibana deprecation service](https://github.com/elastic/kibana/blob/master/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md)?** +**Are there any edge cases?** + +**[For Kibana deprecations] Can the change be registered with the [Kibana deprecation service](https://github.com/elastic/kibana/blob/master/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md)?** -**Are there any edge cases?** +**[For Elasticsearch deprecations] Can the Upgrade Assistant make the migration easier for users? Please explain the proposed solution in as much detail as possible.** + + + ## Test Data diff --git a/api_docs/alerting.json b/api_docs/alerting.json index a42dda758dae1..ddb92f5aff9bb 100644 --- a/api_docs/alerting.json +++ b/api_docs/alerting.json @@ -404,7 +404,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 79 + "lineNumber": 80 }, "deprecated": false, "children": [ @@ -417,7 +417,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 86 + "lineNumber": 87 }, "deprecated": false }, @@ -433,7 +433,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 87 + "lineNumber": 88 }, "deprecated": false }, @@ -449,7 +449,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 88 + "lineNumber": 89 }, "deprecated": false }, @@ -472,7 +472,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 89 + "lineNumber": 90 }, "deprecated": false }, @@ -488,7 +488,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 90 + "lineNumber": 91 }, "deprecated": false }, @@ -504,7 +504,29 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 91 + "lineNumber": 92 + }, + "deprecated": false + }, + { + "parentPluginId": "alerting", + "id": "def-server.AlertExecutorOptions.rule", + "type": "CompoundType", + "tags": [], + "label": "rule", + "description": [], + "signature": [ + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.SanitizedRuleConfig", + "text": "SanitizedRuleConfig" + } + ], + "source": { + "path": "x-pack/plugins/alerting/server/types.ts", + "lineNumber": 93 }, "deprecated": false }, @@ -517,7 +539,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 92 + "lineNumber": 94 }, "deprecated": false }, @@ -533,7 +555,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 93 + "lineNumber": 95 }, "deprecated": false }, @@ -546,7 +568,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 94 + "lineNumber": 96 }, "deprecated": false }, @@ -562,7 +584,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 95 + "lineNumber": 97 }, "deprecated": false }, @@ -578,7 +600,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 96 + "lineNumber": 98 }, "deprecated": false }, @@ -594,7 +616,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 97 + "lineNumber": 99 }, "deprecated": false } @@ -610,7 +632,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 45 + "lineNumber": 46 }, "deprecated": false, "children": [ @@ -633,7 +655,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 46 + "lineNumber": 47 }, "deprecated": false, "returnComment": [], @@ -653,7 +675,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 47 + "lineNumber": 48 }, "deprecated": false, "returnComment": [], @@ -679,7 +701,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 48 + "lineNumber": 49 }, "deprecated": false, "returnComment": [], @@ -697,7 +719,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 49 + "lineNumber": 50 }, "deprecated": false, "returnComment": [], @@ -715,7 +737,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 219 + "lineNumber": 221 }, "deprecated": false, "children": [ @@ -737,7 +759,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 220 + "lineNumber": 222 }, "deprecated": false }, @@ -759,7 +781,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 221 + "lineNumber": 223 }, "deprecated": false } @@ -786,7 +808,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 69 + "lineNumber": 70 }, "deprecated": false, "children": [ @@ -804,7 +826,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 74 + "lineNumber": 75 }, "deprecated": false, "returnComment": [], @@ -846,7 +868,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 113 + "lineNumber": 115 }, "deprecated": false, "children": [ @@ -859,7 +881,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 121 + "lineNumber": 123 }, "deprecated": false }, @@ -872,7 +894,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 122 + "lineNumber": 124 }, "deprecated": false }, @@ -890,7 +912,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 123 + "lineNumber": 125 }, "deprecated": false }, @@ -913,7 +935,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 126 + "lineNumber": 128 }, "deprecated": false }, @@ -929,7 +951,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 127 + "lineNumber": 129 }, "deprecated": false }, @@ -952,7 +974,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 128 + "lineNumber": 130 }, "deprecated": false }, @@ -984,7 +1006,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 129 + "lineNumber": 131 }, "deprecated": false, "returnComment": [], @@ -1023,7 +1045,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 140 + "lineNumber": 142 }, "deprecated": false }, @@ -1063,7 +1085,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 141 + "lineNumber": 143 }, "deprecated": false }, @@ -1079,7 +1101,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 146 + "lineNumber": 148 }, "deprecated": false } @@ -1640,7 +1662,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/types.ts", - "lineNumber": 179 + "lineNumber": 181 }, "deprecated": false, "initialIsOpen": false @@ -2047,7 +2069,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/common/alert.ts", - "lineNumber": 101 + "lineNumber": 121 }, "deprecated": false, "children": [ @@ -2060,7 +2082,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/common/alert.ts", - "lineNumber": 102 + "lineNumber": 122 }, "deprecated": false }, @@ -2073,7 +2095,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/common/alert.ts", - "lineNumber": 103 + "lineNumber": 123 }, "deprecated": false }, @@ -2089,7 +2111,7 @@ ], "source": { "path": "x-pack/plugins/alerting/common/alert.ts", - "lineNumber": 104 + "lineNumber": 124 }, "deprecated": false }, @@ -2105,7 +2127,7 @@ ], "source": { "path": "x-pack/plugins/alerting/common/alert.ts", - "lineNumber": 105 + "lineNumber": 125 }, "deprecated": false } @@ -3040,7 +3062,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/common/alert.ts", - "lineNumber": 86 + "lineNumber": 106 }, "deprecated": false, "children": [ @@ -3064,7 +3086,7 @@ ], "source": { "path": "x-pack/plugins/alerting/common/alert.ts", - "lineNumber": 87 + "lineNumber": 107 }, "deprecated": false }, @@ -3088,7 +3110,7 @@ ], "source": { "path": "x-pack/plugins/alerting/common/alert.ts", - "lineNumber": 91 + "lineNumber": 111 }, "deprecated": false }, @@ -3112,7 +3134,7 @@ ], "source": { "path": "x-pack/plugins/alerting/common/alert.ts", - "lineNumber": 95 + "lineNumber": 115 }, "deprecated": false } @@ -3410,7 +3432,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/common/alert.ts", - "lineNumber": 80 + "lineNumber": 100 }, "deprecated": false, "initialIsOpen": false @@ -3883,6 +3905,23 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.SanitizedRuleConfig", + "type": "Type", + "tags": [], + "label": "SanitizedRuleConfig", + "description": [], + "signature": [ + "Pick, \"enabled\" | \"id\" | \"name\" | \"params\" | \"actions\" | \"tags\" | \"muteAll\" | \"alertTypeId\" | \"consumer\" | \"schedule\" | \"scheduledTaskId\" | \"createdBy\" | \"updatedBy\" | \"createdAt\" | \"updatedAt\" | \"apiKeyOwner\" | \"throttle\" | \"notifyWhen\" | \"mutedInstanceIds\" | \"executionStatus\">, \"enabled\" | \"name\" | \"actions\" | \"tags\" | \"consumer\" | \"schedule\" | \"createdBy\" | \"updatedBy\" | \"createdAt\" | \"updatedAt\" | \"throttle\" | \"notifyWhen\"> & { producer: string; ruleTypeId: string; ruleTypeName: string; }" + ], + "source": { + "path": "x-pack/plugins/alerting/common/alert.ts", + "lineNumber": 80 + }, + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.WithoutReservedActionGroups", diff --git a/dev_docs/assets/kibana_template_solution_nav.png b/dev_docs/assets/kibana_template_solution_nav.png new file mode 100644 index 0000000000000..4ffec1990ed0a Binary files /dev/null and b/dev_docs/assets/kibana_template_solution_nav.png differ diff --git a/dev_docs/assets/kibana_template_solution_nav_mobile.png b/dev_docs/assets/kibana_template_solution_nav_mobile.png new file mode 100644 index 0000000000000..487114f7a344a Binary files /dev/null and b/dev_docs/assets/kibana_template_solution_nav_mobile.png differ diff --git a/dev_docs/assets/target_folder_with_a_kibana_distributable_and_os_packages.png b/dev_docs/assets/target_folder_with_a_kibana_distributable_and_os_packages.png new file mode 100644 index 0000000000000..4ac283ecb8120 Binary files /dev/null and b/dev_docs/assets/target_folder_with_a_kibana_distributable_and_os_packages.png differ diff --git a/dev_docs/assets/target_folder_with_simple_kibana_distributable.png b/dev_docs/assets/target_folder_with_simple_kibana_distributable.png new file mode 100644 index 0000000000000..1b7e6a82426ca Binary files /dev/null and b/dev_docs/assets/target_folder_with_simple_kibana_distributable.png differ diff --git a/dev_docs/tutorials/building_a_kibana_distributable.mdx b/dev_docs/tutorials/building_a_kibana_distributable.mdx new file mode 100644 index 0000000000000..7b06525a5b977 --- /dev/null +++ b/dev_docs/tutorials/building_a_kibana_distributable.mdx @@ -0,0 +1,135 @@ +--- +id: kibDevTutorialBuildingDistributable +slug: /kibana-dev-docs/tutorial/building-distributable +title: Building a Kibana distributable +summary: Learn how to build a Kibana distributable +date: 2021-05-10 +tags: ['kibana', 'onboarding', 'dev', 'tutorials', 'build', 'distributable'] +--- + +On the course of that tutorial it will be possible to learn more about how we can create a Kibana distributable +as well as handle its different main configurations. + +At any given point, you can get CLI help running the following command: + +```bash +yarn build --help +``` + +## Prerequisites + +For the basic steps of the build we only require you to use one of the following operating systems: + +- Linux +- macOS + +However if as part of the build you also want to generate Linux installation packages (deb, rpm) or Docker images there +are other dependencies to have in mind as those installation packages and Docker images generation are run using fpm, dpkg, rpm, and Docker. + +### For Linux Installation Packages Build + +Please make sure you have the following dependencies installed: + +#### Install FPM dependencies + +**On OSX/macOS:** + +```bash +brew install gnu-tar rpm +``` + +**On Red Hat systems (Fedora 22 or older, CentOS, etc):** + +```bash +yum install ruby-devel gcc make rpm-build rubygems +``` + +**On Fedora 23 or newer:** + +```bash +dnf install ruby-devel gcc make rpm-build libffi-devel +``` + +**On Oracle Linux 7.x systems:** + +```bash +yum-config-manager --enable ol7_optional_latest +yum install ruby-devel gcc make rpm-build rubygems +``` + +**On Debian-derived systems (Debian, Ubuntu, etc):** + +```bash +apt-get install ruby ruby-dev rubygems build-essential +``` + +#### Install FPM + +Finally install fpm with the gem tool with: + +```bash +gem install fpm -v 1.5.0 +``` + +### For Docker Images Build + +For Docker, the installation instructions can be found at [Install Docker Engine](https://docs.docker.com/engine/install/). + + +## Create a Kibana distributable + +In a great majority of the use cases where we need to build a Kibana distributable, we just need the archives containing the +node executable and the bundled code of the application. + +By doing that Linux installation packages and Docker images will be excluded and as a result, the build will be quicker. + +We can do it by simply running: + +```bash +yarn build --skip-os-packages +``` + +Note that we used `--skip-os-packages` which will skip the OS packages build. + +> In case you are testing something and running that same command a couple of times, `--skip-node-download` can be used +to speed up the process by a little. + +At the end of the process a Kibana distributable was created in a `target` folder created relative to your repository checkout. +The folder will look like the following: + +![Target Folder Outlook With a Kibana Distributable](../assets/target_folder_with_simple_kibana_distributable.png) + +## Controlling the log levels + +By default, when building the distributable, the `debug` log level will be used across all the steps. +That default setting should give us a good amount of information about the tasks being done. + +To turn it off you can run the build along `--no-debug` flag. At that point that information will no longer be printed out. + +For a longer and verbose logging than `debug` there is other option that can be passed along the build command which is `--verbose`. + +## Create a Kibana distributable with Linux installation packages and Docker images + +If you comply with every prerequisite and dependency listed above, then there is also the option to create a Kibana distributable along with +Linux installation packages like rpm and deb or Docker images. + +To achieve it, you can run: + +```bash +yarn build +``` + +At the end you will get the Kibana distributable archives plus the Docker images and both an rpm and a deb package. + +To specify just a single installation package or Docker images to build instead of all of them you can add rpm, deb or docker-images as an argument: + +```bash +yarn build --deb +yarn build --rpm +yarn build --docker-images +``` + +Again the distributable contents resulting from running the build command can be found in a `target` folder created relative to the repository after the build completes. +It will look something like: + +![Target Folder Outlook With a Kibana Distributable And OS Packages](../assets/target_folder_with_a_kibana_distributable_and_os_packages.png) diff --git a/dev_docs/tutorials/kibana_page_template.mdx b/dev_docs/tutorials/kibana_page_template.mdx index aa38890a8ac9e..d9605ac5643ba 100644 --- a/dev_docs/tutorials/kibana_page_template.mdx +++ b/dev_docs/tutorials/kibana_page_template.mdx @@ -9,13 +9,13 @@ tags: ['kibana', 'dev', 'ui', 'tutorials'] `KibanaPageTemplate` is a thin wrapper around [EuiPageTemplate](https://elastic.github.io/eui/#/layout/page) that makes setting up common types of Kibana pages quicker and easier while also adhering to any Kibana-specific requirements and patterns. -Refer to EUI's documentation on [EuiPageTemplate](https://elastic.github.io/eui/#/layout/page) for constructing page layouts. +Refer to EUI's documentation on [**EuiPageTemplate**](https://elastic.github.io/eui/#/layout/page) for constructing page layouts. ## `isEmptyState` Use the `isEmptyState` prop for when there is no page content to show. For example, before the user has created something, when no search results are found, before data is populated, or when permissions aren't met. -The default empty state uses any `pageHeader` info provided to populate an [`EuiEmptyPrompt`](https://elastic.github.io/eui/#/display/empty-prompt) and uses the `centeredBody` template type. +The default empty state uses any `pageHeader` info provided to populate an [**EuiEmptyPrompt**](https://elastic.github.io/eui/#/display/empty-prompt) and uses the `centeredBody` template type. ```tsx + {...} + +``` + + +![Screenshot of Stack Management empty state with a provided solution navigation shown on the left, outlined in pink.](../assets/kibana_template_solution_nav.png) + +![Screenshots of Stack Management page in mobile view. Menu closed on the left, menu open on the right.](../assets/kibana_template_solution_nav_mobile.png) diff --git a/docs/api/index-patterns.asciidoc b/docs/api/index-patterns.asciidoc new file mode 100644 index 0000000000000..47906e1761138 --- /dev/null +++ b/docs/api/index-patterns.asciidoc @@ -0,0 +1,27 @@ +[[index-patterns-api]] +== Index patterns APIs + +experimental[] Manage {kib} index patterns. + +WARNING: Do not write documents directly to the `.kibana` index. When you write directly +to the `.kibana` index, the data becomes corrupted and permanently breaks future {kib} versions. + +WARNING: Use the index patterns API for managing {kib} index patterns instead of lower-level <>. + +The following index patterns APIs are available: + +* Index patterns + ** <> to retrieve a single {kib} index pattern + ** <> to create {kib} index pattern + ** <> to partially updated {kib} index pattern + ** <> to delete {kib} index pattern +* Fields + ** <> to change field metadata, such as `count`, `customLabel` and `format`. + + + +include::index-patterns/get.asciidoc[] +include::index-patterns/create.asciidoc[] +include::index-patterns/update.asciidoc[] +include::index-patterns/delete.asciidoc[] +include::index-patterns/update-fields.asciidoc[] diff --git a/docs/api/index-patterns/create.asciidoc b/docs/api/index-patterns/create.asciidoc new file mode 100644 index 0000000000000..771292d6f934d --- /dev/null +++ b/docs/api/index-patterns/create.asciidoc @@ -0,0 +1,102 @@ +[[index-patterns-api-create]] +=== Create index pattern API +++++ +Create index pattern +++++ + +experimental[] Create {kib} index patterns. + +[[index-patterns-api-create-request]] +==== Request + +`POST :/api/index_patterns/index_pattern` + +`POST :/s//api/index_patterns/index_pattern` + +[[index-patterns-api-create-path-params]] +==== Path parameters + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[index-patterns-api-create-body-params]] +==== Request body + +`override`:: (Optional, boolean) Overrides an existing index pattern if an +index pattern with the provided title already exists. The default is `false`. + +`refresh_fields`:: (Optional, boolean) Reloads index pattern fields after +the index pattern is stored. The default is `false`. + +`index_pattern`:: (Required, object) The index pattern object. All fields are optional. + +[[index-patterns-api-create-request-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +[[index-patterns-api-create-example]] +==== Examples + +Create an index pattern with a custom title: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/index_patterns/index_pattern +{ + "index_pattern": { + "title": "hello" + } +} +-------------------------------------------------- +// KIBANA + +Customize the creation behavior: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/index_patterns/index_pattern +{ + "override": false, + "refresh_fields": true, + "index_pattern": { + "title": "hello" + } +} +-------------------------------------------------- +// KIBANA + +At creation, all index pattern fields are optional: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/index_patterns/index_pattern +{ + "index_pattern": { + "id": "...", + "version": "...", + "title": "...", + "type": "...", + "timeFieldName": "...", + "sourceFilters": [], + "fields": {}, + "typeMeta": {}, + "fieldFormats": {}, + "fieldAttrs": {}, + "allowNoIndex": "..." + } +} +-------------------------------------------------- +// KIBANA + + +The API returns the index pattern object: + +[source,sh] +-------------------------------------------------- +{ + "index_pattern": {...} +} +-------------------------------------------------- + diff --git a/docs/api/index-patterns/delete.asciidoc b/docs/api/index-patterns/delete.asciidoc new file mode 100644 index 0000000000000..a25f2f13e0b41 --- /dev/null +++ b/docs/api/index-patterns/delete.asciidoc @@ -0,0 +1,41 @@ +[[index-patterns-api-delete]] +=== Delete index pattern API +++++ +Delete index pattern +++++ + +experimental[] Delete {kib} index patterns. + +WARNING: Once you delete an index pattern, _it cannot be recovered_. + +[[index-patterns-api-delete-request]] +==== Request + +`DELETE :/api/index_patterns/index_pattern/` + +`DELETE :/s//api/index_patterns/index_pattern/` + +[[index-patterns-api-delete-path-params]] +==== Path parameters + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +`id`:: + (Required, string) The ID of the index pattern you want to delete. + +[[index-patterns-api-delete-response-codes]] +==== Response code + +`200`:: + Indicates that index pattern is deleted. Returns an empty response body. + +==== Example + +Delete an index pattern object with the `my-pattern` ID: + +[source,sh] +-------------------------------------------------- +$ curl -X DELETE api/index_patterns/index_pattern/my-pattern +-------------------------------------------------- +// KIBANA diff --git a/docs/api/index-patterns/get.asciidoc b/docs/api/index-patterns/get.asciidoc new file mode 100644 index 0000000000000..3f53bf0726bf1 --- /dev/null +++ b/docs/api/index-patterns/get.asciidoc @@ -0,0 +1,64 @@ +[[index-patterns-api-get]] +=== Get index pattern API +++++ +Get index pattern +++++ + +experimental[] Retrieve a single {kib} index pattern by ID. + +[[index-patterns-api-get-request]] +==== Request + +`GET :/api/index_patterns/index_pattern/` + +`GET :/s//api/index_patterns/index_pattern/` + +[[index-patterns-api-get-params]] +==== Path parameters + +`space_id`:: +(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +`id`:: +(Required, string) The ID of the index pattern you want to retrieve. + +[[index-patterns-api-get-codes]] +==== Response code + +`200`:: +Indicates a successful call. + +`404`:: +The specified index pattern and ID doesn't exist. + +[[index-patterns-api-get-example]] +==== Example + +Retrieve the index pattern object with the `my-pattern` ID: + +[source,sh] +-------------------------------------------------- +$ curl -X GET api/index_patterns/index_pattern/my-pattern +-------------------------------------------------- +// KIBANA + +The API returns an index pattern object: + +[source,sh] +-------------------------------------------------- +{ + "index_pattern": { + "id": "my-pattern", + "version": "...", + "title": "...", + "type": "...", + "timeFieldName": "...", + "sourceFilters": [], + "fields": {}, + "typeMeta": {}, + "fieldFormats": {}, + "fieldAttrs": {}, + "allowNoIndex: "..." + } +} +-------------------------------------------------- diff --git a/docs/api/index-patterns/update-fields.asciidoc b/docs/api/index-patterns/update-fields.asciidoc new file mode 100644 index 0000000000000..e3f98631bb52a --- /dev/null +++ b/docs/api/index-patterns/update-fields.asciidoc @@ -0,0 +1,100 @@ +[[index-patterns-fields-api-update]] +=== Update index pattern fields API +++++ +Update index pattern fields metadata +++++ + +experimental[] Update fields presentation metadata, such as `count`, +`customLabel`, and `format`. You can update multiple fields in one request. Updates +are merged with persisted metadata. To remove existing metadata, specify `null` as the value. + +[[index-patterns-fields-api-update-request]] +==== Request + +`POST :/api/index_patterns/index_pattern//fields` + +`POST :/s//api/index_patterns/index_pattern//fields` + +[[index-patterns-fields-api-update-path-params]] +==== Path parameters + +`space_id`:: +(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +`id`:: +(Required, string) The ID of the index pattern fields you want to update. + +[[index-patterns-fields-api-update-request-body]] +==== Request body + +`fields`:: +(Required, object) the field object + + +[[index-patterns-fields-api-update-errors-codes]] +==== Response code + +`200`:: +Indicates a successful call. + +[[index-patterns-fields-api-update-example]] +==== Examples + +Set popularity `count` for field `foo`: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/saved_objects/index-pattern/my-pattern/fields +{ + "fields": { + "foo": { + "count": 123 + } + } +} +-------------------------------------------------- +// KIBANA + +Update multiple metadata fields in one request: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/saved_objects/index-pattern/my-pattern/fields +{ + "fields": { + "foo": { + "count": 123, + "customLabel": "Foo" + }, + "bar": { + "customLabel": "Bar" + } + } +} +-------------------------------------------------- +// KIBANA + +Use `null` value to delete metadata: +[source,sh] +-------------------------------------------------- +$ curl -X POST api/saved_objects/index-pattern/my-pattern/fields +{ + "fields": { + "foo": { + "customLabel": null + } + } +} +-------------------------------------------------- +// KIBANA + + +The endpoint returns the updated index pattern object: +[source,sh] +-------------------------------------------------- +{ + "index_pattern": { + + } +} +-------------------------------------------------- diff --git a/docs/api/index-patterns/update.asciidoc b/docs/api/index-patterns/update.asciidoc new file mode 100644 index 0000000000000..8ed0ff89fb928 --- /dev/null +++ b/docs/api/index-patterns/update.asciidoc @@ -0,0 +1,111 @@ +[[index-patterns-api-update]] +=== Update index pattern API +++++ +Update index pattern +++++ + +experimental[] Update part of an index pattern. Only the specified fields are updated in the +index pattern. Unspecified fields stay as they are persisted. + +[[index-patterns-api-update-request]] +==== Request + +`POST :/api/index_patterns/index_pattern/` + +`POST :/s//api/index_patterns/index_pattern/` + +[[index-patterns-api-update-path-params]] +==== Path parameters + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +`id`:: + (Required, string) The ID of the index pattern you want to update. + +[[index-patterns-api-update-request-body]] +==== Request body + +`refresh_fields`:: (Optional, boolean) Reloads the index pattern fields after +the index pattern is updated. The default is `false`. + +`index_pattern`:: + (Required, object) The index patterns fields you want to update. ++ + +You can partially update the following fields: + +* `title` +* `timeFieldName` +* `fields` +* `sourceFilters` +* `fieldFormatMap` +* `type` +* `typeMeta` + +[[index-patterns-api-update-errors-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +[[index-patterns-api-update-example]] +==== Examples + +Update a title of the `` index pattern: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/saved_objects/index-pattern/my-pattern +{ + "index_pattern": { + "title": "some-other-pattern-*" + } +} +-------------------------------------------------- +// KIBANA + +Customize the update behavior: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/saved_objects/index-pattern/my-pattern +{ + "refresh_fields": true, + "index_pattern": { + "fields": {} + } +} +-------------------------------------------------- +// KIBANA + + +All update fields are optional, but you can specify the following fields: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/saved_objects/index-pattern/my-pattern +{ + "index_pattern": { + "title": "...", + "timeFieldName": "...", + "sourceFilters": [], + "fieldFormats": {}, + "type": "...", + "typeMeta": {}, + "fields": {} + } +} +-------------------------------------------------- +// KIBANA + +The API returns the updated index pattern object: + +[source,sh] +-------------------------------------------------- +{ + "index_pattern": { + + } +} +-------------------------------------------------- diff --git a/docs/api/saved-objects.asciidoc b/docs/api/saved-objects.asciidoc index ecf975134c64a..ba4e5a7e656fc 100644 --- a/docs/api/saved-objects.asciidoc +++ b/docs/api/saved-objects.asciidoc @@ -1,11 +1,13 @@ [[saved-objects-api]] == Saved objects APIs -Manage {kib} saved objects, including dashboards, visualizations, index patterns, and more. +Manage {kib} saved objects, including dashboards, visualizations, and more. WARNING: Do not write documents directly to the `.kibana` index. When you write directly to the `.kibana` index, the data becomes corrupted and permanently breaks future {kib} versions. +NOTE: For managing {kib} index patterns, use the <>. + The following saved objects APIs are available: * <> to retrieve a single {kib} saved object by ID diff --git a/docs/concepts/index.asciidoc b/docs/concepts/index.asciidoc index cb37dceb53564..43e5ae733a760 100644 --- a/docs/concepts/index.asciidoc +++ b/docs/concepts/index.asciidoc @@ -87,6 +87,7 @@ image:concepts/images/refresh-every.png["section of time filter where you can co [float] +[[semi-structured-search]] ==== Semi-structured search Combine free text search with field-based search using the Kibana Query Language (KQL). diff --git a/docs/developer/contributing/development-functional-tests.asciidoc b/docs/developer/contributing/development-functional-tests.asciidoc index 110704a8e569a..f0041b85c14eb 100644 --- a/docs/developer/contributing/development-functional-tests.asciidoc +++ b/docs/developer/contributing/development-functional-tests.asciidoc @@ -139,11 +139,14 @@ export default function (/* { providerAPI } */) { } ----------- -**Services**::: -Services are named singleton values produced by a Service Provider. Tests and other services can retrieve service instances by asking for them by name. All functionality except the mocha API is exposed via services. +**Service**::: +A Service is a named singleton created using a subclass of `FtrService`. Tests and other services can retrieve service instances by asking for them by name. All functionality except the mocha API is exposed via services. When you write your own functional tests check for existing services that help with the interactions you're looking to execute, and add new services for interactions which aren't already encoded in a service. + +**Service Providers**::: +For legacy purposes, and for when creating a subclass of `FtrService` is inconvenient, you can also create services using a "Service Provider". These are functions which which create service instances and return them. These instances are cached and provided to tests. Currently these providers may also return a Promise for the service instance, allowing the service to do some setup work before tests run. We expect to fully deprecate and remove support for async service providers in the near future and instead require that services use the `lifecycle` service to run setup before tests. Providers which return instances of classes other than `FtrService` will likely remain supported for as long as possible. **Page objects**::: -Page objects are a special type of service that encapsulate behaviors common to a particular page or plugin. When you write your own plugin, you’ll likely want to add a page object (or several) that describes the common interactions your tests need to execute. +Page objects are functionally equivalent to services, except they are loaded with a slightly different mechanism and generally defined separate from services. When you write your own functional tests you might want to write some of your services as Page objects, but it is not required. **Test Files**::: The `FunctionalTestRunner`'s primary purpose is to execute test files. These files export a Test Provider that is called with a Provider API but is not expected to return a value. Instead Test Providers define a suite using https://mochajs.org/#bdd[mocha's BDD interface]. diff --git a/docs/developer/getting-started/monorepo-packages.asciidoc b/docs/developer/getting-started/monorepo-packages.asciidoc index a98aa7aa9cd56..8f033029cfac4 100644 --- a/docs/developer/getting-started/monorepo-packages.asciidoc +++ b/docs/developer/getting-started/monorepo-packages.asciidoc @@ -82,12 +82,14 @@ yarn kbn watch-bazel - @kbn/i18n - @kbn/legacy-logging - @kbn/logging -- @kbn/securitysolution-constants - @kbn/securitysolution-es-utils - kbn/securitysolution-io-ts-alerting-types - kbn/securitysolution-io-ts-list-types - kbn/securitysolution-io-ts-types - @kbn/securitysolution-io-ts-utils +- @kbn/securitysolution-list-api +- @kbn/securitysolution-list-constants +- @kbn/securitysolution-list-hooks - @kbn/securitysolution-list-utils - @kbn/securitysolution-utils - @kbn/server-http-tools diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 4ba5e32eec8b5..94384024e0935 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -321,7 +321,7 @@ which will load the visualization's editor. |{kib-repo}blob/{branch}/x-pack/plugins/alerting/README.md[alerting] -|The Kibana alerting plugin provides a common place to set up alerts. You can: +|The Kibana Alerting plugin provides a common place to set up rules. You can: |{kib-repo}blob/{branch}/x-pack/plugins/apm/readme.md[apm] diff --git a/docs/development/core/public/kibana-plugin-core-public.app.deeplinks.md b/docs/development/core/public/kibana-plugin-core-public.app.deeplinks.md new file mode 100644 index 0000000000000..8186996b63fe5 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.app.deeplinks.md @@ -0,0 +1,49 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [App](./kibana-plugin-core-public.app.md) > [deepLinks](./kibana-plugin-core-public.app.deeplinks.md) + +## App.deepLinks property + +Input type for registering secondary in-app locations for an application. + +Deep links must include at least one of `path` or `deepLinks`. A deep link that does not have a `path` represents a topological level in the application's hierarchy, but does not have a destination URL that is user-accessible. + +Signature: + +```typescript +deepLinks?: AppDeepLink[]; +``` + +## Example + + +```ts +core.application.register({ + id: 'my_app', + title: 'Translated title', + keywords: ['translated keyword1', 'translated keyword2'], + deepLinks: [ + { + id: 'sub1', + title: 'Sub1', + path: '/sub1', + keywords: ['subpath1'], + }, + { + id: 'sub2', + title: 'Sub2', + deepLinks: [ + { + id: 'subsub', + title: 'SubSub', + path: '/sub2/sub', + keywords: ['subpath2'], + }, + ], + }, + ], + mount: () => { ... } +}) + +``` + diff --git a/docs/development/core/public/kibana-plugin-core-public.app.keywords.md b/docs/development/core/public/kibana-plugin-core-public.app.keywords.md new file mode 100644 index 0000000000000..585df1b48c16e --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.app.keywords.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [App](./kibana-plugin-core-public.app.md) > [keywords](./kibana-plugin-core-public.app.keywords.md) + +## App.keywords property + +Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. + +Signature: + +```typescript +keywords?: string[]; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.app.md b/docs/development/core/public/kibana-plugin-core-public.app.md index 9a508f293d8e8..721d9a2f121c7 100644 --- a/docs/development/core/public/kibana-plugin-core-public.app.md +++ b/docs/development/core/public/kibana-plugin-core-public.app.md @@ -19,12 +19,13 @@ export interface App | [capabilities](./kibana-plugin-core-public.app.capabilities.md) | Partial<Capabilities> | Custom capabilities defined by the app. | | [category](./kibana-plugin-core-public.app.category.md) | AppCategory | The category definition of the product See [AppCategory](./kibana-plugin-core-public.appcategory.md) See DEFAULT\_APP\_CATEGORIES for more reference | | [chromeless](./kibana-plugin-core-public.app.chromeless.md) | boolean | Hide the UI chrome when the application is mounted. Defaults to false. Takes precedence over chrome service visibility settings. | +| [deepLinks](./kibana-plugin-core-public.app.deeplinks.md) | AppDeepLink[] | Input type for registering secondary in-app locations for an application.Deep links must include at least one of path or deepLinks. A deep link that does not have a path represents a topological level in the application's hierarchy, but does not have a destination URL that is user-accessible. | | [defaultPath](./kibana-plugin-core-public.app.defaultpath.md) | string | Allow to define the default path a user should be directed to when navigating to the app. When defined, this value will be used as a default for the path option when calling [navigateToApp](./kibana-plugin-core-public.applicationstart.navigatetoapp.md)\`, and will also be appended to the [application navLink](./kibana-plugin-core-public.chromenavlink.md) in the navigation bar. | | [euiIconType](./kibana-plugin-core-public.app.euiicontype.md) | string | A EUI iconType that will be used for the app's icon. This icon takes precendence over the icon property. | | [exactRoute](./kibana-plugin-core-public.app.exactroute.md) | boolean | If set to true, the application's route will only be checked against an exact match. Defaults to false. | | [icon](./kibana-plugin-core-public.app.icon.md) | string | A URL to an image file used as an icon. Used as a fallback if euiIconType is not provided. | | [id](./kibana-plugin-core-public.app.id.md) | string | The unique identifier of the application | -| [meta](./kibana-plugin-core-public.app.meta.md) | AppMeta | Meta data for an application that represent additional information for the app. See [AppMeta](./kibana-plugin-core-public.appmeta.md) | +| [keywords](./kibana-plugin-core-public.app.keywords.md) | string[] | Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. | | [mount](./kibana-plugin-core-public.app.mount.md) | AppMount<HistoryLocationState> | A mount function called when the user navigates to this app's route. | | [navLinkStatus](./kibana-plugin-core-public.app.navlinkstatus.md) | AppNavLinkStatus | The initial status of the application's navLink. Defaulting to visible if status is accessible and hidden if status is inaccessible See [AppNavLinkStatus](./kibana-plugin-core-public.appnavlinkstatus.md) | | [order](./kibana-plugin-core-public.app.order.md) | number | An ordinal used to sort nav links relative to one another for display. | diff --git a/docs/development/core/public/kibana-plugin-core-public.app.meta.md b/docs/development/core/public/kibana-plugin-core-public.app.meta.md deleted file mode 100644 index 574fa11605aec..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.app.meta.md +++ /dev/null @@ -1,43 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [App](./kibana-plugin-core-public.app.md) > [meta](./kibana-plugin-core-public.app.meta.md) - -## App.meta property - -Meta data for an application that represent additional information for the app. See [AppMeta](./kibana-plugin-core-public.appmeta.md) - -Signature: - -```typescript -meta?: AppMeta; -``` - -## Remarks - -Used to populate navigational search results (where available). Can be updated using the [App.updater$](./kibana-plugin-core-public.app.updater_.md) observable. See [PublicAppSearchDeepLinkInfo](./kibana-plugin-core-public.publicappsearchdeeplinkinfo.md) for more details. - -## Example - - -```ts -core.application.register({ - id: 'my_app', - title: 'Translated title', - meta: { - keywords: ['translated keyword1', 'translated keyword2'], - searchDeepLinks: [ - { id: 'sub1', title: 'Sub1', path: '/sub1', keywords: ['subpath1'] }, - { - id: 'sub2', - title: 'Sub2', - searchDeepLinks: [ - { id: 'subsub', title: 'SubSub', path: '/sub2/sub', keywords: ['subpath2'] } - ] - } - ], - }, - mount: () => { ... } -}) - -``` - diff --git a/docs/development/core/public/kibana-plugin-core-public.appdeeplink.md b/docs/development/core/public/kibana-plugin-core-public.appdeeplink.md new file mode 100644 index 0000000000000..5aa951cffdcb5 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.appdeeplink.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AppDeepLink](./kibana-plugin-core-public.appdeeplink.md) + +## AppDeepLink type + +Input type for registering secondary in-app locations for an application. + +Deep links must include at least one of `path` or `deepLinks`. A deep link that does not have a `path` represents a topological level in the application's hierarchy, but does not have a destination URL that is user-accessible. + +Signature: + +```typescript +export declare type AppDeepLink = { + id: string; + title: string; + keywords?: string[]; + navLinkStatus?: AppNavLinkStatus; +} & ({ + path: string; + deepLinks?: AppDeepLink[]; +} | { + path?: string; + deepLinks: AppDeepLink[]; +}); +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.appmeta.keywords.md b/docs/development/core/public/kibana-plugin-core-public.appmeta.keywords.md deleted file mode 100644 index 13709df68e76a..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.appmeta.keywords.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AppMeta](./kibana-plugin-core-public.appmeta.md) > [keywords](./kibana-plugin-core-public.appmeta.keywords.md) - -## AppMeta.keywords property - -Keywords to represent this application - -Signature: - -```typescript -keywords?: string[]; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.appmeta.md b/docs/development/core/public/kibana-plugin-core-public.appmeta.md deleted file mode 100644 index a2b72f7ec799d..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.appmeta.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AppMeta](./kibana-plugin-core-public.appmeta.md) - -## AppMeta interface - -Input type for meta data for an application. - -Meta fields include `keywords` and `searchDeepLinks` Keywords is an array of string with which to associate the app, must include at least one unique string as an array. `searchDeepLinks` is an array of links that represent secondary in-app locations for the app. - -Signature: - -```typescript -export interface AppMeta -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [keywords](./kibana-plugin-core-public.appmeta.keywords.md) | string[] | Keywords to represent this application | -| [searchDeepLinks](./kibana-plugin-core-public.appmeta.searchdeeplinks.md) | AppSearchDeepLink[] | Array of links that represent secondary in-app locations for the app. | - diff --git a/docs/development/core/public/kibana-plugin-core-public.appmeta.searchdeeplinks.md b/docs/development/core/public/kibana-plugin-core-public.appmeta.searchdeeplinks.md deleted file mode 100644 index 7ec0bbaa4b418..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.appmeta.searchdeeplinks.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AppMeta](./kibana-plugin-core-public.appmeta.md) > [searchDeepLinks](./kibana-plugin-core-public.appmeta.searchdeeplinks.md) - -## AppMeta.searchDeepLinks property - -Array of links that represent secondary in-app locations for the app. - -Signature: - -```typescript -searchDeepLinks?: AppSearchDeepLink[]; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.appsearchdeeplink.md b/docs/development/core/public/kibana-plugin-core-public.appsearchdeeplink.md deleted file mode 100644 index 29aad675fb105..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.appsearchdeeplink.md +++ /dev/null @@ -1,26 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AppSearchDeepLink](./kibana-plugin-core-public.appsearchdeeplink.md) - -## AppSearchDeepLink type - -Input type for registering secondary in-app locations for an application. - -Deep links must include at least one of `path` or `searchDeepLinks`. A deep link that does not have a `path` represents a topological level in the application's hierarchy, but does not have a destination URL that is user-accessible. - -Signature: - -```typescript -export declare type AppSearchDeepLink = { - id: string; - title: string; -} & ({ - path: string; - searchDeepLinks?: AppSearchDeepLink[]; - keywords?: string[]; -} | { - path?: string; - searchDeepLinks: AppSearchDeepLink[]; - keywords?: string[]; -}); -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.appupdatablefields.md b/docs/development/core/public/kibana-plugin-core-public.appupdatablefields.md index 55672d9339f61..d7b12d4b70701 100644 --- a/docs/development/core/public/kibana-plugin-core-public.appupdatablefields.md +++ b/docs/development/core/public/kibana-plugin-core-public.appupdatablefields.md @@ -9,5 +9,5 @@ Defines the list of fields that can be updated via an [AppUpdater](./kibana-plug Signature: ```typescript -export declare type AppUpdatableFields = Pick; +export declare type AppUpdatableFields = Pick; ``` diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.md b/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.md index 2d879a468f587..c12fb45e6ab42 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.md @@ -23,5 +23,4 @@ export interface ChromeNavLinks | [getNavLinks$()](./kibana-plugin-core-public.chromenavlinks.getnavlinks_.md) | Get an observable for a sorted list of navlinks. | | [has(id)](./kibana-plugin-core-public.chromenavlinks.has.md) | Check whether or not a navlink exists. | | [showOnly(id)](./kibana-plugin-core-public.chromenavlinks.showonly.md) | Remove all navlinks except the one matching the given id. | -| [update(id, values)](./kibana-plugin-core-public.chromenavlinks.update.md) | Update the navlink for the given id with the updated attributes. Returns the updated navlink or undefined if it does not exist. | diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.update.md b/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.update.md deleted file mode 100644 index 7948f2f8543fd..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.chromenavlinks.update.md +++ /dev/null @@ -1,30 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeNavLinks](./kibana-plugin-core-public.chromenavlinks.md) > [update](./kibana-plugin-core-public.chromenavlinks.update.md) - -## ChromeNavLinks.update() method - -> Warning: This API is now obsolete. -> -> Uses the property when registering your application with [ApplicationSetup.register()](./kibana-plugin-core-public.applicationsetup.register.md) instead. -> - -Update the navlink for the given id with the updated attributes. Returns the updated navlink or `undefined` if it does not exist. - -Signature: - -```typescript -update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| id | string | | -| values | ChromeNavLinkUpdateableFields | | - -Returns: - -`ChromeNavLink | undefined` - diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavlinkupdateablefields.md b/docs/development/core/public/kibana-plugin-core-public.chromenavlinkupdateablefields.md deleted file mode 100644 index 0445bb28bb355..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.chromenavlinkupdateablefields.md +++ /dev/null @@ -1,12 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeNavLinkUpdateableFields](./kibana-plugin-core-public.chromenavlinkupdateablefields.md) - -## ChromeNavLinkUpdateableFields type - - -Signature: - -```typescript -export declare type ChromeNavLinkUpdateableFields = Partial>; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index 0448ad42c94fa..9930ab7319f65 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -111,6 +111,7 @@ readonly links: { readonly introduction: string; readonly fieldFormattersNumber: string; readonly fieldFormattersString: string; + readonly runtimeFields: string; }; readonly addData: string; readonly kibana: string; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 78d2d8daa3d45..ab8cdea5e4d86 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
} | | diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index ae16c1c0e5887..6239279f275d1 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -37,7 +37,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AppLeaveDefaultAction](./kibana-plugin-core-public.appleavedefaultaction.md) | Action to return from a [AppLeaveHandler](./kibana-plugin-core-public.appleavehandler.md) to execute the default behaviour when leaving the application.See | | [ApplicationSetup](./kibana-plugin-core-public.applicationsetup.md) | | | [ApplicationStart](./kibana-plugin-core-public.applicationstart.md) | | -| [AppMeta](./kibana-plugin-core-public.appmeta.md) | Input type for meta data for an application.Meta fields include keywords and searchDeepLinks Keywords is an array of string with which to associate the app, must include at least one unique string as an array. searchDeepLinks is an array of links that represent secondary in-app locations for the app. | | [AppMountParameters](./kibana-plugin-core-public.appmountparameters.md) | | | [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md) | A plugin with asynchronous lifecycle methods. | | [Capabilities](./kibana-plugin-core-public.capabilities.md) | The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled. | @@ -144,17 +143,16 @@ The plugin integrates with the core system via lifecycle events: `setup` | Type Alias | Description | | --- | --- | +| [AppDeepLink](./kibana-plugin-core-public.appdeeplink.md) | Input type for registering secondary in-app locations for an application.Deep links must include at least one of path or deepLinks. A deep link that does not have a path represents a topological level in the application's hierarchy, but does not have a destination URL that is user-accessible. | | [AppLeaveAction](./kibana-plugin-core-public.appleaveaction.md) | Possible actions to return from a [AppLeaveHandler](./kibana-plugin-core-public.appleavehandler.md)See [AppLeaveConfirmAction](./kibana-plugin-core-public.appleaveconfirmaction.md) and [AppLeaveDefaultAction](./kibana-plugin-core-public.appleavedefaultaction.md) | | [AppLeaveHandler](./kibana-plugin-core-public.appleavehandler.md) | A handler that will be executed before leaving the application, either when going to another application or when closing the browser tab or manually changing the url. Should return confirm to prompt a message to the user before leaving the page, or default to keep the default behavior (doing nothing).See [AppMountParameters](./kibana-plugin-core-public.appmountparameters.md) for detailed usage examples. | | [AppMount](./kibana-plugin-core-public.appmount.md) | A mount function called when the user navigates to this app's route. | -| [AppSearchDeepLink](./kibana-plugin-core-public.appsearchdeeplink.md) | Input type for registering secondary in-app locations for an application.Deep links must include at least one of path or searchDeepLinks. A deep link that does not have a path represents a topological level in the application's hierarchy, but does not have a destination URL that is user-accessible. | | [AppUnmount](./kibana-plugin-core-public.appunmount.md) | A function called when an application should be unmounted from the page. This function should be synchronous. | | [AppUpdatableFields](./kibana-plugin-core-public.appupdatablefields.md) | Defines the list of fields that can be updated via an [AppUpdater](./kibana-plugin-core-public.appupdater.md). | | [AppUpdater](./kibana-plugin-core-public.appupdater.md) | Updater for applications. see [ApplicationSetup](./kibana-plugin-core-public.applicationsetup.md) | | [ChromeBreadcrumb](./kibana-plugin-core-public.chromebreadcrumb.md) | | | [ChromeHelpExtensionLinkBase](./kibana-plugin-core-public.chromehelpextensionlinkbase.md) | | | [ChromeHelpExtensionMenuLink](./kibana-plugin-core-public.chromehelpextensionmenulink.md) | | -| [ChromeNavLinkUpdateableFields](./kibana-plugin-core-public.chromenavlinkupdateablefields.md) | | | [FatalErrorsStart](./kibana-plugin-core-public.fatalerrorsstart.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. | | [HttpStart](./kibana-plugin-core-public.httpstart.md) | See [HttpSetup](./kibana-plugin-core-public.httpsetup.md) | | [IToasts](./kibana-plugin-core-public.itoasts.md) | Methods for adding and removing global toast messages. See [ToastsApi](./kibana-plugin-core-public.toastsapi.md). | @@ -162,9 +160,8 @@ The plugin integrates with the core system via lifecycle events: `setup` | [NavType](./kibana-plugin-core-public.navtype.md) | | | [PluginInitializer](./kibana-plugin-core-public.plugininitializer.md) | The plugin export at the root of a plugin's public directory should conform to this interface. | | [PluginOpaqueId](./kibana-plugin-core-public.pluginopaqueid.md) | | +| [PublicAppDeepLinkInfo](./kibana-plugin-core-public.publicappdeeplinkinfo.md) | Public information about a registered app's [deepLinks](./kibana-plugin-core-public.appdeeplink.md) | | [PublicAppInfo](./kibana-plugin-core-public.publicappinfo.md) | Public information about a registered [application](./kibana-plugin-core-public.app.md) | -| [PublicAppMetaInfo](./kibana-plugin-core-public.publicappmetainfo.md) | Public information about a registered app's [keywords](./kibana-plugin-core-public.appmeta.md) | -| [PublicAppSearchDeepLinkInfo](./kibana-plugin-core-public.publicappsearchdeeplinkinfo.md) | Public information about a registered app's [searchDeepLinks](./kibana-plugin-core-public.appsearchdeeplink.md) | | [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side. | | [ResolveDeprecationResponse](./kibana-plugin-core-public.resolvedeprecationresponse.md) | | | [SavedObjectAttribute](./kibana-plugin-core-public.savedobjectattribute.md) | Type definition for a Saved Object attribute value | diff --git a/docs/development/core/public/kibana-plugin-core-public.publicappdeeplinkinfo.md b/docs/development/core/public/kibana-plugin-core-public.publicappdeeplinkinfo.md new file mode 100644 index 0000000000000..d3a6a4de905fd --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.publicappdeeplinkinfo.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PublicAppDeepLinkInfo](./kibana-plugin-core-public.publicappdeeplinkinfo.md) + +## PublicAppDeepLinkInfo type + +Public information about a registered app's [deepLinks](./kibana-plugin-core-public.appdeeplink.md) + +Signature: + +```typescript +export declare type PublicAppDeepLinkInfo = Omit & { + deepLinks: PublicAppDeepLinkInfo[]; + keywords: string[]; + navLinkStatus: AppNavLinkStatus; +}; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.publicappinfo.md b/docs/development/core/public/kibana-plugin-core-public.publicappinfo.md index 9f45a06935fe4..a5563eae83563 100644 --- a/docs/development/core/public/kibana-plugin-core-public.publicappinfo.md +++ b/docs/development/core/public/kibana-plugin-core-public.publicappinfo.md @@ -9,10 +9,11 @@ Public information about a registered [application](./kibana-plugin-core-public. Signature: ```typescript -export declare type PublicAppInfo = Omit & { +export declare type PublicAppInfo = Omit & { status: AppStatus; navLinkStatus: AppNavLinkStatus; appRoute: string; - meta: PublicAppMetaInfo; + keywords: string[]; + deepLinks: PublicAppDeepLinkInfo[]; }; ``` diff --git a/docs/development/core/public/kibana-plugin-core-public.publicappmetainfo.md b/docs/development/core/public/kibana-plugin-core-public.publicappmetainfo.md deleted file mode 100644 index 3ef0460aec467..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.publicappmetainfo.md +++ /dev/null @@ -1,16 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PublicAppMetaInfo](./kibana-plugin-core-public.publicappmetainfo.md) - -## PublicAppMetaInfo type - -Public information about a registered app's [keywords](./kibana-plugin-core-public.appmeta.md) - -Signature: - -```typescript -export declare type PublicAppMetaInfo = Omit & { - keywords: string[]; - searchDeepLinks: PublicAppSearchDeepLinkInfo[]; -}; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.publicappsearchdeeplinkinfo.md b/docs/development/core/public/kibana-plugin-core-public.publicappsearchdeeplinkinfo.md deleted file mode 100644 index e88cdb7d55edd..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.publicappsearchdeeplinkinfo.md +++ /dev/null @@ -1,16 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PublicAppSearchDeepLinkInfo](./kibana-plugin-core-public.publicappsearchdeeplinkinfo.md) - -## PublicAppSearchDeepLinkInfo type - -Public information about a registered app's [searchDeepLinks](./kibana-plugin-core-public.appsearchdeeplink.md) - -Signature: - -```typescript -export declare type PublicAppSearchDeepLinkInfo = Omit & { - searchDeepLinks: PublicAppSearchDeepLinkInfo[]; - keywords: string[]; -}; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md new file mode 100644 index 0000000000000..d9c47dec9e9d4 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) > [controlledBy](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md) + +## ApplyGlobalFilterActionContext.controlledBy property + +Signature: + +```typescript +controlledBy?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md index 2f844b6844645..01ccd4819d906 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md @@ -14,6 +14,7 @@ export interface ApplyGlobalFilterActionContext | Property | Type | Description | | --- | --- | --- | +| [controlledBy](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.controlledby.md) | string | | | [embeddable](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md) | unknown | | | [filters](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.filters.md) | Filter[] | | | [timeFieldName](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.timefieldname.md) | string | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md index 742b54e19216e..54b5a33ccf682 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md @@ -33,6 +33,7 @@ esFilters: { disabled: boolean; controlledBy?: string | undefined; index?: string | undefined; + isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.md index b47e00542da97..d3abc8bcaf44b 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchstrategydependencies.md @@ -15,6 +15,7 @@ export interface SearchStrategyDependencies | Property | Type | Description | | --- | --- | --- | | [esClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.esclient.md) | IScopedClusterClient | | +| [request](./kibana-plugin-plugins-data-server.searchstrategydependencies.request.md) | KibanaRequest | | | [savedObjectsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.savedobjectsclient.md) | SavedObjectsClientContract | | | [searchSessionsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.searchsessionsclient.md) | IScopedSearchSessionsClient | | | [uiSettingsClient](./kibana-plugin-plugins-data-server.searchstrategydependencies.uisettingsclient.md) | IUiSettingsClient | | diff --git a/docs/discover/save-search.asciidoc b/docs/discover/save-search.asciidoc index edfdae9a6b081..b59f14180b1ff 100644 --- a/docs/discover/save-search.asciidoc +++ b/docs/discover/save-search.asciidoc @@ -35,8 +35,9 @@ image::discover/images/read-only-badge.png[Example of Discover's read only acces If the saved search is associated with a different index pattern than is currently selected, opening the saved search changes the selected index pattern. The query language used for the saved search is also automatically selected. -. To add your search results to an existing dashboard: -.. Open the dashboard, then click *Edit*. +. To add your search results to a dashboard: +.. Open the main menu, then click *Dashboard*. +.. Open or create the dashboard, then click *Edit*. .. Click *Add from library*. -.. Open the *Types* menu, then select *Saved search*. -.. Select the the saved search that you want. +.. From the *Types* dropdown, select *Saved search*. +.. Select the saved search that you want to visualize, then click *X* to close the list. diff --git a/docs/maps/connect-to-ems.asciidoc b/docs/maps/connect-to-ems.asciidoc index 88301123bae3f..a54da6597b9b0 100644 --- a/docs/maps/connect-to-ems.asciidoc +++ b/docs/maps/connect-to-ems.asciidoc @@ -163,7 +163,7 @@ services: {hosted-ems}: image: {ems-docker-image} volumes: - - ./elastic-maps-server.yml:/usr/src/app/config/elastic-maps-server.yml + - ./elastic-maps-server.yml:/usr/src/app/server/config/elastic-maps-server.yml -------------------------------------------- [float] diff --git a/docs/maps/index.asciidoc b/docs/maps/index.asciidoc index e4150fc280096..45d24bfb5a7e4 100644 --- a/docs/maps/index.asciidoc +++ b/docs/maps/index.asciidoc @@ -1,6 +1,5 @@ :ems-docker-repo: docker.elastic.co/elastic-maps-service/elastic-maps-server-ubi8 :ems-docker-image: {ems-docker-repo}:{version} -:hosted-ems: Elastic Maps Server [role="xpack"] [[maps]] diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 0aab86fb5a9e2..bac2b0ebdf15f 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -723,6 +723,12 @@ out through *Advanced Settings*. *Default: `true`* | Set this value to true to allow Vega to use any URL to access external data sources and images. When false, Vega can only get data from {es}. *Default: `false`* +| `xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled` + | Enables the *Explore underlying data* option that allows you to open *Discover* from a dashboard panel and view the panel data. *Default: `false`* + + | `xpack.discoverEnhanced.actions.exploreDataInChart.enabled` + | Enables you to view the underlying documents in a data series from a dashboard panel. *Default: `false`* + | `xpack.license_management.enabled` | Set this value to false to disable the License Management UI. *Default: `true`* diff --git a/docs/user/api.asciidoc b/docs/user/api.asciidoc index 6daf252c524dd..e4faa81c174e9 100644 --- a/docs/user/api.asciidoc +++ b/docs/user/api.asciidoc @@ -99,6 +99,7 @@ include::{kib-repo-dir}/api/spaces-management.asciidoc[] include::{kib-repo-dir}/api/role-management.asciidoc[] include::{kib-repo-dir}/api/session-management.asciidoc[] include::{kib-repo-dir}/api/saved-objects.asciidoc[] +include::{kib-repo-dir}/api/index-patterns.asciidoc[] include::{kib-repo-dir}/api/alerting.asciidoc[] include::{kib-repo-dir}/api/actions-and-connectors.asciidoc[] include::{kib-repo-dir}/api/dashboard-api.asciidoc[] diff --git a/docs/user/dashboard/aggregation-based.asciidoc b/docs/user/dashboard/aggregation-based.asciidoc index 49c092f8baa4c..cb102b73f93b4 100644 --- a/docs/user/dashboard/aggregation-based.asciidoc +++ b/docs/user/dashboard/aggregation-based.asciidoc @@ -92,17 +92,17 @@ create visual art for a specific topic. Choose the type of visualization you want to create, then use the editor to configure the options. -. From the dashboard, click *Create panel*, then click *Aggregation based* on the *New visualization* window. +. On the dashboard, click *All types > Aggregation based*. -.. Click the type of visualization you want to create. +.. Select the visualization type you want to create. -.. Click the data source you want to visualize. +.. Select the data source you want to visualize. -. From the editor, add the <> you want to visualize, then click *Update*. +. Add the <> you want to visualize using the editor, then click *Update*. + NOTE: For the *Date Histogram* to use an *auto interval*, the date field must match the primary time field of the index pattern. -. To change the order, drag the aggregations along the editor. +. To change the order, drag and drop the aggregations in the editor. + [role="screenshot"] image:images/bar-chart-tutorial-3.png[Option to change the order of aggregations] @@ -137,13 +137,9 @@ Add the sample web logs data that you'll use to create the bar chart, then creat Open the bar chart visualization builder and change the time range. -. On the dashboard, click *Create panel*. +. On the dashboard, click *All types > Aggregation based*, select *Vertical bar*, then select *kibana_sample_data_logs*. -. On the *New visualization* window, click *Aggregation based > Vertical bar*. - -. On the *Choose a source* window, click *kibana_sample_data_logs*. - -. Change the <>> to *Last 7 days*. +. Make sure the <>> is *Last 7 days*. [float] [[tutorial-configure-the-bar-chart]] @@ -177,19 +173,6 @@ TIP: Aggregation-based panels support a maximum of three *Split series*. [role="screenshot"] image:images/bar-chart-tutorial-2.png[Bar chart with sample logs data] -[float] -===== Save the panel - -Save and add the visualization panel to the dashboard. - -. From the toolbar, click *Save*. - -. Enter the *Title* and optional *Description*. - -. From the *Tags* drop down, select any applicable tags. - -. Select *Add to Dashboard after saving*. - . Click *Save and return*. diff --git a/docs/user/dashboard/create-panels-with-editors.asciidoc b/docs/user/dashboard/create-panels-with-editors.asciidoc index 8e047819fd1c6..17d3b5fb8a8a5 100644 --- a/docs/user/dashboard/create-panels-with-editors.asciidoc +++ b/docs/user/dashboard/create-panels-with-editors.asciidoc @@ -1,13 +1,13 @@ [[create-panels-with-editors]] == Create panels with editors -{kib} provides several editors that you can use to create dashboard panels. +{kib} provides several editors that you can use to create panels of your data. [cols="2"] |=== | <> -| Create visualizations with the drag and drop editor. *Lens* is recommended for most users. +| Create visualizations with the drag and drop editor. | <> | Create visualizations with your geographical data. diff --git a/docs/user/dashboard/dashboard.asciidoc b/docs/user/dashboard/dashboard.asciidoc index 070d511ed8073..8226e9c6ed073 100644 --- a/docs/user/dashboard/dashboard.asciidoc +++ b/docs/user/dashboard/dashboard.asciidoc @@ -6,35 +6,34 @@ **_Visualize your data with dashboards._** The best way to understand your data is to visualize it. With dashboards, you can turn your data from one or more <> into a collection of panels -that bring clarity to your data, tell a story about your data, and allow you to focus on only the data that's important to you. Configure each panel to display your data in a chart, table, map, and more, -then compare the panels side-by-side to identify the patterns and connections in your data. +that bring clarity to your data, tell a story about your data, and allow you to focus on only the data that's important to you. [role="screenshot"] image:images/Dashboard_example.png[Example dashboard] -Dashboards support many types of panels, and provide several editors that you can use to create panels. +Panels display your data in charts, tables, maps, and more, which allow you to compare your data side-by-side to identify patterns and connections. Dashboards support several editors you can use to create panels, and support many types of panels to display your data. [cols="2"] |=== -| <> -| Use the *Lens*, *TSVB*, *Vega*, and *Timelion* editors to help you create visualizations of your data, or create aggregation-based visualizations using {es} <>. -*Lens* is recommended for most users. +| <> +| Use the *Lens*, *TSVB*, *Vega*, and *Timelion* editors to create visualizations of your data, or create *Aggregation based* visualizations using {es} aggregations. +*Lens* is the recommended editor. | <> | Create beautiful displays of your geographical data. -| <> -| Add context to your panels with <>, or add dynamic filters with <>. +| <> +| Display the results from machine learning anomaly detection jobs. -| <> -| Display a saved search table from <>. The table results are not aggregated. +| <> +| Display an anomaly chart from the *Anomaly Explorer. | <> | Display a table of live streaming logs. -| <> -| Display the results from machine learning anomaly detection jobs. +| <> +| Add context to your panels with <>, or add dynamic filters with <>. |=== @@ -54,10 +53,8 @@ To create dashboards, you must meet the minimum requirements. * Make sure you have {ref}/getting-started-index.html[data indexed into {es}] and an <>. -* Have an understanding of {ref}/documents-indices.html[{es} documents and indices]. - * When the read-only indicator appears, you have insufficient privileges -to create or save dashboards. The options to create and save dashboards are not visible. For more information, +to create or save dashboards, and the options to create and save dashboards are not visible. For more information, refer to <>. [float] @@ -74,281 +71,225 @@ Begin with an empty dashboard, or open an existing dashboard. * To open an existing dashboard, click the dashboard *Title* you want to open. -[float] -[[add-panels]] -=== Add panels - -To add panels to the dashboard, you can use one of the editors to create a new panel, -add an existing panel from the *Visualize Library*, add a table of live streaming logs, or the add the results from a machine learning anomaly detection job. - [float] [[create-panels-with-lens]] -==== Create panels - -To create panels, use one of the editors, then add the panel to the dashboard. - -. On the dashboard, click *Create panel*. +=== Add panels -. On the *New visualization* window, click the editor you want to use. *Lens* is recommended for most users. +Create and add panels of your data to the dashboard, or add existing panels from the library. -. To create the panel, configure the editor options. +* *Create visualization* — Opens *Lens*, the recommended editor to create visualizations of your data. -. To add the panel to the dashboard, choose one of the following options: +* *All types* — Select the editor to create the panel, or select the panel type you want to add to the dashboard. -* To add the panel to the dashboard without saving to the *Visualize Library*, click *Save and return*. +* *Add from library* — Add panels from the *Visualize Library*, including search results from <>. The search results from *Discover* are not aggregated. + -To add a title to the panel, click *No Title*, enter the *Panel title*, then click *Save*. +When a panel contains a saved query, both queries are applied. -* To save the panel to the *Visualize Library*, click *Save to Library*, configure the options, then click *Save and return*. -+ -When panels are saved in the *Visualize Library*, image:dashboard/images/visualize-library-icon.png[Visualize Library icon] appears in the header. +[[tsvb]] [float] -[[add-panels-from-the-library]] -==== Add panels from the library - -Add panels that you've already created from the *Visualize Library*. - -. On the dashboard, click *Add from library*. +[[save-panels]] +=== Save panels -. On the *Add from library* flyout, click the panels you want to add to the dashboard. +Consider where you want to save the panel in {kib}. You can save the panel just on the dashboard you are working on, or save the panel in the *Visualize Library*. -. To close the flyout, click *X*. -+ -When a panel contains a stored query, both queries are applied. - -. To make changes to the panel, open the panel menu, then select the following options: - -* *Edit visualization* — Opens an editor so that you can reconfigure the panel. -+ -To make changes to the panel without affecting the original version, open the panel menu, then click *More > Unlink from library*. +[float] +[[save-to-visualize-library]] +==== Save to the Visualize Library -* *Edit panel title* — Opens the *Customize panel* window to change the *Panel title* and specify whether you want to display the panel title. +To use the panel on *Canvas* workpads and other dashboards, save the panel to the *Visualize Library*. -[float] -[[add-a-table-of-live-streaming-logs]] -==== Add a logs panel +. Click *Save to library*. -Add a panel that displays a table of live streaming logs. +. Enter the *Title* and add any applicable *Tags*. -. On the dashboard, click *Add from library*. +. Make sure that *Add to Dashboard after saving* is selected. -. On the *Add from library* flyout, click *Create new*, then select *Log stream*. +. Click *Save and return*. ++ +When panels are saved in the *Visualize Library*, image:dashboard/images/visualize-library-icon.png[Visualize Library icon] appears in the panel header. [float] -[[add-machine-learning-results]] -==== Add machine learning results +[[save-to-the-dashboard]] +==== Save to the dashboard -Add a panel that displays the results from machine learning anomaly detection jobs. +Quickly add the panel and return to the dashboard without specifying the save options or adding the panel to the *Visualize Library*. -. On the dashboard, click *Add from library*. +. Click *Save and return*. -. On the *Add from library* flyout, click *Create new*, then select *ML Anomaly Swim Lane*. +. Add more panels to the dashboard, or specify the panel title. -[[tsvb]] +.. In the panel header, click *No Title*. -[float] -[[arrange-panels]] -[[moving-containers]] -[[resizing-containers]] -=== Arrange the panels +.. Select *Show panel title*. -To compare the data in the panels, reorganize or remove the panels on the dashboard. +.. Enter the *Panel title*. -. From the toolbar, click *Edit*, then use the following options: +If you change your mind and want to add the panel to the *Visualize Library*: -* To move, click and hold the panel header, then drag to the new location. +. Open the panel menu, then select *More > Save to library*. -* To resize, click the resize control, then drag to the new dimensions. +. Enter the panel title, then click *Save*. -* To maximize the panel to fullscreen, open the panel menu, then click *More > Maximize panel*. +[float] +[[edit-panels]] +== Edit panels -* To delete, open the panel menu, then click *More > Delete from dashboard*. +To make changes to the panel, use the panel menu options. -. To save your changes, click *Save* in the toolbar. +. In the toolbar, click *Edit*. -[float] -[[apply-design-options]] -=== Apply design options +. Open the panel menu, then use the following options: -Apply a set of design options to the entire dashboard. +* *Edit lens* — Opens *Lens* so you can make changes to the visualization. -. From the toolbar, click *Edit > Options*. - -. Select the following options: +* *Edit visualization* — Opens the editor so you can make changes to the panel. ++ +To make changes without changing the original version, open the panel menu, then click *More > Unlink from library*. -* *Use margins between panels* — Specifies a margin of space between each panel. +* *Edit panel title* — Opens the *Customize panel* window to change the *Panel title*. -* *Show panel titles* — Specifies the appearance of titles in the header of each panel. +* *More > Replace panel* — Opens the *Visualize Library* so you can select a new panel to replace the existing panel. -* *Sync color pallettes across panels* — Specifies whether the color pallette is applied to all panels. +* *More > Delete from dashboard* — Removes the panel from the dashboard. ++ +If you want to use the panel later, make sure that you save the panel to the *Visualize Library*. [float] [[search-or-filter-your-data]] -=== Search or filter your data +== Search and filter your data -{kib} provides you with several ways to search your data and apply {es} filters. You can combine the filters with any panel +{kib} supports several ways to search your data and apply {es} filters. You can combine the filters with any panel filter to display the data want to you see. -[float] -[[semi-structured-search]] -==== Semi-structured search - -Combine free text search with field-based search using the <>. -Type a search term to match across all fields, or begin typing a field name to -get prompted with field names and operators you can use to build a structured query. +For more information about {kib} and {es} filters, refer to <>. -For example, in the sample web logs data, the following query displays data only for the US: +To apply a panel-level time filter: -. Enter `g`, then select *geo.source*. +. Open the panel menu, then select *More > Customize time range*. -. Select *equals some value* and *US*, then click *Update*. +. Enter the time range you want to view, then click *Add to panel*. -. For a more complex search, try: +[float] +[[arrange-panels]] +[[moving-containers]] +[[resizing-containers]] +== Arrange panels -[source,text] -------------------- -geo.src : "US" and url.keyword : "https://www.elastic.co/downloads/beats/metricbeat" -------------------- +To compare the data side-by-side, move and arrange the panels. -[float] -[[time-filter]] -==== Time filter +In the toolbar, click *Edit*, then use the following options: -The <> restrict the data that appears on the dashboard, but you can override the time filter with panel filters. - -. To update the time filter, add a panel that displays time-based data along the x-axis. +* To move, click and hold the panel header, then drag to the new location. -. Open the panel menu, then select *More > Customize time range*. +* To resize, click the resize control, then drag to the new dimensions. -. On the *Customize panel time range* window, specify the time range, then click *Add to panel*. -+ -[role="screenshot"] -image:images/time_range_per_panel.gif[Time range per dashboard panel] +* To maximize to fullscreen, open the panel menu, then click *More > Maximize panel*. [float] -[[additional-filters-with-and]] -==== Additional filters with AND - -Add filters to a dashboard, or pin filters to multiple places in {kib}. To add filters, you can use the *Edit Filter* options, or the advanced JSON editor for the {es} {ref}/query-dsl.html[Query DSL]. -When there is one or more index patterns on the dashboard, you can select the index pattern that contains the fields you want to create the filter. +[[apply-design-options]] +== Apply design options -For example, to filter the dashboard to display only ios data from *kibana_sample_data_logs*: +Apply a set of design options to the entire dashboard. -. Click *Add filter*. +In the toolbar, click *Edit > Options*, then use the following options: -. From the *Index Pattern* dropdown, select *kibana_sample_data_logs*. +* *Use margins between panels* — Specifies a margin of space between each panel. -. Set *Field* to *machine.os*, *Operator* to *is*, and *Value* to *ios*. +* *Show panel titles* — Specifies the appearance of titles in the header of each panel. -. *Save* the filter. -+ -To remove the filter, click *x*. +* *Sync color pallettes across panels* — Specifies whether the color pallette is applied to all panels. [float] -[[add-dynamic-filters]] -==== Add dynamic filters +[[duplicate-panels]] +== Duplicate panels -When you see data in a panel that you want to use as a filter, you can dynamically create the filter. To dynamically add filters, click the data in a panel. - -. Click the data in the panel. - -. Select filters you want to apply to all of the dashboard panels, then click *Apply*. -+ -To remove the filters, click *x*. +To duplicate a panel and the configured functionality, use the clone and copy panel options. Cloned and copied panels replicate all of the functionality from the original panel, +including renaming, editing, and cloning. [float] [[clone-panels]] === Clone panels -To duplicate a panel and the configured functionality, clone the panel. Cloned panels continue to replicate all of the functionality from the original panel, -including renaming, editing, and cloning. When you clone a panel, the clone appears beside the original panel, and moves other panels to provide a space on the -dashboard. +Cloned panels appear next to the original panel, and move the other panels to provide a space on the dashboard. -. From the toolbar, click *Edit*. +. In the toolbar, click *Edit*. . Open the panel menu, then select *Clone panel*. + -[role="screenshot"] -image:images/clone_panel.gif[clone panel] -+ When cloned panels are saved in the *Visualize Library*, image:dashboard/images/visualize-library-icon.png[Visualize Library icon] appears in the header. [float] [[copy-to-dashboard]] === Copy panels -To add a panel to another dashboard, copy the panel. +Copy panels from one dashboard to another dashboard. . Open the panel menu, then select *More > Copy to dashboard*. . On the *Copy to dashboard* window, select the dashboard, then click *Copy and go to dashboard*. [float] -[[explore-the-underlying-data]] -=== Explore the underlying documents +[[explore-the-underlying-documents]] +== Explore the underlying documents -View the underlying documents in a panel, or in a data series. +To gain insight to the data, open the underlying panel or data series documents in *Discover*. The panel documents that you open in *Discover* have the same time range and filters as the source panel. -. In kibana.yml, add the following: -+ -["source","yml"] ------------ -xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled: true ------------ - -TIP: *Explore underlying data* is supported only for visualization panels with a single index pattern. +[float] +[[explore-underlying-panel-documents]] +=== Explore the underlying panel documents -To view the underlying documents in the panel: +When your visualization panel contains a single index pattern, you can open the panel documents in *Discover*. . Open the panel menu. . Click *Explore underlying data*. + -*Discover* opens with the same time range and filters as the panel. -+ [role="screenshot"] image::images/explore_data_context_menu.png[Explore underlying data from panel context menu] -To view the underlying documents in a data series: +[float] +[[explore-underlying-data-series-documents]] +=== Explore the underlying data series documents -. In kibana.yml, add the following: -+ -["source","yml"] ------------ -xpack.discoverEnhanced.actions.exploreDataInChart.enabled: true ------------ +To gain insight to a data series, open the documents in *Discover*. -. Open the dashboard, then click on the data series you want to view. +. Click the data series in the panel that you want to view. + +. Select *Explore underlying data*. + [role="screenshot"] image::images/explore_data_in_chart.png[Explore underlying data from chart] [float] [[download-csv]] -=== Download the panel data +== Download panel data Download panel data in a CSV file. You can download most panels in a CSV file, but there is a shortcut available for *Lens* panels. +[float] [role="xpack"] -To download *Lens* panel data in a CSV file: +[[download-lens-data]] +=== Download Lens data + +When you download *Lens* panel data, each layer produces a single CSV file with columns. +When you download multiple layers, the file names combine the visualization and layer index names. -. Open the *Lens* panel menu. +. Open the *Lens* panel menu . Select *More > Download as CSV*. -+ -[role="screenshot"] -image::images/download_csv_context_menu.png[Download as CSV from panel context menu] -Each layer produces a single CSV file with columns. -When you download multiple layers, the file names combine the visualization and layer index names. +[float] +[[download-other-panel-data]] +=== Download all other panel data -To download all other panel data in a CSV file: +Download the data for non-*Lens* panels. . Open the panel menu, then select *Inspect*. . Click *Download CSV*, then select the CSV type from the dropdown: + * *Formatted CSV* — Contains human-readable dates and numbers. * *Unformatted* — Best used for computer use. @@ -356,40 +297,17 @@ To download all other panel data in a CSV file: [role="screenshot"] image:images/Dashboard_inspect.png[Inspect in dashboard] -[float] -[[save-the-dashboard]] -=== Save the dashboard - -When you're finished making changes, save the dashboard. - -From the toolbar, choose one of the following options: - -* *Save as* — Opens the *Save dashboard* window, which allows you to specify the title and dashboard options. - -* *Save* — Allows you to save the changes you've made to an existing dashboard. - -* *Switch to view mode* — Allows you to exit *Edit* mode without saving your changes, or you can discard the changes you've made. All dashboards with unsaved changes display *Unsaved changes* in the toolbar. - [float] [[share-the-dashboard]] -=== Share the dashboard - -To share the dashboard with a larger audience, click *Share* in the toolbar, then choose one of the following options: - -* *Embed code* — Embed a fully interactive dashboard as an iframe on a web page. To access embedded dashboards, you can require users to -log in using their {kib} credentials, via reverse proxy, or enable <>. - -* *Permalinks* — Share a direct link to a {kib} dashboard. User authentication is required. - -* *PDF Reports* — Generate a PDF report. For more information, refer to <>. +== Share dashboards -* *PNG Reports* — Generate a PNG report. For more information, refer to <>. +To share the dashboard with a larger audience, click *Share* in the toolbar. For detailed information, refer to <>. [float] [[import-dashboards]] -=== Export the dashboard +== Export dashboards -To automate {kib}, you can export dashboards as JSON using the <>. It is important to export dashboards with all references needed. +To automate {kib}, you can export dashboards as JSON using the <>. It is important to export dashboards with all necessary references. -- include::tutorial-create-a-dashboard-of-lens-panels.asciidoc[] diff --git a/docs/user/dashboard/drilldowns.asciidoc b/docs/user/dashboard/drilldowns.asciidoc index fc25f84030ee2..0eb4b43466ff9 100644 --- a/docs/user/dashboard/drilldowns.asciidoc +++ b/docs/user/dashboard/drilldowns.asciidoc @@ -152,7 +152,7 @@ To create dashboard drilldowns, you create or locate the dashboards you want to * *[Logs] Total Requests and Bytes* * *[Logs] Visitors by OS* + -If you don’t see the data on a panel, try changing the <>. +If you don’t see the data on a panel, change the <>. . Save the dashboard. In the *Title* field, enter `Host Overview`. diff --git a/docs/user/dashboard/enhance-dashboards.asciidoc b/docs/user/dashboard/enhance-dashboards.asciidoc index 7176a2e283457..c999ec9b68251 100644 --- a/docs/user/dashboard/enhance-dashboards.asciidoc +++ b/docs/user/dashboard/enhance-dashboards.asciidoc @@ -1,7 +1,7 @@ [[enhance-dashboards]] == Enhance dashboards -Now that you have added panels to your dashboard, you can add filter panels to interact with the data, and Markdown panels to add context to the data. +You can add filter panels to interact with the data in your visualization panels, and Markdown panels to add context to the data. To make your dashboard look the way you want, use the editing options. [float] @@ -21,11 +21,9 @@ min and max aggregation. For example, use the range slider when you want to filt [role="screenshot"] image::images/dashboard-controls.png[] -. From the dashboard, click *Create panel*. +. On the dashboard, click *All types*, then select *Controls*. -. On the *New Visualization* window, click *Controls*. - -. Click *Options*, then configure the following, then click *Update*: +. Click *Options*, then configure the following options: * *Update {kib} filters on each change* — When selected, all interactive inputs create filters that refresh the dashboard. When unselected, {kib} filters are created only when you click *Apply changes*. @@ -34,6 +32,8 @@ image::images/dashboard-controls.png[] * *Pin filters for all applications* — When selected, all filters created by interacting with the inputs are automatically pinned. +. Click *Update* + [float] [[add-text]] === Add text @@ -42,9 +42,7 @@ Add text panels with *Markdown* when you want to provide context to the other pa *Markdown* is a text entry field that accepts GitHub-flavored Markdown text. For information about GitHub-flavored Markdown text, click *Help*. -. From the dashboard, click *Create panel*. - -. On the *New Visualization* window, click *Text*. +. From the dashboard, click *All types*, then select *Text*. . In the *Markdown* field, enter the text, then click *Update*. diff --git a/docs/user/dashboard/lens.asciidoc b/docs/user/dashboard/lens.asciidoc index 94c9db1462760..613432908df3d 100644 --- a/docs/user/dashboard/lens.asciidoc +++ b/docs/user/dashboard/lens.asciidoc @@ -13,11 +13,9 @@ image:dashboard/images/lens.png[Lens] Open *Lens*, then explore the fields in your data. The list of fields are determined by the index pattern and time filter. -. On the dashboard, click *Create panel*. +. On the dashboard, click *Create visualization*. -. On the *New visualization* window, click *Lens*. - -. <>. +. In *Lens*, <>. . To view the fields in the a different index pattern, click the index pattern, then select a different index pattern from the dropdown. @@ -56,11 +54,11 @@ TIP: *Other* can equal more than 100% by a small amount. [float] [[create-the-visualization-panel]] -==== Create the visualization panel +==== Create visualizations -Drag and drop the fields on to the visualization builder, then +Drag and drop the fields on to the visualization builder, then customize the visualization. -. Drag and drop the fields to the visualization builder. +. Drag and drop the fields on to the visualization builder. . To change the visualization type, use the following options: @@ -102,7 +100,7 @@ For more information about adding fields to index patterns and Painless scriptin [float] [[drag-and-drop-keyboard-navigation]] -===== Create visualization panels with keyboard navigation +===== Create visualizations with keyboard navigation *Lens* has a fully accessible, continuously improved drag and drop system, which allows you to use a keyboard instead of a mouse. diff --git a/docs/user/dashboard/timelion.asciidoc b/docs/user/dashboard/timelion.asciidoc index 12d0169c13f66..675fd03df3648 100644 --- a/docs/user/dashboard/timelion.asciidoc +++ b/docs/user/dashboard/timelion.asciidoc @@ -1,7 +1,7 @@ [[timelion]] === Timelion -Instead of using a visual editor to create charts, you define a graph by chaining functions together, using the *Timelion*-specific syntax. +To use *Timelion*, you define a graph by chaining functions together, using the *Timelion*-specific syntax. The syntax enables some features that classical point series charts don't offer, such as pulling data from different indices or data sources into one graph. deprecated::[7.0.0,"*Timelion* is still supported. The *Timelion app* is deprecated in 7.0, replaced by dashboard features. In the last 7.x minor version and later, the *Timelion app* is removed from {kib}. To prepare for the removal of *Timelion app*, you must migrate *Timelion app* worksheets to a dashboard. For information on how to migrate *Timelion app* worksheets, refer to the link:https://www.elastic.co/guide/en/kibana/7.10/release-notes-7.10.0.html#deprecation-v7.10.0[7.10.0 Release Notes]."] @@ -90,11 +90,9 @@ Set up Metricbeat, then create the dashboard. Open *Timelion* and change the time range. -. On the dashboard, click *Create panel*. +. On the dashboard, click *All types > Aggregation based*, then select *Timelion*. -. On the *New visualization* window, click *Aggregation based > Timelion*. - -. Change the <> to *Last 7 days*. +. Make sure the <> is *Last 7 days*. [float] [[timelion-tutorial-create-time-series-visualizations]] @@ -242,20 +240,7 @@ Move the legend to the north west position with two columns, then click *Update image::images/timelion-customize04.png[Final time series visualization] {nbsp} -[float] -==== Save the panel - -Save and add the panel to the dashboard. - -. From the toolbar, click *Save*. - -. Enter the *Title* and optional *Description*. - -. From the *Tags* drop down, select any applicable tags. - -. Select *Add to Dashboard after saving*. - -. Click *Save and return*. +To save the panel, click *Save and return* in the toolbar. [float] [[timelion-tutorial-create-visualizations-with-mathematical-functions]] @@ -364,20 +349,7 @@ Customize and format the visualization using the following functions, then click image::images/timelion-math05.png[Final visualization that displays inbound and outbound network traffic] {nbsp} -[float] -==== Save the panel - -Save and add the panel to the dashboard. - -. From the toolbar, click *Save*. - -. Enter the *Title* and optional *Description*. - -. From the *Tags* drop down, select any applicable tags. - -. Select *Add to Dashboard after saving*. - -. Click *Save and return*. +To save the panel, click *Save and return* in the toolbar. [float] [[timelion-tutorial-create-visualizations-withconditional-logic-and-tracking-trends]] @@ -539,19 +511,6 @@ Customize and format the visualization using the following functions, then click image::images/timelion-conditional04.png[Final visualization that displays outliers and patterns over time] {nbsp} -[float] -==== Save the panel - -Save and add the panel to the dashboard. - -. From the toolbar, click *Save*. - -. Enter the *Title* and optional *Description*. - -. From the *Tags* drop down, select any applicable tags. - -. Select *Add to Dashboard after saving*. - -. Click *Save and return*. +To save the panel, click *Save and return* in the toolbar. For more information about *Timelion* conditions, refer to https://www.elastic.co/blog/timeseries-if-then-else-with-timelion[I have but one .condition()]. diff --git a/docs/user/dashboard/tsvb.asciidoc b/docs/user/dashboard/tsvb.asciidoc index 5c4ce8e365e86..b69df7c7d26d6 100644 --- a/docs/user/dashboard/tsvb.asciidoc +++ b/docs/user/dashboard/tsvb.asciidoc @@ -14,9 +14,7 @@ image::visualize/images/tsvb-screenshot.png[TSVB overview] Open *TSVB*, then make sure the required settings are configured. -. On the dashboard, click *Create panel*. - -. On the *New visualization* window, click *TSVB*. +. On the dashboard, click *All types*, then select *TSVB*. . In *TSVB*, click *Panel options*, then specify the required *Data* settings. diff --git a/docs/user/dashboard/vega.asciidoc b/docs/user/dashboard/vega.asciidoc index b90370af5a12a..faf54e2b7acfc 100644 --- a/docs/user/dashboard/vega.asciidoc +++ b/docs/user/dashboard/vega.asciidoc @@ -37,13 +37,11 @@ Before starting, add the eCommerce sample data that you'll use in your spec, the Open *Vega-Lite* and change the time range. -. On the dashboard, click *Create panel*. - -. On the *New visualization* window, click *Custom visualization*. +. On the dashboard, click *All types*, then select *Custom visualization*. + A pre-populated line chart displays the total number of documents. -. Change the <> to *Last 7 days*. +. Make sure the <> is *Last 7 days*. [float] [[vega-tutorial-create-a-stacked-area-chart]] @@ -595,9 +593,9 @@ Add the {es} search query with the `data` block, then click *Update*: ``` [float] -===== Change the X- and Y-axes +===== Change the x- and y-axes -Display labels for the X- and Y-axes. +Display labels for the x- and y-axes. In the *Vega* spec, add the `scales` block, then click *Update*: diff --git a/package.json b/package.json index b8f3a52c4883a..3cdde5e52584a 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "dependencies": { "@elastic/apm-rum": "^5.6.1", "@elastic/apm-rum-react": "^1.2.5", - "@elastic/charts": "29.1.0", + "@elastic/charts": "29.2.0", "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath/npm_module", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.4", "@elastic/ems-client": "7.13.0", @@ -130,18 +130,21 @@ "@kbn/config": "link:bazel-bin/packages/kbn-config/npm_module", "@kbn/config-schema": "link:bazel-bin/packages/kbn-config-schema/npm_module", "@kbn/crypto": "link:bazel-bin/packages/kbn-crypto/npm_module", + "@kbn/mapbox-gl": "link:bazel-bin/packages/kbn-mapbox-gl/npm_module", "@kbn/i18n": "link:bazel-bin/packages/kbn-i18n/npm_module", "@kbn/interpreter": "link:packages/kbn-interpreter", "@kbn/io-ts-utils": "link:packages/kbn-io-ts-utils", "@kbn/legacy-logging": "link:bazel-bin/packages/kbn-legacy-logging/npm_module", "@kbn/logging": "link:bazel-bin/packages/kbn-logging/npm_module", "@kbn/monaco": "link:packages/kbn-monaco", - "@kbn/securitysolution-constants": "link:bazel-bin/packages/kbn-securitysolution-constants/npm_module", + "@kbn/securitysolution-list-constants": "link:bazel-bin/packages/kbn-securitysolution-list-constants/npm_module", "@kbn/securitysolution-es-utils": "link:bazel-bin/packages/kbn-securitysolution-es-utils/npm_module", "@kbn/securitysolution-io-ts-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-types/npm_module", "@kbn/securitysolution-io-ts-alerting-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-alerting-types/npm_module", "@kbn/securitysolution-io-ts-list-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-list-types/npm_module", "@kbn/securitysolution-io-ts-utils": "link:bazel-bin/packages/kbn-securitysolution-io-ts-utils/npm_module", + "@kbn/securitysolution-list-api": "link:bazel-bin/packages/kbn-securitysolution-list-api/npm_module", + "@kbn/securitysolution-list-hooks": "link:bazel-bin/packages/kbn-securitysolution-list-hooks/npm_module", "@kbn/securitysolution-list-utils": "link:bazel-bin/packages/kbn-securitysolution-list-utils/npm_module", "@kbn/securitysolution-utils": "link:bazel-bin/packages/kbn-securitysolution-utils/npm_module", "@kbn/server-http-tools": "link:bazel-bin/packages/kbn-server-http-tools/npm_module", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index e400567a28e9b..43528e0ae4162 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -24,13 +24,16 @@ filegroup( "//packages/kbn-i18n:build", "//packages/kbn-legacy-logging:build", "//packages/kbn-logging:build", + "//packages/kbn-mapbox-gl:build", "//packages/kbn-plugin-generator:build", - "//packages/kbn-securitysolution-constants:build", - "//packages/kbn-securitysolution-list-utils:build", + "//packages/kbn-securitysolution-list-constants:build", "//packages/kbn-securitysolution-io-ts-types:build", "//packages/kbn-securitysolution-io-ts-alerting-types:build", "//packages/kbn-securitysolution-io-ts-list-types:build", "//packages/kbn-securitysolution-io-ts-utils:build", + "//packages/kbn-securitysolution-list-api:build", + "//packages/kbn-securitysolution-list-hooks:build", + "//packages/kbn-securitysolution-list-utils:build", "//packages/kbn-securitysolution-utils:build", "//packages/kbn-securitysolution-es-utils:build", "//packages/kbn-server-http-tools:build", diff --git a/packages/elastic-datemath/BUILD.bazel b/packages/elastic-datemath/BUILD.bazel index f3eb4548088cb..7c5bb7d07c85a 100644 --- a/packages/elastic-datemath/BUILD.bazel +++ b/packages/elastic-datemath/BUILD.bazel @@ -55,7 +55,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-ace/BUILD.bazel b/packages/kbn-ace/BUILD.bazel index f52754d864777..78e986d6e0716 100644 --- a/packages/kbn-ace/BUILD.bazel +++ b/packages/kbn-ace/BUILD.bazel @@ -64,7 +64,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) @@ -82,4 +82,4 @@ filegroup( ":npm_module", ], visibility = ["//visibility:public"], -) \ No newline at end of file +) diff --git a/packages/kbn-analytics/BUILD.bazel b/packages/kbn-analytics/BUILD.bazel index a5506598baeac..9eeaf4dc5842e 100644 --- a/packages/kbn-analytics/BUILD.bazel +++ b/packages/kbn-analytics/BUILD.bazel @@ -56,10 +56,10 @@ ts_project( srcs = SRCS, deps = DEPS, declaration = True, - declaration_dir = "types", + declaration_dir = "target_types", declaration_map = True, incremental = True, - out_dir = "node", + out_dir = "target_node", source_map = True, root_dir = "src", tsconfig = ":tsconfig", @@ -72,38 +72,16 @@ ts_project( deps = DEPS, declaration = False, incremental = True, - out_dir = "web", + out_dir = "target_web", source_map = True, root_dir = "src", tsconfig = ":tsconfig_browser", ) -filegroup( - name = "tsc_types", - srcs = [":tsc"], - output_group = "types", -) - -filegroup( - name = "target_files", - srcs = [ - ":tsc", - ":tsc_browser", - ":tsc_types", - ], -) - -pkg_npm( - name = "target", - deps = [ - ":target_files", - ], -) - js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":target"] + DEPS, + deps = DEPS + [":tsc", ":tsc_browser"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-analytics/package.json b/packages/kbn-analytics/package.json index 726b62e1c61b8..177c0eb815760 100644 --- a/packages/kbn-analytics/package.json +++ b/packages/kbn-analytics/package.json @@ -3,9 +3,9 @@ "private": true, "version": "1.0.0", "description": "Kibana Analytics tool", - "main": "target/node/index.js", - "types": "target/types/index.d.ts", - "browser": "target/web/index.js", + "main": "target_node/index.js", + "types": "target_types/index.d.ts", + "browser": "target_web/index.js", "author": "Ahmad Bamieh ", "license": "SSPL-1.0 OR Elastic License 2.0" } \ No newline at end of file diff --git a/packages/kbn-analytics/tsconfig.browser.json b/packages/kbn-analytics/tsconfig.browser.json index 12f70b77008e7..8a65c551d3bc4 100644 --- a/packages/kbn-analytics/tsconfig.browser.json +++ b/packages/kbn-analytics/tsconfig.browser.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.browser.json", "compilerOptions": { "incremental": true, - "outDir": "./target/web", + "outDir": "./target_web", "stripInternal": true, "declaration": false, "isolatedModules": true, diff --git a/packages/kbn-analytics/tsconfig.json b/packages/kbn-analytics/tsconfig.json index 165a8b695db57..2eaa88cd3ce5f 100644 --- a/packages/kbn-analytics/tsconfig.json +++ b/packages/kbn-analytics/tsconfig.json @@ -2,8 +2,8 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "incremental": true, - "declarationDir": "./target/types", - "outDir": "./target/node", + "declarationDir": "./target_types", + "outDir": "./target_node", "stripInternal": true, "declaration": true, "declarationMap": true, diff --git a/packages/kbn-apm-config-loader/BUILD.bazel b/packages/kbn-apm-config-loader/BUILD.bazel index 58a86ccfcf018..09a121d71575c 100644 --- a/packages/kbn-apm-config-loader/BUILD.bazel +++ b/packages/kbn-apm-config-loader/BUILD.bazel @@ -66,7 +66,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-apm-utils/BUILD.bazel b/packages/kbn-apm-utils/BUILD.bazel index 335494bea45f0..328fd91a5d965 100644 --- a/packages/kbn-apm-utils/BUILD.bazel +++ b/packages/kbn-apm-utils/BUILD.bazel @@ -54,7 +54,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-babel-code-parser/BUILD.bazel b/packages/kbn-babel-code-parser/BUILD.bazel index e8869cb40555e..c1576ce59aa5c 100644 --- a/packages/kbn-babel-code-parser/BUILD.bazel +++ b/packages/kbn-babel-code-parser/BUILD.bazel @@ -53,7 +53,7 @@ babel( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":target"] + DEPS, + deps = DEPS + [":target"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-config-schema/BUILD.bazel b/packages/kbn-config-schema/BUILD.bazel index 0c6b3c10db4fd..1ae4fa5eeeb27 100644 --- a/packages/kbn-config-schema/BUILD.bazel +++ b/packages/kbn-config-schema/BUILD.bazel @@ -64,7 +64,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-config/BUILD.bazel b/packages/kbn-config/BUILD.bazel index a079d9c8f6413..bc21f827addd2 100644 --- a/packages/kbn-config/BUILD.bazel +++ b/packages/kbn-config/BUILD.bazel @@ -76,7 +76,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-config/src/config_service.mock.ts b/packages/kbn-config/src/config_service.mock.ts index 83fbf20b5c0b3..68adbba7c0ed7 100644 --- a/packages/kbn-config/src/config_service.mock.ts +++ b/packages/kbn-config/src/config_service.mock.ts @@ -26,11 +26,13 @@ const createConfigServiceMock = ({ addDeprecationProvider: jest.fn(), validate: jest.fn(), getHandledDeprecatedConfigs: jest.fn(), + getDeprecatedConfigPath$: jest.fn(), }; mocked.atPath.mockReturnValue(new BehaviorSubject(atPath)); mocked.atPathSync.mockReturnValue(atPath); mocked.getConfig$.mockReturnValue(new BehaviorSubject(new ObjectToConfigAdapter(getConfig$))); + mocked.getDeprecatedConfigPath$.mockReturnValue(new BehaviorSubject({ set: [], unset: [] })); mocked.getUsedPaths.mockResolvedValue([]); mocked.getUnusedPaths.mockResolvedValue([]); mocked.isEnabledAtPath.mockResolvedValue(true); diff --git a/packages/kbn-config/src/config_service.test.mocks.ts b/packages/kbn-config/src/config_service.test.mocks.ts index d8da2852b9251..39aa551ae85f9 100644 --- a/packages/kbn-config/src/config_service.test.mocks.ts +++ b/packages/kbn-config/src/config_service.test.mocks.ts @@ -11,10 +11,17 @@ import type { applyDeprecations } from './deprecation/apply_deprecations'; jest.mock('../../../package.json', () => mockPackage); +const changedPaths = { + set: ['foo'], + unset: ['bar.baz'], +}; + +export { changedPaths as mockedChangedPaths }; + export const mockApplyDeprecations = jest.fn< - Record, + ReturnType, Parameters ->((config, deprecations, createAddDeprecation) => config); +>((config, deprecations, createAddDeprecation) => ({ config, changedPaths })); jest.mock('./deprecation/apply_deprecations', () => ({ applyDeprecations: mockApplyDeprecations, diff --git a/packages/kbn-config/src/config_service.test.ts b/packages/kbn-config/src/config_service.test.ts index 64404341bc64d..c2d4f15b6d915 100644 --- a/packages/kbn-config/src/config_service.test.ts +++ b/packages/kbn-config/src/config_service.test.ts @@ -9,7 +9,7 @@ import { BehaviorSubject, Observable } from 'rxjs'; import { first, take } from 'rxjs/operators'; -import { mockApplyDeprecations } from './config_service.test.mocks'; +import { mockApplyDeprecations, mockedChangedPaths } from './config_service.test.mocks'; import { rawConfigServiceMock } from './raw/raw_config_service.mock'; import { schema } from '@kbn/config-schema'; @@ -420,7 +420,7 @@ test('logs deprecation warning during validation', async () => { const addDeprecation = createAddDeprecation!(''); addDeprecation({ message: 'some deprecation message' }); addDeprecation({ message: 'another deprecation message' }); - return config; + return { config, changedPaths: mockedChangedPaths }; }); loggerMock.clear(logger); @@ -446,12 +446,12 @@ test('does not log warnings for silent deprecations during validation', async () const addDeprecation = createAddDeprecation!(''); addDeprecation({ message: 'some deprecation message', silent: true }); addDeprecation({ message: 'another deprecation message' }); - return config; + return { config, changedPaths: mockedChangedPaths }; }) .mockImplementationOnce((config, deprecations, createAddDeprecation) => { const addDeprecation = createAddDeprecation!(''); addDeprecation({ message: 'I am silent', silent: true }); - return config; + return { config, changedPaths: mockedChangedPaths }; }); loggerMock.clear(logger); @@ -521,7 +521,7 @@ describe('getHandledDeprecatedConfigs', () => { const addDeprecation = createAddDeprecation!(deprecation.path); addDeprecation({ message: `some deprecation message`, documentationUrl: 'some-url' }); }); - return config; + return { config, changedPaths: mockedChangedPaths }; }); await configService.validate(); @@ -541,3 +541,18 @@ describe('getHandledDeprecatedConfigs', () => { `); }); }); + +describe('getDeprecatedConfigPath$', () => { + it('returns all config paths changes during deprecation', async () => { + const rawConfig$ = new BehaviorSubject>({ key: 'value' }); + const rawConfigProvider = rawConfigServiceMock.create({ rawConfig$ }); + + const configService = new ConfigService(rawConfigProvider, defaultEnv, logger); + await configService.setSchema('key', schema.string()); + await configService.validate(); + + const deprecatedConfigPath$ = configService.getDeprecatedConfigPath$(); + const deprecatedConfigPath = await deprecatedConfigPath$.pipe(first()).toPromise(); + expect(deprecatedConfigPath).toEqual(mockedChangedPaths); + }); +}); diff --git a/packages/kbn-config/src/config_service.ts b/packages/kbn-config/src/config_service.ts index 91927b4c7b5c9..a80680bd46dfc 100644 --- a/packages/kbn-config/src/config_service.ts +++ b/packages/kbn-config/src/config_service.ts @@ -22,6 +22,7 @@ import { ConfigDeprecationProvider, configDeprecationFactory, DeprecatedConfigDetails, + ChangedDeprecatedPaths, } from './deprecation'; import { LegacyObjectToConfigAdapter } from './legacy'; @@ -36,6 +37,10 @@ export class ConfigService { private validated = false; private readonly config$: Observable; private lastConfig?: Config; + private readonly deprecatedConfigPaths = new BehaviorSubject({ + set: [], + unset: [], + }); /** * Whenever a config if read at a path, we mark that path as 'handled'. We can @@ -57,7 +62,8 @@ export class ConfigService { this.config$ = combineLatest([this.rawConfigProvider.getConfig$(), this.deprecations]).pipe( map(([rawConfig, deprecations]) => { const migrated = applyDeprecations(rawConfig, deprecations); - return new LegacyObjectToConfigAdapter(migrated); + this.deprecatedConfigPaths.next(migrated.changedPaths); + return new LegacyObjectToConfigAdapter(migrated.config); }), tap((config) => { this.lastConfig = config; @@ -191,6 +197,10 @@ export class ConfigService { return config.getFlattenedPaths().filter((path) => isPathHandled(path, handledPaths)); } + public getDeprecatedConfigPath$() { + return this.deprecatedConfigPaths.asObservable(); + } + private async logDeprecation() { const rawConfig = await this.rawConfigProvider.getConfig$().pipe(take(1)).toPromise(); const deprecations = await this.deprecations.pipe(take(1)).toPromise(); diff --git a/packages/kbn-config/src/deprecation/apply_deprecations.test.ts b/packages/kbn-config/src/deprecation/apply_deprecations.test.ts index 47746967bbe5f..8ad1491c19c9b 100644 --- a/packages/kbn-config/src/deprecation/apply_deprecations.test.ts +++ b/packages/kbn-config/src/deprecation/apply_deprecations.test.ts @@ -82,7 +82,7 @@ describe('applyDeprecations', () => { it('returns the migrated config', () => { const initialConfig = { foo: 'bar', deprecated: 'deprecated', renamed: 'renamed' }; - const migrated = applyDeprecations(initialConfig, [ + const { config: migrated } = applyDeprecations(initialConfig, [ wrapHandler(deprecations.unused('deprecated')), wrapHandler(deprecations.rename('renamed', 'newname')), ]); @@ -93,7 +93,7 @@ describe('applyDeprecations', () => { it('does not alter the initial config', () => { const initialConfig = { foo: 'bar', deprecated: 'deprecated' }; - const migrated = applyDeprecations(initialConfig, [ + const { config: migrated } = applyDeprecations(initialConfig, [ wrapHandler(deprecations.unused('deprecated')), ]); @@ -110,7 +110,7 @@ describe('applyDeprecations', () => { return { unset: [{ path: 'unknown' }] }; }); - const migrated = applyDeprecations( + const { config: migrated } = applyDeprecations( initialConfig, [wrapHandler(handler, 'pathA')], createAddDeprecation @@ -128,7 +128,7 @@ describe('applyDeprecations', () => { return { rewrite: [{ path: 'foo' }] }; }); - const migrated = applyDeprecations( + const { config: migrated } = applyDeprecations( initialConfig, [wrapHandler(handler, 'pathA')], createAddDeprecation @@ -136,4 +136,25 @@ describe('applyDeprecations', () => { expect(migrated).toEqual(initialConfig); }); + + it('returns a list of changes config paths', () => { + const addDeprecation = jest.fn(); + const createAddDeprecation = jest.fn().mockReturnValue(addDeprecation); + const initialConfig = { foo: 'bar', deprecated: 'deprecated' }; + + const handler = jest.fn().mockImplementation((config) => { + return { set: [{ path: 'foo', value: 'bar' }], unset: [{ path: 'baz' }] }; + }); + + const { changedPaths } = applyDeprecations( + initialConfig, + [wrapHandler(handler, 'pathA')], + createAddDeprecation + ); + + expect(changedPaths).toEqual({ + set: ['foo'], + unset: ['baz'], + }); + }); }); diff --git a/packages/kbn-config/src/deprecation/apply_deprecations.ts b/packages/kbn-config/src/deprecation/apply_deprecations.ts index 092a5ced28371..d38ae98835831 100644 --- a/packages/kbn-config/src/deprecation/apply_deprecations.ts +++ b/packages/kbn-config/src/deprecation/apply_deprecations.ts @@ -8,7 +8,11 @@ import { cloneDeep, unset } from 'lodash'; import { set } from '@elastic/safer-lodash-set'; -import { ConfigDeprecationWithContext, AddConfigDeprecation } from './types'; +import type { + AddConfigDeprecation, + ChangedDeprecatedPaths, + ConfigDeprecationWithContext, +} from './types'; const noopAddDeprecationFactory: () => AddConfigDeprecation = () => () => undefined; /** @@ -22,22 +26,31 @@ export const applyDeprecations = ( config: Record, deprecations: ConfigDeprecationWithContext[], createAddDeprecation: (pluginId: string) => AddConfigDeprecation = noopAddDeprecationFactory -) => { +): { config: Record; changedPaths: ChangedDeprecatedPaths } => { const result = cloneDeep(config); + const changedPaths: ChangedDeprecatedPaths = { + set: [], + unset: [], + }; deprecations.forEach(({ deprecation, path }) => { const commands = deprecation(result, path, createAddDeprecation(path)); if (commands) { if (commands.set) { + changedPaths.set.push(...commands.set.map((c) => c.path)); commands.set.forEach(function ({ path: commandPath, value }) { set(result, commandPath, value); }); } if (commands.unset) { + changedPaths.unset.push(...commands.unset.map((c) => c.path)); commands.unset.forEach(function ({ path: commandPath }) { unset(result, commandPath); }); } } }); - return result; + return { + config: result, + changedPaths, + }; }; diff --git a/packages/kbn-config/src/deprecation/index.ts b/packages/kbn-config/src/deprecation/index.ts index 48576e6d830be..ce10bafd9c575 100644 --- a/packages/kbn-config/src/deprecation/index.ts +++ b/packages/kbn-config/src/deprecation/index.ts @@ -14,6 +14,7 @@ export type { AddConfigDeprecation, ConfigDeprecationProvider, DeprecatedConfigDetails, + ChangedDeprecatedPaths, } from './types'; export { configDeprecationFactory } from './deprecation_factory'; export { applyDeprecations } from './apply_deprecations'; diff --git a/packages/kbn-config/src/deprecation/types.ts b/packages/kbn-config/src/deprecation/types.ts index 6944f45c1e1d2..0522365ad76c1 100644 --- a/packages/kbn-config/src/deprecation/types.ts +++ b/packages/kbn-config/src/deprecation/types.ts @@ -55,6 +55,16 @@ export type ConfigDeprecation = ( addDeprecation: AddConfigDeprecation ) => void | ConfigDeprecationCommand; +/** + * List of config paths changed during deprecation. + * + * @public + */ +export interface ChangedDeprecatedPaths { + set: string[]; + unset: string[]; +} + /** * Outcome of deprecation operation. Allows mutating config values in a declarative way. * diff --git a/packages/kbn-config/src/index.ts b/packages/kbn-config/src/index.ts index cf875d3daa4a2..294caba4e7048 100644 --- a/packages/kbn-config/src/index.ts +++ b/packages/kbn-config/src/index.ts @@ -13,6 +13,7 @@ export type { ConfigDeprecationWithContext, ConfigDeprecation, ConfigDeprecationCommand, + ChangedDeprecatedPaths, } from './deprecation'; export { applyDeprecations, configDeprecationFactory } from './deprecation'; diff --git a/packages/kbn-crypto/BUILD.bazel b/packages/kbn-crypto/BUILD.bazel index 14e292c056db6..b1723e4120e79 100644 --- a/packages/kbn-crypto/BUILD.bazel +++ b/packages/kbn-crypto/BUILD.bazel @@ -67,7 +67,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) @@ -85,4 +85,4 @@ filegroup( ":npm_module", ], visibility = ["//visibility:public"], -) \ No newline at end of file +) diff --git a/packages/kbn-dev-utils/BUILD.bazel b/packages/kbn-dev-utils/BUILD.bazel index e3935040240dc..9109f766e0e9f 100644 --- a/packages/kbn-dev-utils/BUILD.bazel +++ b/packages/kbn-dev-utils/BUILD.bazel @@ -107,7 +107,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-docs-utils/BUILD.bazel b/packages/kbn-docs-utils/BUILD.bazel index e72d83851f5d2..cf609f126d7db 100644 --- a/packages/kbn-docs-utils/BUILD.bazel +++ b/packages/kbn-docs-utils/BUILD.bazel @@ -67,7 +67,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-es/BUILD.bazel b/packages/kbn-es/BUILD.bazel index bc8986e527fc5..6c845996ce5e5 100644 --- a/packages/kbn-es/BUILD.bazel +++ b/packages/kbn-es/BUILD.bazel @@ -69,7 +69,7 @@ babel( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":target"] + DEPS, + deps = DEPS + [":target"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-i18n/BUILD.bazel b/packages/kbn-i18n/BUILD.bazel index 02f5874a69a83..a48939d6e26fd 100644 --- a/packages/kbn-i18n/BUILD.bazel +++ b/packages/kbn-i18n/BUILD.bazel @@ -111,7 +111,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc", ":tsc_browser"] + DEPS, + deps = DEPS + [":tsc", ":tsc_browser"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-i18n/tsconfig.browser.json b/packages/kbn-i18n/tsconfig.browser.json index 707e3294bf1e7..d7b8495a0d21d 100644 --- a/packages/kbn-i18n/tsconfig.browser.json +++ b/packages/kbn-i18n/tsconfig.browser.json @@ -8,7 +8,10 @@ "isolatedModules": true, "sourceMap": true, "sourceRoot": "../../../../../packages/kbn-i18n/src", - "types": ["node"], + "types": [ + "jest", + "node" + ], }, "include": [ "src/**/*.ts", diff --git a/packages/kbn-legacy-logging/BUILD.bazel b/packages/kbn-legacy-logging/BUILD.bazel index 1cb7ae8d83fdf..21cb8c338f89f 100644 --- a/packages/kbn-legacy-logging/BUILD.bazel +++ b/packages/kbn-legacy-logging/BUILD.bazel @@ -71,7 +71,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-logging/BUILD.bazel b/packages/kbn-logging/BUILD.bazel index f42ca22ae5256..a5ef1581f41ad 100644 --- a/packages/kbn-logging/BUILD.bazel +++ b/packages/kbn-logging/BUILD.bazel @@ -61,7 +61,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-mapbox-gl/BUILD.bazel b/packages/kbn-mapbox-gl/BUILD.bazel new file mode 100644 index 0000000000000..7d7186068832e --- /dev/null +++ b/packages/kbn-mapbox-gl/BUILD.bazel @@ -0,0 +1,84 @@ + +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-mapbox-gl" +PKG_REQUIRE_NAME = "@kbn/mapbox-gl" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md" +] + +SRC_DEPS = [ + "@npm//@mapbox/mapbox-gl-rtl-text", + "@npm//file-loader", + "@npm//mapbox-gl", +] + +TYPES_DEPS = [ + "@npm//@types/mapbox-gl", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = DEPS + [":tsc"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-mapbox-gl/README.md b/packages/kbn-mapbox-gl/README.md new file mode 100644 index 0000000000000..c1fdea491feaa --- /dev/null +++ b/packages/kbn-mapbox-gl/README.md @@ -0,0 +1,3 @@ +# @kbn/mapbox-gl + +Default instantiation for mapbox-gl. \ No newline at end of file diff --git a/packages/kbn-mapbox-gl/package.json b/packages/kbn-mapbox-gl/package.json new file mode 100644 index 0000000000000..9de88dac54a5a --- /dev/null +++ b/packages/kbn-mapbox-gl/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/mapbox-gl", + "version": "1.0.0", + "private": true, + "license": "SSPL-1.0 OR Elastic License 2.0", + "main": "./target/index.js", + "types": "./target/index.d.ts" +} diff --git a/packages/kbn-mapbox-gl/src/index.ts b/packages/kbn-mapbox-gl/src/index.ts new file mode 100644 index 0000000000000..117b874a28ffb --- /dev/null +++ b/packages/kbn-mapbox-gl/src/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 './typings'; +import mapboxgl from 'mapbox-gl/dist/mapbox-gl-csp'; +// @ts-expect-error +import mbRtlPlugin from '!!file-loader!@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js'; +// @ts-expect-error +import mbWorkerUrl from '!!file-loader!mapbox-gl/dist/mapbox-gl-csp-worker'; +import 'mapbox-gl/dist/mapbox-gl.css'; + +mapboxgl.workerUrl = mbWorkerUrl; +mapboxgl.setRTLTextPlugin(mbRtlPlugin); + +export { mapboxgl }; diff --git a/packages/kbn-mapbox-gl/src/typings.ts b/packages/kbn-mapbox-gl/src/typings.ts new file mode 100644 index 0000000000000..0cc6908aca428 --- /dev/null +++ b/packages/kbn-mapbox-gl/src/typings.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +// Mapbox-gl doesn't declare this type. +declare module 'mapbox-gl/dist/mapbox-gl-csp'; diff --git a/packages/kbn-mapbox-gl/tsconfig.json b/packages/kbn-mapbox-gl/tsconfig.json new file mode 100644 index 0000000000000..cf1cca0f5a0fd --- /dev/null +++ b/packages/kbn-mapbox-gl/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "incremental": true, + "outDir": "./target", + "declaration": true, + "declarationMap": true, + "rootDir": "src", + "sourceMap": true, + "sourceRoot": "../../../../packages/kbn-mapbox-gl/src", + "types": [] + }, + "include": [ + "src/**/*", + ] +} diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 2639f6fd273f7..2e76c26dd7b38 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -9,7 +9,7 @@ pageLoadAssetSize: charts: 195358 cloud: 21076 console: 46091 - core: 414000 + core: 432925 crossClusterReplication: 65408 dashboard: 374194 dashboardEnhanced: 65646 @@ -46,7 +46,7 @@ pageLoadAssetSize: lens: 96624 licenseManagement: 41817 licensing: 29004 - lists: 200000 + lists: 22900 logstash: 53548 management: 46112 maps: 80000 @@ -77,7 +77,7 @@ pageLoadAssetSize: tileMap: 65337 timelion: 29920 transform: 41007 - triggersActionsUi: 186732 + triggersActionsUi: 100000 uiActions: 97717 uiActionsEnhanced: 313011 upgradeAssistant: 81241 diff --git a/packages/kbn-plugin-generator/BUILD.bazel b/packages/kbn-plugin-generator/BUILD.bazel index e22d41076db00..cd18f5e14a736 100644 --- a/packages/kbn-plugin-generator/BUILD.bazel +++ b/packages/kbn-plugin-generator/BUILD.bazel @@ -85,7 +85,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-constants/package.json b/packages/kbn-securitysolution-constants/package.json deleted file mode 100644 index bb60f79aa03e5..0000000000000 --- a/packages/kbn-securitysolution-constants/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "@kbn/securitysolution-constants", - "version": "1.0.0", - "description": "security solution constants to use across plugins such lists, security_solution, cases, etc...", - "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target/index.js", - "types": "./target/index.d.ts", - "private": true -} diff --git a/packages/kbn-securitysolution-es-utils/BUILD.bazel b/packages/kbn-securitysolution-es-utils/BUILD.bazel index 0cc27358c5da2..b69aa3df8ecb5 100644 --- a/packages/kbn-securitysolution-es-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-es-utils/BUILD.bazel @@ -67,7 +67,7 @@ js_library( package_name = PKG_REQUIRE_NAME, srcs = NPM_MODULE_EXTRA_FILES, visibility = ["//visibility:public"], - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], ) pkg_npm( diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel b/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel index ba7123d0c1f21..8e4a464783fc6 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel +++ b/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel @@ -73,7 +73,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.ts index 91a3951ef11fc..4013a3a0497db 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.ts +++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.ts @@ -7,15 +7,7 @@ */ /** - * This ID is used for _both_ the Saved Object ID and for the list_id - * for the single global space agnostic endpoint list - * TODO: Create a kbn-securitysolution-constants and add this to it. - * @deprecated Use the ENDPOINT_LIST_ID from the kbn-securitysolution-constants. - */ -export const ENDPOINT_LIST_ID = 'endpoint_list'; - -/** - * TODO: Create a kbn-securitysolution-constants and add this to it. - * @deprecated Use the DEFAULT_MAX_SIGNALS from the kbn-securitysolution-constants. + * TODO: Create a kbn-alerting-constants and add this to it. + * @deprecated Use a DEFAULT_MAX_SIGNALS from a kbn-alerting-constants package. */ export const DEFAULT_MAX_SIGNALS = 100; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts index 37ed4b2daa510..30b3c727d87a2 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts +++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts @@ -8,7 +8,7 @@ import { Either } from 'fp-ts/lib/Either'; import * as t from 'io-ts'; -import { parseScheduleDates } from '@kbn/securitysolution-io-ts-types'; +import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; const stringValidator = (input: unknown): input is string => typeof input === 'string'; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/index.ts index c6f29862206e6..2b8ba39fdf4f3 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/index.ts +++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/index.ts @@ -7,7 +7,6 @@ */ export * from './actions'; -export * from './constants'; export * from './default_actions_array'; export * from './default_export_file_name'; export * from './default_from_string'; diff --git a/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel b/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel index af8fb6838a5c1..91e4667c16b4e 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel +++ b/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel @@ -73,7 +73,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.mock.ts index c6f54b57d937b..b5e4751025439 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.mock.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/common/lists/index.mock.ts @@ -7,7 +7,7 @@ */ import { List, ListArray } from '.'; -import { ENDPOINT_LIST_ID } from '../../constants'; +import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; export const getListMock = (): List => ({ id: 'some_uuid', diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.ts deleted file mode 100644 index 2f520e79bf42c..0000000000000 --- a/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the 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. - */ - -/** - * This ID is used for _both_ the Saved Object ID and for the list_id - * for the single global space agnostic endpoint list. - * - * TODO: Create a kbn-securitysolution-constants and add this to it. - * @deprecated Use the ENDPOINT_LIST_ID from the kbn-securitysolution-constants. - */ -export const ENDPOINT_LIST_ID = 'endpoint_list'; - -/** - * Description of trusted apps agnostic list - * @deprecated Use the ENDPOINT_LIST_ID from the kbn-securitysolution-constants. - */ -export const ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION = 'Endpoint Security Trusted Apps List'; - -/** - * ID of trusted apps agnostic list - * @deprecated Use the ENDPOINT_LIST_ID from the kbn-securitysolution-constants. - */ -export const ENDPOINT_TRUSTED_APPS_LIST_ID = 'endpoint_trusted_apps'; - -/** - * Name of trusted apps agnostic list - * @deprecated Use the ENDPOINT_LIST_ID from the kbn-securitysolution-constants. - */ -export const ENDPOINT_TRUSTED_APPS_LIST_NAME = 'Endpoint Security Trusted Apps List'; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/index.ts index 33d97d868771e..3d1b267c01d3b 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/index.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/index.ts @@ -7,6 +7,6 @@ */ export * from './common'; -export * from './constants'; export * from './request'; export * from './response'; +export * from './typescript_types'; diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.mock.ts index 5928c420c88e3..c77fb35a40b60 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.mock.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_schema/index.mock.ts @@ -25,7 +25,7 @@ import { ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION, ENDPOINT_TRUSTED_APPS_LIST_ID, ENDPOINT_TRUSTED_APPS_LIST_NAME, -} from '../..'; +} from '@kbn/securitysolution-list-constants'; import { ExceptionListSchema } from '.'; diff --git a/x-pack/plugins/lists/public/exceptions/types.ts b/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts similarity index 74% rename from x-pack/plugins/lists/public/exceptions/types.ts rename to packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts index 8686c5d7ad250..f75f0dcebf4f6 100644 --- a/x-pack/plugins/lists/public/exceptions/types.ts +++ b/packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts @@ -1,51 +1,47 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { - CreateExceptionListItemSchema, - CreateExceptionListSchema, - ExceptionListItemSchema, - ExceptionListSchema, - ExceptionListType, - NamespaceType, - Page, - PerPage, - TotalOrUndefined, - UpdateExceptionListItemSchema, - UpdateExceptionListSchema, -} from '@kbn/securitysolution-io-ts-list-types'; - -import { HttpStart, NotificationsStart } from '../../../../../src/core/public'; +import { NamespaceType } from '../common/default_namespace'; +import { ExceptionListType } from '../common/exception_list'; +import { Page } from '../common/page'; +import { PerPage } from '../common/per_page'; +import { TotalOrUndefined } from '../common/total'; +import { CreateExceptionListItemSchema } from '../request/create_exception_list_item_schema'; +import { CreateExceptionListSchema } from '../request/create_exception_list_schema'; +import { UpdateExceptionListItemSchema } from '../request/update_exception_list_item_schema'; +import { UpdateExceptionListSchema } from '../request/update_exception_list_schema'; +import { ExceptionListItemSchema } from '../response/exception_list_item_schema'; +import { ExceptionListSchema } from '../response/exception_list_schema'; + +// TODO: Replace these with kbn packaged versions once we have those available to us +// These originally came from this location below before moving them to this hacked "any" types: +// import { HttpStart, NotificationsStart } from '../../../../../src/core/public'; +interface HttpStart { + fetch: (...args: any) => any; +} +type NotificationsStart = any; -export interface FilterExceptionsOptions { - filter: string; - tags: string[]; -} - -export interface Pagination { - page: Page; - perPage: PerPage; - total: TotalOrUndefined; +export interface ExceptionListFilter { + name?: string | null; + list_id?: string | null; + created_by?: string | null; + type?: string | null; + tags?: string | null; } -export type AddExceptionList = UpdateExceptionListSchema | CreateExceptionListSchema; - -export interface PersistHookProps { +export interface UseExceptionListsProps { + errorMessage: string; + filterOptions?: ExceptionListFilter; http: HttpStart; - onError: (arg: Error) => void; -} - -export interface ExceptionList extends ExceptionListSchema { - totalItems: number; -} - -export interface UseExceptionListItemsSuccess { - exceptions: ExceptionListItemSchema[]; - pagination: Pagination; + namespaceTypes: NamespaceType[]; + notifications: NotificationsStart; + pagination?: Pagination; + showTrustedApps: boolean; } export interface UseExceptionListProps { @@ -60,27 +56,9 @@ export interface UseExceptionListProps { onSuccess?: (arg: UseExceptionListItemsSuccess) => void; } -export interface ExceptionListIdentifiers { - id: string; - listId: string; - namespaceType: NamespaceType; - type: ExceptionListType; -} - -export interface ApiCallByListIdProps { - http: HttpStart; - listIds: string[]; - namespaceTypes: NamespaceType[]; - filterOptions: FilterExceptionsOptions[]; - pagination: Partial; - signal: AbortSignal; -} - -export interface ApiCallByIdProps { - http: HttpStart; - id: string; - namespaceType: NamespaceType; - signal: AbortSignal; +export interface FilterExceptionsOptions { + filter: string; + tags: string[]; } export interface ApiCallMemoProps { @@ -101,6 +79,24 @@ export interface ApiListExportProps { onSuccess: (blob: Blob) => void; } +export interface Pagination { + page: Page; + perPage: PerPage; + total: TotalOrUndefined; +} + +export interface UseExceptionListItemsSuccess { + exceptions: ExceptionListItemSchema[]; + pagination: Pagination; +} + +export interface ExceptionListIdentifiers { + id: string; + listId: string; + namespaceType: NamespaceType; + type: ExceptionListType; +} + export interface ApiCallFindListsItemsMemoProps { lists: ExceptionListIdentifiers[]; filterOptions: FilterExceptionsOptions[]; @@ -110,40 +106,29 @@ export interface ApiCallFindListsItemsMemoProps { onError: (arg: string[]) => void; onSuccess: (arg: UseExceptionListItemsSuccess) => void; } -export interface ApiCallFetchExceptionListsProps { + +export interface ExportExceptionListProps { http: HttpStart; - namespaceTypes: string; - pagination: Partial; - filters: string; + id: string; + listId: string; + namespaceType: NamespaceType; signal: AbortSignal; } -export interface UseExceptionListsSuccess { - exceptions: ExceptionListSchema[]; - pagination: Pagination; -} - -export interface ExceptionListFilter { - name?: string | null; - list_id?: string | null; - created_by?: string | null; - type?: string | null; - tags?: string | null; +export interface AddEndpointExceptionListProps { + http: HttpStart; + signal: AbortSignal; } -export interface UseExceptionListsProps { - errorMessage: string; - filterOptions?: ExceptionListFilter; +export interface UpdateExceptionListItemProps { http: HttpStart; - namespaceTypes: NamespaceType[]; - notifications: NotificationsStart; - pagination?: Pagination; - showTrustedApps: boolean; + listItem: UpdateExceptionListItemSchema; + signal: AbortSignal; } -export interface AddExceptionListProps { +export interface UpdateExceptionListProps { http: HttpStart; - list: CreateExceptionListSchema; + list: UpdateExceptionListSchema; signal: AbortSignal; } @@ -153,27 +138,48 @@ export interface AddExceptionListItemProps { signal: AbortSignal; } -export interface UpdateExceptionListProps { +export interface AddExceptionListProps { http: HttpStart; - list: UpdateExceptionListSchema; + list: CreateExceptionListSchema; signal: AbortSignal; } -export interface UpdateExceptionListItemProps { - http: HttpStart; - listItem: UpdateExceptionListItemSchema; - signal: AbortSignal; +export interface UseExceptionListsSuccess { + exceptions: ExceptionListSchema[]; + pagination: Pagination; } -export interface AddEndpointExceptionListProps { +export interface ApiCallFetchExceptionListsProps { http: HttpStart; + namespaceTypes: string; + pagination: Partial; + filters: string; signal: AbortSignal; } -export interface ExportExceptionListProps { +export interface ApiCallByIdProps { http: HttpStart; id: string; - listId: string; namespaceType: NamespaceType; signal: AbortSignal; } + +export interface ApiCallByListIdProps { + http: HttpStart; + listIds: string[]; + namespaceTypes: NamespaceType[]; + filterOptions: FilterExceptionsOptions[]; + pagination: Partial; + signal: AbortSignal; +} + +export type AddExceptionList = UpdateExceptionListSchema | CreateExceptionListSchema; + +export interface PersistHookProps { + http: HttpStart; + onError: (arg: Error) => void; +} + +export interface ExceptionList extends ExceptionListSchema { + totalItems: number; +} diff --git a/packages/kbn-securitysolution-io-ts-types/BUILD.bazel b/packages/kbn-securitysolution-io-ts-types/BUILD.bazel index 0a21f5ed94f01..d30eb913b1a57 100644 --- a/packages/kbn-securitysolution-io-ts-types/BUILD.bazel +++ b/packages/kbn-securitysolution-io-ts-types/BUILD.bazel @@ -72,7 +72,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-io-ts-types/src/index.ts b/packages/kbn-securitysolution-io-ts-types/src/index.ts index fc0f017016e9f..2847894d63690 100644 --- a/packages/kbn-securitysolution-io-ts-types/src/index.ts +++ b/packages/kbn-securitysolution-io-ts-types/src/index.ts @@ -22,7 +22,6 @@ export * from './non_empty_string'; export * from './non_empty_string_array'; export * from './operator'; export * from './only_false_allowed'; -export * from './parse_schedule_dates'; export * from './positive_integer'; export * from './positive_integer_greater_than_zero'; export * from './string_to_positive_number'; diff --git a/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel b/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel index 66c5674067ecd..b9326a5020717 100644 --- a/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel @@ -71,7 +71,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-io-ts-utils/src/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/index.ts index c21096e497134..8082574296e3f 100644 --- a/packages/kbn-securitysolution-io-ts-utils/src/index.ts +++ b/packages/kbn-securitysolution-io-ts-utils/src/index.ts @@ -7,6 +7,7 @@ */ export * from './format_errors'; +export * from './parse_schedule_dates'; export * from './exact_check'; export * from './format_errors'; export * from './test_utils'; diff --git a/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.test.ts new file mode 100644 index 0000000000000..8919f63aad51e --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.test.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 and the 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 moment from 'moment'; +import { parseScheduleDates } from '.'; + +describe('parseScheduleDates', () => { + test('it returns a moment when given an ISO string', () => { + const result = parseScheduleDates('2020-01-01T00:00:00.000Z'); + expect(result).not.toBeNull(); + expect(result).toEqual(moment('2020-01-01T00:00:00.000Z')); + }); + + test('it returns a moment when given `now`', () => { + const result = parseScheduleDates('now'); + + expect(result).not.toBeNull(); + expect(moment.isMoment(result)).toBeTruthy(); + }); + + test('it returns a moment when given `now-x`', () => { + const result = parseScheduleDates('now-6m'); + + expect(result).not.toBeNull(); + expect(moment.isMoment(result)).toBeTruthy(); + }); + + test('it returns null when given a string that is not an ISO string, `now` or `now-x`', () => { + const result = parseScheduleDates('invalid'); + + expect(result).toBeNull(); + }); +}); diff --git a/packages/kbn-securitysolution-io-ts-types/src/parse_schedule_dates/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts similarity index 100% rename from packages/kbn-securitysolution-io-ts-types/src/parse_schedule_dates/index.ts rename to packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts diff --git a/packages/kbn-securitysolution-list-api/BUILD.bazel b/packages/kbn-securitysolution-list-api/BUILD.bazel new file mode 100644 index 0000000000000..9055cf0804e49 --- /dev/null +++ b/packages/kbn-securitysolution-list-api/BUILD.bazel @@ -0,0 +1,89 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-securitysolution-list-api" + +PKG_REQUIRE_NAME = "@kbn/securitysolution-list-api" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/*.mock.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md", +] + +SRC_DEPS = [ + "//packages/kbn-securitysolution-io-ts-utils", + "//packages/kbn-securitysolution-list-constants", + "//packages/kbn-securitysolution-io-ts-list-types", + "@npm//fp-ts", + "@npm//io-ts", + "@npm//tslib", +] + +TYPES_DEPS = [ + "@npm//@types/jest", + "@npm//@types/node", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + srcs = SRCS, + args = ["--pretty"], + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + root_dir = "src", + source_map = True, + tsconfig = ":tsconfig", + deps = DEPS, +) + +js_library( + name = PKG_BASE_NAME, + package_name = PKG_REQUIRE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + visibility = ["//visibility:public"], + deps = DEPS + [":tsc"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ], +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-securitysolution-list-api/README.md b/packages/kbn-securitysolution-list-api/README.md new file mode 100644 index 0000000000000..478406c1fd1df --- /dev/null +++ b/packages/kbn-securitysolution-list-api/README.md @@ -0,0 +1,4 @@ +# kbn-securitysolution-list-api + +This is where the REST API for the list plugin lives + diff --git a/packages/kbn-securitysolution-constants/jest.config.js b/packages/kbn-securitysolution-list-api/jest.config.js similarity index 86% rename from packages/kbn-securitysolution-constants/jest.config.js rename to packages/kbn-securitysolution-list-api/jest.config.js index f0bb13e39417c..869f74bdbb688 100644 --- a/packages/kbn-securitysolution-constants/jest.config.js +++ b/packages/kbn-securitysolution-list-api/jest.config.js @@ -9,5 +9,5 @@ module.exports = { preset: '@kbn/test', rootDir: '../..', - roots: ['/packages/kbn-securitysolution-constants'], + roots: ['/packages/kbn-securitysolution-list-api'], }; diff --git a/packages/kbn-securitysolution-list-api/package.json b/packages/kbn-securitysolution-list-api/package.json new file mode 100644 index 0000000000000..11108c61bfde1 --- /dev/null +++ b/packages/kbn-securitysolution-list-api/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/securitysolution-list-api", + "version": "1.0.0", + "description": "security solution list REST API", + "license": "SSPL-1.0 OR Elastic License 2.0", + "main": "./target/index.js", + "types": "./target/index.d.ts", + "private": true +} diff --git a/packages/kbn-securitysolution-list-api/src/api/index.test.ts b/packages/kbn-securitysolution-list-api/src/api/index.test.ts new file mode 100644 index 0000000000000..02cc02ced5ac4 --- /dev/null +++ b/packages/kbn-securitysolution-list-api/src/api/index.test.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 and the 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. + */ +describe('Exceptions Lists API', () => { + test('we should port these tests', () => { + // See the file outside of this at: x-pack/plugins/lists/public/exceptions/api.test.ts + // for the tests. We cannot port the tests over until we move the mocks into their own package + // and possibly core mocks end up within packages. + expect(true).toBe(true); + }); +}); diff --git a/x-pack/plugins/lists/public/exceptions/api.ts b/packages/kbn-securitysolution-list-api/src/api/index.ts similarity index 98% rename from x-pack/plugins/lists/public/exceptions/api.ts rename to packages/kbn-securitysolution-list-api/src/api/index.ts index 19c19c7e2c1ed..d70417a29971f 100644 --- a/x-pack/plugins/lists/public/exceptions/api.ts +++ b/packages/kbn-securitysolution-list-api/src/api/index.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { chain, fromEither, tryCatch } from 'fp-ts/lib/TaskEither'; @@ -19,18 +20,6 @@ import { exceptionListSchema, foundExceptionListItemSchema, foundExceptionListSchema, -} from '@kbn/securitysolution-io-ts-list-types'; - -import { toError, toPromise } from '../common/fp_utils'; -import { - ENDPOINT_LIST_URL, - EXCEPTION_LIST_ITEM_URL, - EXCEPTION_LIST_NAMESPACE, - EXCEPTION_LIST_NAMESPACE_AGNOSTIC, - EXCEPTION_LIST_URL, -} from '../../common/constants'; - -import { AddEndpointExceptionListProps, AddExceptionListItemProps, AddExceptionListProps, @@ -40,7 +29,16 @@ import { ExportExceptionListProps, UpdateExceptionListItemProps, UpdateExceptionListProps, -} from './types'; +} from '@kbn/securitysolution-io-ts-list-types'; + +import { + ENDPOINT_LIST_URL, + EXCEPTION_LIST_ITEM_URL, + EXCEPTION_LIST_NAMESPACE, + EXCEPTION_LIST_NAMESPACE_AGNOSTIC, + EXCEPTION_LIST_URL, +} from '@kbn/securitysolution-list-constants'; +import { toError, toPromise } from '../fp_utils'; /** * Add new ExceptionList diff --git a/x-pack/plugins/lists/public/common/fp_utils.test.ts b/packages/kbn-securitysolution-list-api/src/fp_utils/index.test.ts similarity index 75% rename from x-pack/plugins/lists/public/common/fp_utils.test.ts rename to packages/kbn-securitysolution-list-api/src/fp_utils/index.test.ts index a79616ddb80d0..dbd9d53856c8a 100644 --- a/x-pack/plugins/lists/public/common/fp_utils.test.ts +++ b/packages/kbn-securitysolution-list-api/src/fp_utils/index.test.ts @@ -1,13 +1,14 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { tryCatch } from 'fp-ts/lib/TaskEither'; -import { toPromise } from './fp_utils'; +import { toPromise } from '.'; describe('toPromise', () => { it('rejects with left if TaskEither is left', async () => { diff --git a/x-pack/plugins/security_solution/common/fp_utils.ts b/packages/kbn-securitysolution-list-api/src/fp_utils/index.ts similarity index 62% rename from x-pack/plugins/security_solution/common/fp_utils.ts rename to packages/kbn-securitysolution-list-api/src/fp_utils/index.ts index f6c0425073f86..f95fbf3c41912 100644 --- a/x-pack/plugins/security_solution/common/fp_utils.ts +++ b/packages/kbn-securitysolution-list-api/src/fp_utils/index.ts @@ -1,14 +1,17 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { pipe } from 'fp-ts/lib/pipeable'; import { TaskEither } from 'fp-ts/lib/TaskEither'; import { fold } from 'fp-ts/lib/Either'; +// TODO: This is copied in a few other spots and probably should live within its own kbn package +// rather than living here. A package such as kbn-security-solution-fp-utils export const toPromise = async (taskEither: TaskEither): Promise => pipe( await taskEither(), diff --git a/packages/kbn-securitysolution-list-api/src/index.ts b/packages/kbn-securitysolution-list-api/src/index.ts new file mode 100644 index 0000000000000..6ea8a30367c75 --- /dev/null +++ b/packages/kbn-securitysolution-list-api/src/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +export * from './api'; +export * from './fp_utils'; +export * from './list_api'; diff --git a/packages/kbn-securitysolution-list-api/src/list_api/index.test.ts b/packages/kbn-securitysolution-list-api/src/list_api/index.test.ts new file mode 100644 index 0000000000000..be5f307fee83f --- /dev/null +++ b/packages/kbn-securitysolution-list-api/src/list_api/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('Value Lists API', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/lists/api.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/x-pack/plugins/lists/public/lists/api.ts b/packages/kbn-securitysolution-list-api/src/list_api/index.ts similarity index 91% rename from x-pack/plugins/lists/public/lists/api.ts rename to packages/kbn-securitysolution-list-api/src/list_api/index.ts index ec17c079b8fff..b9d5417f761c0 100644 --- a/x-pack/plugins/lists/public/lists/api.ts +++ b/packages/kbn-securitysolution-list-api/src/list_api/index.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { chain, fromEither, map, tryCatch } from 'fp-ts/lib/TaskEither'; @@ -29,9 +30,13 @@ import { listItemIndexExistSchema, listSchema, } from '@kbn/securitysolution-io-ts-list-types'; - -import { LIST_INDEX, LIST_ITEM_URL, LIST_PRIVILEGES_URL, LIST_URL } from '../../common/constants'; -import { toError, toPromise } from '../common/fp_utils'; +import { + LIST_INDEX, + LIST_ITEM_URL, + LIST_PRIVILEGES_URL, + LIST_URL, +} from '@kbn/securitysolution-list-constants'; +import { toError, toPromise } from '../fp_utils'; import { ApiParams, @@ -69,9 +74,9 @@ const findListsWithValidation = async ({ }: FindListsParams): Promise => pipe( { - cursor: cursor?.toString(), - page: pageIndex?.toString(), - per_page: pageSize?.toString(), + cursor: cursor != null ? cursor.toString() : undefined, + page: pageIndex != null ? pageIndex.toString() : undefined, + per_page: pageSize != null ? pageSize.toString() : undefined, }, (payload) => fromEither(validateEither(findListSchema, payload)), chain((payload) => tryCatch(() => findLists({ http, signal, ...payload }), toError)), diff --git a/packages/kbn-securitysolution-list-api/src/list_api/types.ts b/packages/kbn-securitysolution-list-api/src/list_api/types.ts new file mode 100644 index 0000000000000..2914f2ee21760 --- /dev/null +++ b/packages/kbn-securitysolution-list-api/src/list_api/types.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 and the 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 { Type } from '@kbn/securitysolution-io-ts-list-types'; + +// TODO: Replace these with kbn packaged versions once we have those available to us +// These originally came from this location below before moving them to this hacked "any" types: +// import { HttpStart, NotificationsStart } from '../../../../../src/core/public'; +interface HttpStart { + fetch: (...args: any) => any; +} + +export interface ApiParams { + http: HttpStart; + signal: AbortSignal; +} +export type ApiPayload = Omit; + +export interface FindListsParams extends ApiParams { + cursor?: string | undefined; + pageSize: number | undefined; + pageIndex: number | undefined; +} + +export interface ImportListParams extends ApiParams { + file: File; + listId: string | undefined; + type: Type | undefined; +} + +export interface DeleteListParams extends ApiParams { + deleteReferences?: boolean; + id: string; + ignoreReferences?: boolean; +} + +export interface ExportListParams extends ApiParams { + listId: string; +} diff --git a/packages/kbn-securitysolution-constants/tsconfig.json b/packages/kbn-securitysolution-list-api/tsconfig.json similarity index 79% rename from packages/kbn-securitysolution-constants/tsconfig.json rename to packages/kbn-securitysolution-list-api/tsconfig.json index cf06f4ced4b9f..4fad35e754f29 100644 --- a/packages/kbn-securitysolution-constants/tsconfig.json +++ b/packages/kbn-securitysolution-list-api/tsconfig.json @@ -7,7 +7,7 @@ "outDir": "target", "rootDir": "src", "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-constants/src", + "sourceRoot": "../../../../packages/kbn-securitysolution-list-api/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-constants/BUILD.bazel b/packages/kbn-securitysolution-list-constants/BUILD.bazel similarity index 89% rename from packages/kbn-securitysolution-constants/BUILD.bazel rename to packages/kbn-securitysolution-list-constants/BUILD.bazel index 8b2d8c9103f3e..8d6779bfa122e 100644 --- a/packages/kbn-securitysolution-constants/BUILD.bazel +++ b/packages/kbn-securitysolution-list-constants/BUILD.bazel @@ -1,9 +1,9 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") -PKG_BASE_NAME = "kbn-securitysolution-constants" +PKG_BASE_NAME = "kbn-securitysolution-list-constants" -PKG_REQUIRE_NAME = "@kbn/securitysolution-constants" +PKG_REQUIRE_NAME = "@kbn/securitysolution-list-constants" SOURCE_FILES = glob( [ @@ -65,7 +65,7 @@ js_library( package_name = PKG_REQUIRE_NAME, srcs = NPM_MODULE_EXTRA_FILES, visibility = ["//visibility:public"], - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], ) pkg_npm( diff --git a/packages/kbn-securitysolution-list-constants/README.md b/packages/kbn-securitysolution-list-constants/README.md new file mode 100644 index 0000000000000..c6f10d3bd009b --- /dev/null +++ b/packages/kbn-securitysolution-list-constants/README.md @@ -0,0 +1,6 @@ +# kbn-securitysolution-list-constants + +This is where shared constants for security solution lists should go that are going to be shared among plugins. +This was originally created to remove the dependencies between security_solution and other projects. + + diff --git a/packages/kbn-securitysolution-list-constants/jest.config.js b/packages/kbn-securitysolution-list-constants/jest.config.js new file mode 100644 index 0000000000000..21dffdfcf5a68 --- /dev/null +++ b/packages/kbn-securitysolution-list-constants/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-securitysolution-list-constants'], +}; diff --git a/packages/kbn-securitysolution-list-constants/package.json b/packages/kbn-securitysolution-list-constants/package.json new file mode 100644 index 0000000000000..b9d65734aff56 --- /dev/null +++ b/packages/kbn-securitysolution-list-constants/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/securitysolution-list-constants", + "version": "1.0.0", + "description": "security solution list constants to use across plugins such lists, security_solution, cases, etc...", + "license": "SSPL-1.0 OR Elastic License 2.0", + "main": "./target/index.js", + "types": "./target/index.d.ts", + "private": true +} diff --git a/packages/kbn-securitysolution-constants/src/index.ts b/packages/kbn-securitysolution-list-constants/src/index.ts similarity index 96% rename from packages/kbn-securitysolution-constants/src/index.ts rename to packages/kbn-securitysolution-list-constants/src/index.ts index 06b741d761367..dae414aad0deb 100644 --- a/packages/kbn-securitysolution-constants/src/index.ts +++ b/packages/kbn-securitysolution-list-constants/src/index.ts @@ -70,6 +70,3 @@ export const ENDPOINT_EVENT_FILTERS_LIST_NAME = 'Endpoint Security Event Filters /** Description of event filters agnostic list */ export const ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION = 'Endpoint Security Event Filters List'; - -/** The default max signals without any additional configuration */ -export const DEFAULT_MAX_SIGNALS = 100; diff --git a/packages/kbn-securitysolution-list-constants/tsconfig.json b/packages/kbn-securitysolution-list-constants/tsconfig.json new file mode 100644 index 0000000000000..84edcdd1d5794 --- /dev/null +++ b/packages/kbn-securitysolution-list-constants/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "incremental": true, + "outDir": "target", + "rootDir": "src", + "sourceMap": true, + "sourceRoot": "../../../../packages/kbn-securitysolution-list-constants/src", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-securitysolution-list-hooks/BUILD.bazel b/packages/kbn-securitysolution-list-hooks/BUILD.bazel new file mode 100644 index 0000000000000..1078d9bf3d329 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/BUILD.bazel @@ -0,0 +1,95 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-securitysolution-list-hooks" + +PKG_REQUIRE_NAME = "@kbn/securitysolution-list-hooks" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/*.mock.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md", +] + +SRC_DEPS = [ + "//packages/kbn-securitysolution-io-ts-list-types", + "//packages/kbn-securitysolution-list-api", + "//packages/kbn-securitysolution-list-constants", + "//packages/kbn-securitysolution-list-utils", + "//packages/kbn-securitysolution-utils", + "@npm//lodash", + "@npm//tslib", + "@npm//react", + "@npm//react-intl", +] + +TYPES_DEPS = [ + "@npm//@types/jest", + "@npm//@types/lodash", + "@npm//@types/node", + "@npm//@types/react", + "@npm//@types/react-intl", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + srcs = SRCS, + args = ["--pretty"], + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + root_dir = "src", + source_map = True, + tsconfig = ":tsconfig", + deps = DEPS, +) + +js_library( + name = PKG_BASE_NAME, + package_name = PKG_REQUIRE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + visibility = ["//visibility:public"], + deps = DEPS + [":tsc"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ], +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-securitysolution-constants/README.md b/packages/kbn-securitysolution-list-hooks/README.md similarity index 86% rename from packages/kbn-securitysolution-constants/README.md rename to packages/kbn-securitysolution-list-hooks/README.md index dd1ab8da6a2a8..c192bd9a4460d 100644 --- a/packages/kbn-securitysolution-constants/README.md +++ b/packages/kbn-securitysolution-list-hooks/README.md @@ -1,4 +1,4 @@ -# kbn-securitysolution-constants +# kbn-securitysolution-list-hooks This is where shared constants for security solution should go that are going to be shared among plugins. This was originally created to remove the dependencies between security_solution and other projects such as lists. diff --git a/packages/kbn-securitysolution-list-hooks/jest.config.js b/packages/kbn-securitysolution-list-hooks/jest.config.js new file mode 100644 index 0000000000000..cbb4c1aa9d437 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-securitysolution-list-hooks'], +}; diff --git a/packages/kbn-securitysolution-list-hooks/package.json b/packages/kbn-securitysolution-list-hooks/package.json new file mode 100644 index 0000000000000..2eb456da97b72 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/securitysolution-list-hooks", + "version": "1.0.0", + "description": "Security solution list ReactJS hooks", + "license": "SSPL-1.0 OR Elastic License 2.0", + "main": "./target/index.js", + "types": "./target/index.d.ts", + "private": true +} diff --git a/packages/kbn-securitysolution-list-hooks/src/index.ts b/packages/kbn-securitysolution-list-hooks/src/index.ts new file mode 100644 index 0000000000000..46d6a20deb0ac --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ +export * from './transforms'; +export * from './use_api'; +export * from './use_async'; +export * from './use_create_list_index'; +export * from './use_cursor'; +export * from './use_delete_list'; +export * from './use_exception_list_items'; +export * from './use_exception_lists'; +export * from './use_export_list'; +export * from './use_find_lists'; +export * from './use_import_list'; +export * from './use_is_mounted'; +export * from './use_persist_exception_item'; +export * from './use_persist_exception_list'; +export * from './use_read_list_index'; +export * from './use_read_list_privileges'; +export * from './with_optional_signal'; diff --git a/packages/kbn-securitysolution-list-hooks/src/transforms/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/transforms/index.test.ts new file mode 100644 index 0000000000000..9b6bbf5908c3f --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/transforms/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('Exceptions transforms', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/exceptions/transforms.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/x-pack/plugins/lists/public/exceptions/transforms.ts b/packages/kbn-securitysolution-list-hooks/src/transforms/index.ts similarity index 96% rename from x-pack/plugins/lists/public/exceptions/transforms.ts rename to packages/kbn-securitysolution-list-hooks/src/transforms/index.ts index 49cf012a1c16b..75652b9ab5f6f 100644 --- a/x-pack/plugins/lists/public/exceptions/transforms.ts +++ b/packages/kbn-securitysolution-list-hooks/src/transforms/index.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { flow } from 'fp-ts/lib/function'; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_api/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_api/index.test.ts new file mode 100644 index 0000000000000..7ccf2146594be --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_api/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('useApi', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_api.ts b/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts similarity index 89% rename from x-pack/plugins/lists/public/exceptions/hooks/use_api.ts rename to packages/kbn-securitysolution-list-hooks/src/use_api/index.ts index 6e04827dad60b..3b980f84d82a8 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_api.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_api/index.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { useMemo } from 'react'; @@ -11,12 +12,20 @@ import type { ExceptionListItemSchema, ExceptionListSchema, UpdateExceptionListItemSchema, + ApiCallFindListsItemsMemoProps, + ApiCallMemoProps, + ApiListExportProps, } from '@kbn/securitysolution-io-ts-list-types'; +import * as Api from '@kbn/securitysolution-list-api'; -import * as Api from '../api'; -import { HttpStart } from '../../../../../../src/core/public'; -import { ApiCallFindListsItemsMemoProps, ApiCallMemoProps, ApiListExportProps } from '../types'; -import { getIdsAndNamespaces } from '../utils'; +// TODO: Replace these with kbn packaged versions once we have those available to us +// These originally came from this location below before moving them to this hacked "any" types: +// import { HttpStart, NotificationsStart } from '../../../../../src/core/public'; +interface HttpStart { + fetch: (...args: any) => any; +} + +import { getIdsAndNamespaces } from '@kbn/securitysolution-list-utils'; import { transformInput, transformNewItemOutput, transformOutput } from '../transforms'; export interface ExceptionsApi { @@ -206,7 +215,7 @@ export const useApi = (http: HttpStart): ExceptionsApi => { exceptions: [], pagination: { page: 0, - perPage: pagination.perPage ?? 0, + perPage: pagination.perPage != null ? pagination.perPage : 0, total: 0, }, }); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_api/types.ts b/packages/kbn-securitysolution-list-hooks/src/use_api/types.ts new file mode 100644 index 0000000000000..5c2d5b68ae2e0 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_api/types.ts @@ -0,0 +1,7 @@ +/* + * Copyright 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. + */ diff --git a/x-pack/plugins/lists/public/common/hooks/use_async.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_async/index.test.ts similarity index 93% rename from x-pack/plugins/lists/public/common/hooks/use_async.test.ts rename to packages/kbn-securitysolution-list-hooks/src/use_async/index.test.ts index abf1ae7a81893..886a3dd27befc 100644 --- a/x-pack/plugins/lists/public/common/hooks/use_async.test.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_async/index.test.ts @@ -1,13 +1,14 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { act, renderHook } from '@testing-library/react-hooks'; -import { useAsync } from './use_async'; +import { useAsync } from '.'; interface TestArgs { n: number; diff --git a/x-pack/plugins/lists/public/common/hooks/use_async.ts b/packages/kbn-securitysolution-list-hooks/src/use_async/index.ts similarity index 79% rename from x-pack/plugins/lists/public/common/hooks/use_async.ts rename to packages/kbn-securitysolution-list-hooks/src/use_async/index.ts index a73a21b8dc0d1..8a9048acbe369 100644 --- a/x-pack/plugins/lists/public/common/hooks/use_async.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_async/index.ts @@ -1,13 +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. + * 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, useState } from 'react'; -import { useIsMounted } from './use_is_mounted'; +import { useIsMounted } from '../use_is_mounted'; + +// TODO: This is probably better off in another package such as kbn-securitysolution-hook-utils export interface Async { loading: boolean; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.test.ts new file mode 100644 index 0000000000000..e0285d39f2fa9 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('useCreateListIndex', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/lists/hooks/use_create_list_index.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.ts new file mode 100644 index 0000000000000..1682bcb5ebf5c --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_create_list_index/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { createListIndex } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '../with_optional_signal'; +import { useAsync } from '../use_async'; + +const createListIndexWithOptionalSignal = withOptionalSignal(createListIndex); + +export const useCreateListIndex = () => useAsync(createListIndexWithOptionalSignal); diff --git a/x-pack/plugins/lists/public/common/hooks/use_cursor.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.test.ts similarity index 93% rename from x-pack/plugins/lists/public/common/hooks/use_cursor.test.ts rename to packages/kbn-securitysolution-list-hooks/src/use_cursor/index.test.ts index 07d084939e4f1..24096c8d2dc41 100644 --- a/x-pack/plugins/lists/public/common/hooks/use_cursor.test.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.test.ts @@ -1,13 +1,14 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { act, renderHook } from '@testing-library/react-hooks'; -import { UseCursorProps, useCursor } from './use_cursor'; +import { UseCursorProps, useCursor } from '.'; describe('useCursor', () => { it('returns undefined cursor if no values have been set', () => { diff --git a/x-pack/plugins/lists/public/common/hooks/use_cursor.ts b/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.ts similarity index 85% rename from x-pack/plugins/lists/public/common/hooks/use_cursor.ts rename to packages/kbn-securitysolution-list-hooks/src/use_cursor/index.ts index 1bc85c63363c8..f4a17c69a01dc 100644 --- a/x-pack/plugins/lists/public/common/hooks/use_cursor.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_cursor/index.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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, useState } from 'react'; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.test.ts new file mode 100644 index 0000000000000..f1a6f9e8348ba --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('useDeleteList', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/lists/hooks/use_delete_list.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.ts new file mode 100644 index 0000000000000..3be7056d5cf03 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { deleteList } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '../with_optional_signal'; +import { useAsync } from '../use_async'; + +const deleteListWithOptionalSignal = withOptionalSignal(deleteList); + +export const useDeleteList = () => useAsync(deleteListWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.test.ts new file mode 100644 index 0000000000000..4ca0a66e4f602 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('useExceptionListItems', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/exceptions/hooks/use_exception_list_items.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list_items.ts b/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.ts similarity index 91% rename from x-pack/plugins/lists/public/exceptions/hooks/use_exception_list_items.ts rename to packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.ts index 52fbccc376012..4962ecee58016 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list_items.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_exception_list_items/index.ts @@ -1,16 +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. + * 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 { useEffect, useRef, useState } from 'react'; -import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import type { + ExceptionListItemSchema, + Pagination, + UseExceptionListProps, + FilterExceptionsOptions, +} from '@kbn/securitysolution-io-ts-list-types'; +import { fetchExceptionListsItemsByListIds } from '@kbn/securitysolution-list-api'; -import { fetchExceptionListsItemsByListIds } from '../api'; -import { FilterExceptionsOptions, Pagination, UseExceptionListProps } from '../types'; -import { getIdsAndNamespaces } from '../utils'; +import { getIdsAndNamespaces } from '@kbn/securitysolution-list-utils'; import { transformInput } from '../transforms'; type Func = () => void; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.test.ts new file mode 100644 index 0000000000000..7236000fef999 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('useExceptionLists', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/exceptions/hooks/use_exception_lists.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_lists.ts b/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.ts similarity index 86% rename from x-pack/plugins/lists/public/exceptions/hooks/use_exception_lists.ts rename to packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.ts index 31f4106c3afbb..a9a93aa8df49a 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_lists.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_exception_lists/index.ts @@ -1,16 +1,20 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { useEffect, useMemo, useRef, useState } from 'react'; -import type { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; +import type { + ExceptionListSchema, + UseExceptionListsProps, + Pagination, +} from '@kbn/securitysolution-io-ts-list-types'; +import { fetchExceptionLists } from '@kbn/securitysolution-list-api'; -import { fetchExceptionLists } from '../api'; -import { Pagination, UseExceptionListsProps } from '../types'; -import { getFilters } from '../utils'; +import { getFilters } from '@kbn/securitysolution-list-utils'; export type Func = () => void; export type ReturnExceptionLists = [boolean, ExceptionListSchema[], Pagination, Func | null]; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.test.ts new file mode 100644 index 0000000000000..92ac43c834273 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('useExceptionLists', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/lists/hooks/use_export_list.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.ts new file mode 100644 index 0000000000000..9e312f7e3a112 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_export_list/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { exportList } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '../with_optional_signal'; +import { useAsync } from '../use_async'; + +const exportListWithOptionalSignal = withOptionalSignal(exportList); + +export const useExportList = () => useAsync(exportListWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.test.ts new file mode 100644 index 0000000000000..c236be7d03750 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('useFindLists', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/lists/hooks/use_find_lists.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.ts new file mode 100644 index 0000000000000..42d1c54e37ab4 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { findLists } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '../with_optional_signal'; +import { useAsync } from '../use_async'; + +const findListsWithOptionalSignal = withOptionalSignal(findLists); + +export const useFindLists = () => useAsync(findListsWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.test.ts new file mode 100644 index 0000000000000..0bf2a722a8332 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('useImportList', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/lists/hooks/use_import_list.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts new file mode 100644 index 0000000000000..f3c0cfb14130e --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { importList } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '../with_optional_signal'; +import { useAsync } from '../use_async'; + +const importListWithOptionalSignal = withOptionalSignal(importList); + +export const useImportList = () => useAsync(importListWithOptionalSignal); diff --git a/x-pack/plugins/lists/public/common/hooks/use_is_mounted.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_is_mounted/index.test.ts similarity index 73% rename from x-pack/plugins/lists/public/common/hooks/use_is_mounted.test.ts rename to packages/kbn-securitysolution-list-hooks/src/use_is_mounted/index.test.ts index 98632db3624f3..8f6c9051a4f31 100644 --- a/x-pack/plugins/lists/public/common/hooks/use_is_mounted.test.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_is_mounted/index.test.ts @@ -1,13 +1,14 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { renderHook } from '@testing-library/react-hooks'; -import { useIsMounted } from './use_is_mounted'; +import { useIsMounted } from '.'; describe('useIsMounted', () => { it('evaluates to true when mounted', () => { diff --git a/x-pack/plugins/lists/public/common/hooks/use_is_mounted.ts b/packages/kbn-securitysolution-list-hooks/src/use_is_mounted/index.ts similarity index 72% rename from x-pack/plugins/lists/public/common/hooks/use_is_mounted.ts rename to packages/kbn-securitysolution-list-hooks/src/use_is_mounted/index.ts index 8d325aed1abd6..98c2a6cc3e405 100644 --- a/x-pack/plugins/lists/public/common/hooks/use_is_mounted.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_is_mounted/index.ts @@ -1,14 +1,17 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 } from 'react'; type GetIsMounted = () => boolean; +// TODO: This is probably better off in another package such as kbn-securitysolution-hook-utils + /** * * @returns A {@link GetIsMounted} getter function returning whether the component is currently mounted diff --git a/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.test.ts new file mode 100644 index 0000000000000..3971d066ddee1 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('usePersistExceptionItem', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.ts b/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.ts similarity index 88% rename from x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.ts rename to packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.ts index 0ed3668b94c0c..bd874d30efb20 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_item.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.ts @@ -1,19 +1,20 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { Dispatch, useEffect, useState } from 'react'; import type { CreateExceptionListItemSchema, + PersistHookProps, UpdateExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; +import { addExceptionListItem, updateExceptionListItem } from '@kbn/securitysolution-list-api'; -import { addExceptionListItem, updateExceptionListItem } from '../api'; import { transformNewItemOutput, transformOutput } from '../transforms'; -import { PersistHookProps } from '../types'; interface PersistReturnExceptionItem { isLoading: boolean; @@ -25,6 +26,8 @@ export type ReturnPersistExceptionItem = [ Dispatch ]; +// TODO: Add this to @kbn/securitysolution-list-hooks + /** * Hook for creating or updating ExceptionListItem * diff --git a/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.test.ts new file mode 100644 index 0000000000000..6fe38f4d6d273 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('usePersistExceptionList', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.ts b/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.ts similarity index 83% rename from x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.ts rename to packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.ts index eccd8532a7e2f..fcce937cc52ab 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/persist_exception_list.ts +++ b/packages/kbn-securitysolution-list-hooks/src/use_persist_exception_list/index.ts @@ -1,15 +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. + * 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 { Dispatch, useEffect, useState } from 'react'; -import type { UpdateExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; - -import { addExceptionList, updateExceptionList } from '../api'; -import { AddExceptionList, PersistHookProps } from '../types'; +import type { + AddExceptionList, + PersistHookProps, + UpdateExceptionListSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { addExceptionList, updateExceptionList } from '@kbn/securitysolution-list-api'; interface PersistReturnExceptionList { isLoading: boolean; diff --git a/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.test.ts new file mode 100644 index 0000000000000..bb63ed86d01ef --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('useReadListIndex', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/public/lists/hooks/use_read_list_index.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.ts new file mode 100644 index 0000000000000..ef68b3ee6c3f9 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_read_list_index/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { readListIndex } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '../with_optional_signal'; +import { useAsync } from '../use_async'; + +const readListIndexWithOptionalSignal = withOptionalSignal(readListIndex); + +export const useReadListIndex = () => useAsync(readListIndexWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/src/use_read_list_privileges/index.ts b/packages/kbn-securitysolution-list-hooks/src/use_read_list_privileges/index.ts new file mode 100644 index 0000000000000..f7c530196087f --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/use_read_list_privileges/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { readListPrivileges } from '@kbn/securitysolution-list-api'; +import { withOptionalSignal } from '../with_optional_signal'; +import { useAsync } from '../use_async'; + +const readListPrivilegesWithOptionalSignal = withOptionalSignal(readListPrivileges); + +export const useReadListPrivileges = () => useAsync(readListPrivilegesWithOptionalSignal); diff --git a/packages/kbn-securitysolution-list-hooks/src/with_optional_signal/index.test.ts b/packages/kbn-securitysolution-list-hooks/src/with_optional_signal/index.test.ts new file mode 100644 index 0000000000000..89068bd768ee4 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/with_optional_signal/index.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { withOptionalSignal } from '.'; + +type TestFn = ({ number, signal }: { number: number; signal: AbortSignal }) => boolean; + +describe('withOptionalSignal', () => { + it('does not require a signal on the returned function', () => { + const fn = jest.fn().mockReturnValue('hello') as TestFn; + + const wrappedFn = withOptionalSignal(fn); + + expect(wrappedFn({ number: 1 })).toEqual('hello'); + }); + + it('will pass a given signal to the wrapped function', () => { + const fn = jest.fn().mockReturnValue('hello') as TestFn; + const { signal } = new AbortController(); + + const wrappedFn = withOptionalSignal(fn); + + wrappedFn({ number: 1, signal }); + expect(fn).toHaveBeenCalledWith({ number: 1, signal }); + }); +}); diff --git a/packages/kbn-securitysolution-list-hooks/src/with_optional_signal/index.ts b/packages/kbn-securitysolution-list-hooks/src/with_optional_signal/index.ts new file mode 100644 index 0000000000000..55d444e0e4d7a --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/src/with_optional_signal/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +interface SignalArgs { + signal: AbortSignal; +} + +export type OptionalSignalArgs = Omit & Partial; + +// TODO: This is probably better off in another package such as kbn-securitysolution-hook-utils + +/** + * + * @param fn an async function receiving an AbortSignal argument + * + * @returns An async function where the AbortSignal argument is optional + */ +export const withOptionalSignal = (fn: (args: Args) => Result) => ( + args: OptionalSignalArgs +): Result => { + const signal = args.signal != null ? args.signal : new AbortController().signal; + return fn({ ...args, signal } as Args); +}; diff --git a/packages/kbn-securitysolution-list-hooks/tsconfig.json b/packages/kbn-securitysolution-list-hooks/tsconfig.json new file mode 100644 index 0000000000000..9e99c22ea9a07 --- /dev/null +++ b/packages/kbn-securitysolution-list-hooks/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "incremental": true, + "outDir": "target", + "rootDir": "src", + "sourceMap": true, + "sourceRoot": "../../../../packages/kbn-securitysolution-list-hooks/src", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-securitysolution-list-utils/BUILD.bazel b/packages/kbn-securitysolution-list-utils/BUILD.bazel index f79a2ffb21c54..0d257a95f0259 100644 --- a/packages/kbn-securitysolution-list-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-list-utils/BUILD.bazel @@ -29,13 +29,16 @@ NPM_MODULE_EXTRA_FILES = [ SRC_DEPS = [ "//packages/kbn-i18n", + "//packages/kbn-securitysolution-list-constants", "//packages/kbn-securitysolution-io-ts-list-types", "//packages/kbn-securitysolution-utils", + "@npm//lodash", "@npm//tslib", ] TYPES_DEPS = [ "@npm//@types/jest", + "@npm//@types/lodash", "@npm//@types/node", ] @@ -68,7 +71,7 @@ js_library( package_name = PKG_REQUIRE_NAME, srcs = NPM_MODULE_EXTRA_FILES, visibility = ["//visibility:public"], - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], ) pkg_npm( diff --git a/packages/kbn-securitysolution-list-utils/src/build_exception_filter/index.test.ts b/packages/kbn-securitysolution-list-utils/src/build_exception_filter/index.test.ts new file mode 100644 index 0000000000000..78277845fc7e3 --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/build_exception_filter/index.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +describe('build_exceptions_filter', () => { + test('Tests should be ported', () => { + // TODO: Port all the tests from: x-pack/plugins/lists/common/exceptions/build_exceptions_filter.test.ts here once mocks are figured out and kbn package mocks are figured out + expect(true).toBe(true); + }); +}); diff --git a/x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts b/packages/kbn-securitysolution-list-utils/src/build_exception_filter/index.ts similarity index 89% rename from x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts rename to packages/kbn-securitysolution-list-utils/src/build_exception_filter/index.ts index 9276f46e8a82c..080bd0a311d7e 100644 --- a/x-pack/plugins/lists/common/exceptions/build_exceptions_filter.ts +++ b/packages/kbn-securitysolution-list-utils/src/build_exception_filter/index.ts @@ -1,11 +1,13 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { chunk } from 'lodash/fp'; + import { CreateExceptionListItemSchema, EntryExists, @@ -19,10 +21,15 @@ import { entriesNested, } from '@kbn/securitysolution-io-ts-list-types'; -import type { Filter } from '../../../../../src/plugins/data/common'; +import { hasLargeValueList } from '../has_large_value_list'; -import type { BooleanFilter, NestedFilter } from './types'; -import { hasLargeValueList } from './utils'; +/** + * Originally this was an import type of: + * import type { Filter } from '../../../../../src/plugins/data/common'; + * TODO: Once we have the type for this within kbn packages, replace this with that one + * @deprecated + */ +type Filter = any; type NonListEntry = EntryMatch | EntryMatchAny | EntryNested | EntryExists; interface ExceptionListItemNonLargeList extends ExceptionListItemSchema { @@ -37,6 +44,24 @@ export type ExceptionItemSansLargeValueLists = | ExceptionListItemNonLargeList | CreateExceptionListItemNonLargeList; +export interface BooleanFilter { + bool: { + must?: unknown | unknown[]; + must_not?: unknown | unknown[]; + should?: unknown[]; + filter?: unknown | unknown[]; + minimum_should_match?: number; + }; +} + +export interface NestedFilter { + nested: { + path: string; + query: unknown | unknown[]; + score_mode: string; + }; +} + export const chunkExceptions = ( exceptions: ExceptionItemSansLargeValueLists[], chunkSize: number diff --git a/packages/kbn-securitysolution-list-utils/src/get_exception_list_type/index.ts b/packages/kbn-securitysolution-list-utils/src/get_exception_list_type/index.ts new file mode 100644 index 0000000000000..eb68e2486686f --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_exception_list_type/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; + +import { exceptionListAgnosticSavedObjectType } from '../types'; + +export const getExceptionListType = ({ + savedObjectType, +}: { + savedObjectType: string; +}): NamespaceType => { + if (savedObjectType === exceptionListAgnosticSavedObjectType) { + return 'agnostic'; + } else { + return 'single'; + } +}; diff --git a/packages/kbn-securitysolution-list-utils/src/get_filters/index.test.ts b/packages/kbn-securitysolution-list-utils/src/get_filters/index.test.ts new file mode 100644 index 0000000000000..327a29dc1b987 --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_filters/index.test.ts @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { getFilters } from '.'; + +describe('getFilters', () => { + describe('single', () => { + test('it properly formats when no filters passed and "showTrustedApps" is false', () => { + const filter = getFilters({}, ['single'], false); + + expect(filter).toEqual('(not exception-list.attributes.list_id: endpoint_trusted_apps*)'); + }); + + test('it properly formats when no filters passed and "showTrustedApps" is true', () => { + const filter = getFilters({}, ['single'], true); + + expect(filter).toEqual('(exception-list.attributes.list_id: endpoint_trusted_apps*)'); + }); + + test('it properly formats when filters passed and "showTrustedApps" is false', () => { + const filter = getFilters({ created_by: 'moi', name: 'Sample' }, ['single'], false); + + expect(filter).toEqual( + '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample) AND (not exception-list.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + + test('it if filters passed and "showTrustedApps" is true', () => { + const filter = getFilters({ created_by: 'moi', name: 'Sample' }, ['single'], true); + + expect(filter).toEqual( + '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample) AND (exception-list.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + }); + + describe('agnostic', () => { + test('it properly formats when no filters passed and "showTrustedApps" is false', () => { + const filter = getFilters({}, ['agnostic'], false); + + expect(filter).toEqual( + '(not exception-list-agnostic.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + + test('it properly formats when no filters passed and "showTrustedApps" is true', () => { + const filter = getFilters({}, ['agnostic'], true); + + expect(filter).toEqual( + '(exception-list-agnostic.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + + test('it properly formats when filters passed and "showTrustedApps" is false', () => { + const filter = getFilters({ created_by: 'moi', name: 'Sample' }, ['agnostic'], false); + + expect(filter).toEqual( + '(exception-list-agnostic.attributes.created_by:moi) AND (exception-list-agnostic.attributes.name.text:Sample) AND (not exception-list-agnostic.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + + test('it if filters passed and "showTrustedApps" is true', () => { + const filter = getFilters({ created_by: 'moi', name: 'Sample' }, ['agnostic'], true); + + expect(filter).toEqual( + '(exception-list-agnostic.attributes.created_by:moi) AND (exception-list-agnostic.attributes.name.text:Sample) AND (exception-list-agnostic.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + }); + + describe('single, agnostic', () => { + test('it properly formats when no filters passed and "showTrustedApps" is false', () => { + const filter = getFilters({}, ['single', 'agnostic'], false); + + expect(filter).toEqual( + '(not exception-list.attributes.list_id: endpoint_trusted_apps* AND not exception-list-agnostic.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + + test('it properly formats when no filters passed and "showTrustedApps" is true', () => { + const filter = getFilters({}, ['single', 'agnostic'], true); + + expect(filter).toEqual( + '(exception-list.attributes.list_id: endpoint_trusted_apps* OR exception-list-agnostic.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + + test('it properly formats when filters passed and "showTrustedApps" is false', () => { + const filter = getFilters( + { created_by: 'moi', name: 'Sample' }, + ['single', 'agnostic'], + false + ); + + expect(filter).toEqual( + '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample) AND (not exception-list.attributes.list_id: endpoint_trusted_apps* AND not exception-list-agnostic.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + + test('it properly formats when filters passed and "showTrustedApps" is true', () => { + const filter = getFilters( + { created_by: 'moi', name: 'Sample' }, + ['single', 'agnostic'], + true + ); + + expect(filter).toEqual( + '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample) AND (exception-list.attributes.list_id: endpoint_trusted_apps* OR exception-list-agnostic.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + }); +}); diff --git a/packages/kbn-securitysolution-list-utils/src/get_filters/index.ts b/packages/kbn-securitysolution-list-utils/src/get_filters/index.ts new file mode 100644 index 0000000000000..c9dd6ccae484c --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_filters/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { ExceptionListFilter, NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; +import { getGeneralFilters } from '../get_general_filters'; +import { getSavedObjectTypes } from '../get_saved_object_types'; +import { getTrustedAppsFilter } from '../get_trusted_apps_filter'; + +export const getFilters = ( + filters: ExceptionListFilter, + namespaceTypes: NamespaceType[], + showTrustedApps: boolean +): string => { + const namespaces = getSavedObjectTypes({ namespaceType: namespaceTypes }); + const generalFilters = getGeneralFilters(filters, namespaces); + const trustedAppsFilter = getTrustedAppsFilter(showTrustedApps, namespaces); + return [generalFilters, trustedAppsFilter].filter((filter) => filter.trim() !== '').join(' AND '); +}; diff --git a/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.test.ts b/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.test.ts new file mode 100644 index 0000000000000..8786b48b73c82 --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { getGeneralFilters } from '.'; + +describe('getGeneralFilters', () => { + test('it returns empty string if no filters', () => { + const filters = getGeneralFilters({}, ['exception-list']); + + expect(filters).toEqual(''); + }); + + test('it properly formats filters when one namespace type passed in', () => { + const filters = getGeneralFilters({ created_by: 'moi', name: 'Sample' }, ['exception-list']); + + expect(filters).toEqual( + '(exception-list.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample)' + ); + }); + + test('it properly formats filters when two namespace types passed in', () => { + const filters = getGeneralFilters({ created_by: 'moi', name: 'Sample' }, [ + 'exception-list', + 'exception-list-agnostic', + ]); + + expect(filters).toEqual( + '(exception-list.attributes.created_by:moi OR exception-list-agnostic.attributes.created_by:moi) AND (exception-list.attributes.name.text:Sample OR exception-list-agnostic.attributes.name.text:Sample)' + ); + }); +}); diff --git a/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.ts b/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.ts new file mode 100644 index 0000000000000..f44e37e547fe9 --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_general_filters/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { ExceptionListFilter } from '@kbn/securitysolution-io-ts-list-types'; +import { get } from 'lodash/fp'; +import { SavedObjectType } from '../types'; + +export const getGeneralFilters = ( + filters: ExceptionListFilter, + namespaceTypes: SavedObjectType[] +): string => { + return Object.keys(filters) + .map((filterKey) => { + const value = get(filterKey, filters); + if (value != null && value.trim() !== '') { + const filtersByNamespace = namespaceTypes + .map((namespace) => { + const fieldToSearch = filterKey === 'name' ? 'name.text' : filterKey; + return `${namespace}.attributes.${fieldToSearch}:${value}`; + }) + .join(' OR '); + return `(${filtersByNamespace})`; + } else return null; + }) + .filter((item) => item != null) + .join(' AND '); +}; diff --git a/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.test.ts b/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.test.ts new file mode 100644 index 0000000000000..6ecba8b97207a --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { getIdsAndNamespaces } from '.'; + +describe('getIdsAndNamespaces', () => { + test('it returns empty arrays if no lists found', async () => { + const output = getIdsAndNamespaces({ + lists: [], + showDetection: false, + showEndpoint: false, + }); + + expect(output).toEqual({ ids: [], namespaces: [] }); + }); + + test('it returns all lists if "showDetection" and "showEndpoint" are "false"', async () => { + const output = getIdsAndNamespaces({ + lists: [ + { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, + { + id: 'myListIdEndpoint', + listId: 'list_id_endpoint', + namespaceType: 'agnostic', + type: 'endpoint', + }, + ], + showDetection: false, + showEndpoint: false, + }); + + expect(output).toEqual({ + ids: ['list_id', 'list_id_endpoint'], + namespaces: ['single', 'agnostic'], + }); + }); + + test('it returns only detections lists if "showDetection" is "true"', async () => { + const output = getIdsAndNamespaces({ + lists: [ + { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, + { + id: 'myListIdEndpoint', + listId: 'list_id_endpoint', + namespaceType: 'agnostic', + type: 'endpoint', + }, + ], + showDetection: true, + showEndpoint: false, + }); + + expect(output).toEqual({ + ids: ['list_id'], + namespaces: ['single'], + }); + }); + + test('it returns only endpoint lists if "showEndpoint" is "true"', async () => { + const output = getIdsAndNamespaces({ + lists: [ + { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, + { + id: 'myListIdEndpoint', + listId: 'list_id_endpoint', + namespaceType: 'agnostic', + type: 'endpoint', + }, + ], + showDetection: false, + showEndpoint: true, + }); + + expect(output).toEqual({ + ids: ['list_id_endpoint'], + namespaces: ['agnostic'], + }); + }); + + test('it returns only detection lists if both "showEndpoint" and "showDetection" are "true"', async () => { + const output = getIdsAndNamespaces({ + lists: [ + { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' }, + { + id: 'myListIdEndpoint', + listId: 'list_id_endpoint', + namespaceType: 'agnostic', + type: 'endpoint', + }, + ], + showDetection: true, + showEndpoint: true, + }); + + expect(output).toEqual({ + ids: ['list_id'], + namespaces: ['single'], + }); + }); +}); diff --git a/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.ts b/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.ts new file mode 100644 index 0000000000000..a1ab4c14728e4 --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_ids_and_namespaces/index.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { ExceptionListIdentifiers, NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; + +export const getIdsAndNamespaces = ({ + lists, + showDetection, + showEndpoint, +}: { + lists: ExceptionListIdentifiers[]; + showDetection: boolean; + showEndpoint: boolean; +}): { ids: string[]; namespaces: NamespaceType[] } => + lists + .filter((list) => { + if (showDetection) { + return list.type === 'detection'; + } else if (showEndpoint) { + return list.type === 'endpoint'; + } else { + return true; + } + }) + .reduce<{ ids: string[]; namespaces: NamespaceType[] }>( + (acc, { listId, namespaceType }) => ({ + ids: [...acc.ids, listId], + namespaces: [...acc.namespaces, namespaceType], + }), + { ids: [], namespaces: [] } + ); diff --git a/packages/kbn-securitysolution-list-utils/src/get_saved_object_type/index.ts b/packages/kbn-securitysolution-list-utils/src/get_saved_object_type/index.ts new file mode 100644 index 0000000000000..1d59694e43366 --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_saved_object_type/index.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 and the 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 { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; + +import { + exceptionListAgnosticSavedObjectType, + exceptionListSavedObjectType, + SavedObjectType, +} from '../types'; + +export const getSavedObjectType = ({ + namespaceType, +}: { + namespaceType: NamespaceType; +}): SavedObjectType => { + if (namespaceType === 'agnostic') { + return exceptionListAgnosticSavedObjectType; + } else { + return exceptionListSavedObjectType; + } +}; diff --git a/packages/kbn-securitysolution-list-utils/src/get_saved_object_types/index.ts b/packages/kbn-securitysolution-list-utils/src/get_saved_object_types/index.ts new file mode 100644 index 0000000000000..f61dfe071802d --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_saved_object_types/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { NamespaceTypeArray } from '@kbn/securitysolution-io-ts-list-types'; + +import { SavedObjectType } from '../types'; +import { getSavedObjectType } from '../get_saved_object_type'; + +export const getSavedObjectTypes = ({ + namespaceType, +}: { + namespaceType: NamespaceTypeArray; +}): SavedObjectType[] => { + return namespaceType.map((singleNamespaceType) => + getSavedObjectType({ namespaceType: singleNamespaceType }) + ); +}; diff --git a/packages/kbn-securitysolution-list-utils/src/get_trusted_apps_filter/index.test.ts b/packages/kbn-securitysolution-list-utils/src/get_trusted_apps_filter/index.test.ts new file mode 100644 index 0000000000000..da178b15390e6 --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_trusted_apps_filter/index.test.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 and the 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 { getTrustedAppsFilter } from '.'; + +describe('getTrustedAppsFilter', () => { + test('it returns filter to search for "exception-list" namespace trusted apps', () => { + const filter = getTrustedAppsFilter(true, ['exception-list']); + + expect(filter).toEqual('(exception-list.attributes.list_id: endpoint_trusted_apps*)'); + }); + + test('it returns filter to search for "exception-list" and "agnostic" namespace trusted apps', () => { + const filter = getTrustedAppsFilter(true, ['exception-list', 'exception-list-agnostic']); + + expect(filter).toEqual( + '(exception-list.attributes.list_id: endpoint_trusted_apps* OR exception-list-agnostic.attributes.list_id: endpoint_trusted_apps*)' + ); + }); + + test('it returns filter to exclude "exception-list" namespace trusted apps', () => { + const filter = getTrustedAppsFilter(false, ['exception-list']); + + expect(filter).toEqual('(not exception-list.attributes.list_id: endpoint_trusted_apps*)'); + }); + + test('it returns filter to exclude "exception-list" and "agnostic" namespace trusted apps', () => { + const filter = getTrustedAppsFilter(false, ['exception-list', 'exception-list-agnostic']); + + expect(filter).toEqual( + '(not exception-list.attributes.list_id: endpoint_trusted_apps* AND not exception-list-agnostic.attributes.list_id: endpoint_trusted_apps*)' + ); + }); +}); diff --git a/packages/kbn-securitysolution-list-utils/src/get_trusted_apps_filter/index.ts b/packages/kbn-securitysolution-list-utils/src/get_trusted_apps_filter/index.ts new file mode 100644 index 0000000000000..9c969068d4edf --- /dev/null +++ b/packages/kbn-securitysolution-list-utils/src/get_trusted_apps_filter/index.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 and the 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 { ENDPOINT_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants'; +import { SavedObjectType } from '../types'; + +export const getTrustedAppsFilter = ( + showTrustedApps: boolean, + namespaceTypes: SavedObjectType[] +): string => { + if (showTrustedApps) { + const filters = namespaceTypes.map((namespace) => { + return `${namespace}.attributes.list_id: ${ENDPOINT_TRUSTED_APPS_LIST_ID}*`; + }); + return `(${filters.join(' OR ')})`; + } else { + const filters = namespaceTypes.map((namespace) => { + return `not ${namespace}.attributes.list_id: ${ENDPOINT_TRUSTED_APPS_LIST_ID}*`; + }); + return `(${filters.join(' AND ')})`; + } +}; diff --git a/x-pack/plugins/lists/common/exceptions/utils.ts b/packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.ts similarity index 68% rename from x-pack/plugins/lists/common/exceptions/utils.ts rename to packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.ts index 350cb581153b5..fd7f4bcf8c784 100644 --- a/x-pack/plugins/lists/common/exceptions/utils.ts +++ b/packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { EntriesArray } from '@kbn/securitysolution-io-ts-list-types'; diff --git a/packages/kbn-securitysolution-list-utils/src/index.ts b/packages/kbn-securitysolution-list-utils/src/index.ts index 55dd47d00e4da..9e88cac6b5d19 100644 --- a/packages/kbn-securitysolution-list-utils/src/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/index.ts @@ -6,5 +6,14 @@ * Side Public License, v 1. */ export * from './autocomplete_operators'; +export * from './build_exception_filter'; +export * from './get_exception_list_type'; +export * from './get_filters'; +export * from './get_general_filters'; +export * from './get_ids_and_namespaces'; +export * from './get_saved_object_type'; +export * from './get_saved_object_types'; +export * from './get_trusted_apps_filter'; +export * from './has_large_value_list'; export * from './helpers'; export * from './types'; diff --git a/packages/kbn-securitysolution-list-utils/src/types/index.ts b/packages/kbn-securitysolution-list-utils/src/types/index.ts index c8603fa01157c..faf68ca157981 100644 --- a/packages/kbn-securitysolution-list-utils/src/types/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/types/index.ts @@ -18,6 +18,10 @@ import type { ListOperatorEnum as OperatorEnum, ListOperatorTypeEnum as OperatorTypeEnum, } from '@kbn/securitysolution-io-ts-list-types'; +import { + EXCEPTION_LIST_NAMESPACE, + EXCEPTION_LIST_NAMESPACE_AGNOSTIC, +} from '@kbn/securitysolution-list-constants'; import type { OperatorOption } from '../autocomplete_operators/types'; @@ -98,3 +102,9 @@ export type CreateExceptionListItemBuilderSchema = Omit< export type ExceptionsBuilderExceptionItem = | ExceptionListItemBuilderSchema | CreateExceptionListItemBuilderSchema; + +export const exceptionListSavedObjectType = EXCEPTION_LIST_NAMESPACE; +export const exceptionListAgnosticSavedObjectType = EXCEPTION_LIST_NAMESPACE_AGNOSTIC; +export type SavedObjectType = + | typeof EXCEPTION_LIST_NAMESPACE + | typeof EXCEPTION_LIST_NAMESPACE_AGNOSTIC; diff --git a/packages/kbn-securitysolution-utils/BUILD.bazel b/packages/kbn-securitysolution-utils/BUILD.bazel index 42897e93593b6..6084480ef6a15 100644 --- a/packages/kbn-securitysolution-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-utils/BUILD.bazel @@ -67,7 +67,7 @@ js_library( package_name = PKG_REQUIRE_NAME, srcs = NPM_MODULE_EXTRA_FILES, visibility = ["//visibility:public"], - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], ) pkg_npm( diff --git a/packages/kbn-server-http-tools/BUILD.bazel b/packages/kbn-server-http-tools/BUILD.bazel index 61570969c85f1..d654527b2658a 100644 --- a/packages/kbn-server-http-tools/BUILD.bazel +++ b/packages/kbn-server-http-tools/BUILD.bazel @@ -69,7 +69,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-std/BUILD.bazel b/packages/kbn-std/BUILD.bazel index 82520be97df1f..e60577f0f0853 100644 --- a/packages/kbn-std/BUILD.bazel +++ b/packages/kbn-std/BUILD.bazel @@ -64,7 +64,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-telemetry-tools/BUILD.bazel b/packages/kbn-telemetry-tools/BUILD.bazel index 4d1b4f21117c4..9a6b4a10bd190 100644 --- a/packages/kbn-telemetry-tools/BUILD.bazel +++ b/packages/kbn-telemetry-tools/BUILD.bazel @@ -76,7 +76,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts b/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts index 1aa5df1105f46..2d05d5bba5ff6 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts @@ -12,6 +12,7 @@ import { loadTracer } from '../load_tracer'; import { createAsyncInstance, isAsyncInstance } from './async_instance'; import { Providers } from './read_provider_spec'; import { createVerboseInstance } from './verbose_instance'; +import { GenericFtrService } from '../../public_types'; export class ProviderCollection { private readonly instances = new Map(); @@ -58,12 +59,19 @@ export class ProviderCollection { } public invokeProviderFn(provider: (args: any) => any) { - return provider({ + const ctx = { getService: this.getService, hasService: this.hasService, getPageObject: this.getPageObject, getPageObjects: this.getPageObjects, - }); + }; + + if (provider.prototype instanceof GenericFtrService) { + const Constructor = (provider as any) as new (ctx: any) => any; + return new Constructor(ctx); + } + + return provider(ctx); } private findProvider(type: string, name: string) { diff --git a/packages/kbn-test/src/functional_test_runner/public_types.ts b/packages/kbn-test/src/functional_test_runner/public_types.ts index 915cb34f6ffe5..4a30744c09b51 100644 --- a/packages/kbn-test/src/functional_test_runner/public_types.ts +++ b/packages/kbn-test/src/functional_test_runner/public_types.ts @@ -13,7 +13,7 @@ import { Test, Suite } from './fake_mocha_types'; export { Lifecycle, Config, FailureMetadata }; -interface AsyncInstance { +export interface AsyncInstance { /** * Services that are initialized async are not ready before the tests execute, so you might need * to call `init()` and await the promise it returns before interacting with the service @@ -39,7 +39,11 @@ export type ProvidedType any> = MaybeAsyncInstance * promise types into the async instances that other providers will receive. */ type ProvidedTypeMap = { - [K in keyof T]: T[K] extends (...args: any[]) => any ? ProvidedType : unknown; + [K in keyof T]: T[K] extends new (...args: any[]) => infer X + ? X + : T[K] extends (...args: any[]) => any + ? ProvidedType + : unknown; }; export interface GenericFtrProviderContext< @@ -84,6 +88,10 @@ export interface GenericFtrProviderContext< loadTestFile(path: string): void; } +export class GenericFtrService> { + constructor(protected readonly ctx: ProviderContext) {} +} + export interface FtrConfigProviderContext { log: ToolingLog; readConfigFile(path: string): Promise; diff --git a/packages/kbn-utility-types/BUILD.bazel b/packages/kbn-utility-types/BUILD.bazel index 1a02f94a88f4a..46843b97e746f 100644 --- a/packages/kbn-utility-types/BUILD.bazel +++ b/packages/kbn-utility-types/BUILD.bazel @@ -58,7 +58,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = "@kbn/utility-types", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-utils/BUILD.bazel b/packages/kbn-utils/BUILD.bazel index 57aee048746b4..a2789f4280abf 100644 --- a/packages/kbn-utils/BUILD.bazel +++ b/packages/kbn-utils/BUILD.bazel @@ -61,7 +61,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = [":tsc"] + DEPS, + deps = DEPS + [":tsc"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/renovate.json5 b/renovate.json5 index ea41175e1aaab..f533eac479650 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -12,6 +12,7 @@ baseBranches: [ 'master', '7.x', + '7.13', ], prConcurrentLimit: 0, prHourlyLimit: 0, @@ -44,17 +45,25 @@ { groupName: '@elastic/elasticsearch', packageNames: ['@elastic/elasticsearch'], - reviewers: ['team:kibana-operations'], + reviewers: ['team:kibana-operations', 'team:kibana-core'], matchBaseBranches: ['master'], - labels: ['release_note:skip', 'v8.0.0', 'Team:Operations', 'backport:skip'], + labels: ['release_note:skip', 'v8.0.0', 'Team:Operations', 'Team:Core', 'backport:skip'], enabled: true, }, { groupName: '@elastic/elasticsearch', packageNames: ['@elastic/elasticsearch'], - reviewers: ['team:kibana-operations'], + reviewers: ['team:kibana-operations', 'team:kibana-core'], matchBaseBranches: ['7.x'], - labels: ['release_note:skip', 'v7.14.0', 'Team:Operations', 'backport:skip'], + labels: ['release_note:skip', 'v7.14.0', 'Team:Operations', 'Team:Core', 'backport:skip'], + enabled: true, + }, + { + groupName: '@elastic/elasticsearch', + packageNames: ['@elastic/elasticsearch'], + reviewers: ['team:kibana-operations'], + matchBaseBranches: ['7.13'], + labels: ['release_note:skip', 'v7.13.0', 'Team:Operations', 'backport:skip'], enabled: true, }, { diff --git a/src/core/public/application/application_service.test.ts b/src/core/public/application/application_service.test.ts index 76b9c7a73d3bd..2e2f1cad49f19 100644 --- a/src/core/public/application/application_service.test.ts +++ b/src/core/public/application/application_service.test.ts @@ -75,7 +75,10 @@ describe('#setup()', () => { const pluginId = Symbol('plugin'); const updater$ = new BehaviorSubject((app) => ({})); setup.register(pluginId, createApp({ id: 'app1', updater$ })); - setup.register(pluginId, createApp({ id: 'app2' })); + setup.register( + pluginId, + createApp({ id: 'app2', deepLinks: [{ id: 'subapp1', title: 'Subapp', path: '/subapp' }] }) + ); const { applications$ } = await service.start(startDeps); let applications = await applications$.pipe(take(1)).toPromise(); @@ -92,6 +95,11 @@ describe('#setup()', () => { id: 'app2', navLinkStatus: AppNavLinkStatus.visible, status: AppStatus.accessible, + deepLinks: [ + expect.objectContaining({ + navLinkStatus: AppNavLinkStatus.hidden, + }), + ], }) ); diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx index 4a93c98205b84..bbfea61220b51 100644 --- a/src/core/public/application/application_service.tsx +++ b/src/core/public/application/application_service.tsx @@ -19,6 +19,7 @@ import { AppRouter } from './ui'; import { Capabilities, CapabilitiesService } from './capabilities'; import { App, + AppDeepLink, AppLeaveHandler, AppMount, AppNavLinkStatus, @@ -166,6 +167,7 @@ export class ApplicationService { ...appProps, status: app.status ?? AppStatus.accessible, navLinkStatus: app.navLinkStatus ?? AppNavLinkStatus.default, + deepLinks: populateDeepLinkDefaults(appProps.deepLinks), }); if (updater$) { registerStatusUpdater(app.id, updater$); @@ -392,3 +394,12 @@ const updateStatus = (app: App, statusUpdaters: AppUpdaterWrapper[]): App => { ...changes, }; }; + +const populateDeepLinkDefaults = (deepLinks?: AppDeepLink[]): AppDeepLink[] => { + if (!deepLinks) return []; + return deepLinks.map((deepLink) => ({ + ...deepLink, + navLinkStatus: deepLink.navLinkStatus ?? AppNavLinkStatus.default, + deepLinks: populateDeepLinkDefaults(deepLink.deepLinks), + })); +}; diff --git a/src/core/public/application/index.ts b/src/core/public/application/index.ts index 1e9a91717e81a..68e1991646afb 100644 --- a/src/core/public/application/index.ts +++ b/src/core/public/application/index.ts @@ -18,8 +18,7 @@ export type { AppMountParameters, AppUpdatableFields, AppUpdater, - AppMeta, - AppSearchDeepLink, + AppDeepLink, ApplicationSetup, ApplicationStart, AppLeaveHandler, @@ -29,8 +28,7 @@ export type { AppLeaveConfirmAction, NavigateToAppOptions, PublicAppInfo, - PublicAppMetaInfo, - PublicAppSearchDeepLinkInfo, + PublicAppDeepLinkInfo, // Internal types InternalApplicationSetup, InternalApplicationStart, diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index 24f46752f28e5..ffc41955360bd 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -63,7 +63,7 @@ export enum AppNavLinkStatus { */ export type AppUpdatableFields = Pick< App, - 'status' | 'navLinkStatus' | 'tooltip' | 'defaultPath' | 'meta' + 'status' | 'navLinkStatus' | 'tooltip' | 'defaultPath' | 'deepLinks' >; /** @@ -211,106 +211,92 @@ export interface App { */ exactRoute?: boolean; + /** Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. */ + keywords?: string[]; + /** - * Meta data for an application that represent additional information for the app. - * See {@link AppMeta} + * Input type for registering secondary in-app locations for an application. * - * @remarks - * Used to populate navigational search results (where available). - * Can be updated using the {@link App.updater$} observable. See {@link PublicAppSearchDeepLinkInfo} for more details. + * Deep links must include at least one of `path` or `deepLinks`. A deep link that does not have a `path` + * represents a topological level in the application's hierarchy, but does not have a destination URL that is + * user-accessible. * * @example * ```ts * core.application.register({ * id: 'my_app', * title: 'Translated title', - * meta: { - * keywords: ['translated keyword1', 'translated keyword2'], - * searchDeepLinks: [ - * { id: 'sub1', title: 'Sub1', path: '/sub1', keywords: ['subpath1'] }, + * keywords: ['translated keyword1', 'translated keyword2'], + * deepLinks: [ + * { + * id: 'sub1', + * title: 'Sub1', + * path: '/sub1', + * keywords: ['subpath1'], + * }, * { * id: 'sub2', * title: 'Sub2', - * searchDeepLinks: [ - * { id: 'subsub', title: 'SubSub', path: '/sub2/sub', keywords: ['subpath2'] } - * ] - * } + * deepLinks: [ + * { + * id: 'subsub', + * title: 'SubSub', + * path: '/sub2/sub', + * keywords: ['subpath2'], + * }, + * ], + * }, * ], - * }, * mount: () => { ... } * }) * ``` */ - meta?: AppMeta; -} - -/** - * Input type for meta data for an application. - * - * Meta fields include `keywords` and `searchDeepLinks` - * Keywords is an array of string with which to associate the app, must include at least one unique string as an array. - * `searchDeepLinks` is an array of links that represent secondary in-app locations for the app. - * @public - */ -export interface AppMeta { - /** Keywords to represent this application */ - keywords?: string[]; - /** Array of links that represent secondary in-app locations for the app. */ - searchDeepLinks?: AppSearchDeepLink[]; + deepLinks?: AppDeepLink[]; } /** - * Public information about a registered app's {@link AppMeta | keywords } + * Public information about a registered app's {@link AppDeepLink | deepLinks} * * @public */ -export type PublicAppMetaInfo = Omit & { - keywords: string[]; - searchDeepLinks: PublicAppSearchDeepLinkInfo[]; -}; - -/** - * Public information about a registered app's {@link AppSearchDeepLink | searchDeepLinks} - * - * @public - */ -export type PublicAppSearchDeepLinkInfo = Omit< - AppSearchDeepLink, - 'searchDeepLinks' | 'keywords' +export type PublicAppDeepLinkInfo = Omit< + AppDeepLink, + 'deepLinks' | 'keywords' | 'navLinkStatus' > & { - searchDeepLinks: PublicAppSearchDeepLinkInfo[]; + deepLinks: PublicAppDeepLinkInfo[]; keywords: string[]; + navLinkStatus: AppNavLinkStatus; }; /** * Input type for registering secondary in-app locations for an application. * - * Deep links must include at least one of `path` or `searchDeepLinks`. A deep link that does not have a `path` + * Deep links must include at least one of `path` or `deepLinks`. A deep link that does not have a `path` * represents a topological level in the application's hierarchy, but does not have a destination URL that is * user-accessible. * @public */ -export type AppSearchDeepLink = { +export type AppDeepLink = { /** Identifier to represent this sublink, should be unique for this application */ id: string; /** Title to label represent this deep link */ title: string; + /** Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. */ + keywords?: string[]; + /** Optional status of the chrome navigation, defaults to `hidden` */ + navLinkStatus?: AppNavLinkStatus; } & ( | { /** URL path to access this link, relative to the application's appRoute. */ path: string; /** Optional array of links that are 'underneath' this section in the hierarchy */ - searchDeepLinks?: AppSearchDeepLink[]; - /** Optional keywords to match with in deep links search for the page at the path */ - keywords?: string[]; + deepLinks?: AppDeepLink[]; } | { /** Optional path to access this section. Omit if this part of the hierarchy does not have a page URL. */ path?: string; /** Array links that are 'underneath' this section in this hierarchy. */ - searchDeepLinks: AppSearchDeepLink[]; - /** Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. */ - keywords?: string[]; + deepLinks: AppDeepLink[]; } ); @@ -319,12 +305,13 @@ export type AppSearchDeepLink = { * * @public */ -export type PublicAppInfo = Omit & { +export type PublicAppInfo = Omit & { // remove optional on fields populated with default values status: AppStatus; navLinkStatus: AppNavLinkStatus; appRoute: string; - meta: PublicAppMetaInfo; + keywords: string[]; + deepLinks: PublicAppDeepLinkInfo[]; }; /** diff --git a/src/core/public/application/utils/get_app_info.test.ts b/src/core/public/application/utils/get_app_info.test.ts index 28824867234ff..ef4a06707d666 100644 --- a/src/core/public/application/utils/get_app_info.test.ts +++ b/src/core/public/application/utils/get_app_info.test.ts @@ -32,24 +32,20 @@ describe('getAppInfo', () => { status: AppStatus.accessible, navLinkStatus: AppNavLinkStatus.visible, appRoute: `/app/some-id`, - meta: { - keywords: [], - searchDeepLinks: [], - }, + keywords: [], + deepLinks: [], }); }); - it('populates default values for nested searchDeepLinks', () => { + it('populates default values for nested deepLinks', () => { const app = createApp({ - meta: { - searchDeepLinks: [ - { - id: 'sub-id', - title: 'sub-title', - searchDeepLinks: [{ id: 'sub-sub-id', title: 'sub-sub-title', path: '/sub-sub' }], - }, - ], - }, + deepLinks: [ + { + id: 'sub-id', + title: 'sub-title', + deepLinks: [{ id: 'sub-sub-id', title: 'sub-sub-title', path: '/sub-sub' }], + }, + ], }); const info = getAppInfo(app); @@ -59,25 +55,23 @@ describe('getAppInfo', () => { status: AppStatus.accessible, navLinkStatus: AppNavLinkStatus.visible, appRoute: `/app/some-id`, - meta: { - keywords: [], - searchDeepLinks: [ - { - id: 'sub-id', - title: 'sub-title', - keywords: [], - searchDeepLinks: [ - { - id: 'sub-sub-id', - title: 'sub-sub-title', - path: '/sub-sub', - keywords: [], - searchDeepLinks: [], // default empty array added - }, - ], - }, - ], - }, + keywords: [], + deepLinks: [ + { + id: 'sub-id', + title: 'sub-title', + keywords: [], + deepLinks: [ + { + id: 'sub-sub-id', + title: 'sub-sub-title', + path: '/sub-sub', + keywords: [], + deepLinks: [], // default empty array added + }, + ], + }, + ], }); }); @@ -110,22 +104,20 @@ describe('getAppInfo', () => { it('adds default meta fields to sublinks when needed', () => { const app = createApp({ - meta: { - searchDeepLinks: [ - { - id: 'sub-id', - title: 'sub-title', - searchDeepLinks: [ - { - id: 'sub-sub-id', - title: 'sub-sub-title', - path: '/sub-sub', - keywords: ['sub sub'], - }, - ], - }, - ], - }, + deepLinks: [ + { + id: 'sub-id', + title: 'sub-title', + deepLinks: [ + { + id: 'sub-sub-id', + title: 'sub-sub-title', + path: '/sub-sub', + keywords: ['sub sub'], + }, + ], + }, + ], }); const info = getAppInfo(app); @@ -135,25 +127,23 @@ describe('getAppInfo', () => { status: AppStatus.accessible, navLinkStatus: AppNavLinkStatus.visible, appRoute: `/app/some-id`, - meta: { - keywords: [], - searchDeepLinks: [ - { - id: 'sub-id', - title: 'sub-title', - keywords: [], // default empty array - searchDeepLinks: [ - { - id: 'sub-sub-id', - title: 'sub-sub-title', - path: '/sub-sub', - keywords: ['sub sub'], - searchDeepLinks: [], - }, - ], - }, - ], - }, + keywords: [], + deepLinks: [ + { + id: 'sub-id', + title: 'sub-title', + keywords: [], // default empty array + deepLinks: [ + { + id: 'sub-sub-id', + title: 'sub-sub-title', + path: '/sub-sub', + keywords: ['sub sub'], + deepLinks: [], + }, + ], + }, + ], }); }); }); diff --git a/src/core/public/application/utils/get_app_info.ts b/src/core/public/application/utils/get_app_info.ts index ca1e8ac807646..4c94e24f501bc 100644 --- a/src/core/public/application/utils/get_app_info.ts +++ b/src/core/public/application/utils/get_app_info.ts @@ -10,9 +10,9 @@ import { App, AppNavLinkStatus, AppStatus, - AppSearchDeepLink, + AppDeepLink, PublicAppInfo, - PublicAppSearchDeepLinkInfo, + PublicAppDeepLinkInfo, } from '../types'; export function getAppInfo(app: App): PublicAppInfo { @@ -28,29 +28,27 @@ export function getAppInfo(app: App): PublicAppInfo { status: app.status!, navLinkStatus, appRoute: app.appRoute!, - meta: { - keywords: app.meta?.keywords ?? [], - searchDeepLinks: getSearchDeepLinkInfos(app, app.meta?.searchDeepLinks), - }, + keywords: app.keywords ?? [], + deepLinks: getDeepLinkInfos(app.deepLinks), }; } -function getSearchDeepLinkInfos( - app: App, - searchDeepLinks?: AppSearchDeepLink[] -): PublicAppSearchDeepLinkInfo[] { - if (!searchDeepLinks) { - return []; - } +function getDeepLinkInfos(deepLinks?: AppDeepLink[]): PublicAppDeepLinkInfo[] { + if (!deepLinks) return []; - return searchDeepLinks.map( - (rawDeepLink): PublicAppSearchDeepLinkInfo => { + return deepLinks.map( + (rawDeepLink): PublicAppDeepLinkInfo => { + const navLinkStatus = + rawDeepLink.navLinkStatus === AppNavLinkStatus.default + ? AppNavLinkStatus.hidden + : rawDeepLink.navLinkStatus!; return { id: rawDeepLink.id, title: rawDeepLink.title, path: rawDeepLink.path, keywords: rawDeepLink.keywords ?? [], - searchDeepLinks: getSearchDeepLinkInfos(app, rawDeepLink.searchDeepLinks), + navLinkStatus, + deepLinks: getDeepLinkInfos(rawDeepLink.deepLinks), }; } ); diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index ae9c58af69603..b624084258817 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -20,7 +20,6 @@ const createStartContractMock = () => { get: jest.fn(), getAll: jest.fn(), showOnly: jest.fn(), - update: jest.fn(), enableForcedAppSwitcherNavigation: jest.fn(), getForceAppSwitcherNavigation$: jest.fn(), }, diff --git a/src/core/public/chrome/index.ts b/src/core/public/chrome/index.ts index 73499a81f2220..dd7affcdbf7cd 100644 --- a/src/core/public/chrome/index.ts +++ b/src/core/public/chrome/index.ts @@ -16,7 +16,7 @@ export type { ChromeHelpExtensionMenuGitHubLink, } from './ui/header/header_help_menu'; export type { NavType } from './ui'; -export type { ChromeNavLink, ChromeNavLinks, ChromeNavLinkUpdateableFields } from './nav_links'; +export type { ChromeNavLink, ChromeNavLinks } from './nav_links'; export type { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, diff --git a/src/core/public/chrome/nav_links/index.ts b/src/core/public/chrome/nav_links/index.ts index a35a512b2a842..e41799b9918a3 100644 --- a/src/core/public/chrome/nav_links/index.ts +++ b/src/core/public/chrome/nav_links/index.ts @@ -7,5 +7,5 @@ */ export { NavLinksService } from './nav_links_service'; -export type { ChromeNavLink, ChromeNavLinkUpdateableFields } from './nav_link'; +export type { ChromeNavLink } from './nav_link'; export type { ChromeNavLinks } from './nav_links_service'; diff --git a/src/core/public/chrome/nav_links/nav_link.ts b/src/core/public/chrome/nav_links/nav_link.ts index 65d59c59e9f75..87175ea465b7f 100644 --- a/src/core/public/chrome/nav_links/nav_link.ts +++ b/src/core/public/chrome/nav_links/nav_link.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { pick } from '@kbn/std'; import { AppCategory } from '../../'; /** @@ -81,11 +80,6 @@ export interface ChromeNavLink { readonly hidden?: boolean; } -/** @public */ -export type ChromeNavLinkUpdateableFields = Partial< - Pick ->; - export class NavLinkWrapper { public readonly id: string; public readonly properties: Readonly; @@ -98,10 +92,4 @@ export class NavLinkWrapper { this.id = properties.id; this.properties = Object.freeze(properties); } - - public update(newProps: ChromeNavLinkUpdateableFields) { - // Enforce limited properties at runtime for JS code - newProps = pick(newProps, ['disabled', 'hidden', 'url', 'href']); - return new NavLinkWrapper({ ...this.properties, ...newProps }); - } } diff --git a/src/core/public/chrome/nav_links/nav_links_service.test.ts b/src/core/public/chrome/nav_links/nav_links_service.test.ts index 74128aa798e7c..afb902fd6bd83 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.test.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.test.ts @@ -73,13 +73,10 @@ describe('NavLinksService', () => { const navLinkIds$ = start.getNavLinks$().pipe(map((links) => links.map((l) => l.id))); const emittedLinks: string[][] = []; navLinkIds$.subscribe((r) => emittedLinks.push(r)); - start.update('app1', { href: '/foo' }); + start.showOnly('app1'); service.stop(); - expect(emittedLinks).toEqual([ - ['app2', 'app1'], - ['app2', 'app1'], - ]); + expect(emittedLinks).toEqual([['app2', 'app1'], ['app1']]); }); it('completes when service is stopped', async () => { @@ -170,45 +167,6 @@ describe('NavLinksService', () => { }); }); - describe('#update()', () => { - it('updates the navlinks and returns the updated link', async () => { - expect(start.update('app2', { hidden: true })).toEqual( - expect.objectContaining({ - hidden: true, - id: 'app2', - order: -10, - title: 'App 2', - euiIconType: 'canvasApp', - }) - ); - const hiddenLinkIds = await start - .getNavLinks$() - .pipe( - take(1), - map((links) => links.filter((l) => l.hidden).map((l) => l.id)) - ) - .toPromise(); - expect(hiddenLinkIds).toEqual(['app2']); - }); - - it('returns undefined if link does not exist', () => { - expect(start.update('fake', { hidden: true })).toBeUndefined(); - }); - - it('keeps the updated link when availableApps are re-emitted', async () => { - start.update('app2', { hidden: true }); - mockAppService.applications$.next(mockAppService.applications$.value); - const hiddenLinkIds = await start - .getNavLinks$() - .pipe( - take(1), - map((links) => links.filter((l) => l.hidden).map((l) => l.id)) - ) - .toPromise(); - expect(hiddenLinkIds).toEqual(['app2']); - }); - }); - describe('#enableForcedAppSwitcherNavigation()', () => { it('flips #getForceAppSwitcherNavigation$()', async () => { await expect(start.getForceAppSwitcherNavigation$().pipe(take(1)).toPromise()).resolves.toBe( diff --git a/src/core/public/chrome/nav_links/nav_links_service.ts b/src/core/public/chrome/nav_links/nav_links_service.ts index 7a216d584044c..d41d8ae964d62 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.ts @@ -12,7 +12,7 @@ import { map, takeUntil } from 'rxjs/operators'; import { InternalApplicationStart } from '../../application'; import { HttpStart } from '../../http'; -import { ChromeNavLink, ChromeNavLinkUpdateableFields, NavLinkWrapper } from './nav_link'; +import { ChromeNavLink, NavLinkWrapper } from './nav_link'; import { toNavLink } from './to_nav_link'; interface StartDeps { @@ -58,18 +58,6 @@ export interface ChromeNavLinks { */ showOnly(id: string): void; - /** - * Update the navlink for the given id with the updated attributes. - * Returns the updated navlink or `undefined` if it does not exist. - * - * @deprecated Uses the {@link AppBase.updater$} property when registering - * your application with {@link ApplicationSetup.register} instead. - * - * @param id - * @param values - */ - update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined; - /** * Enable forced navigation mode, which will trigger a page refresh * when a nav link is clicked and only the hash is updated. @@ -109,6 +97,7 @@ export class NavLinksService { // now that availableApps$ is an observable, we need to keep record of all // manual link modifications to be able to re-apply then after every // availableApps$ changes. + // Only in use by `showOnly` API, can be removed once dashboard_mode is removed in 8.0 const linkUpdaters$ = new BehaviorSubject([]); const navLinks$ = new BehaviorSubject>(new Map()); @@ -153,25 +142,6 @@ export class NavLinksService { linkUpdaters$.next([...linkUpdaters$.value, updater]); }, - update(id: string, values: ChromeNavLinkUpdateableFields) { - if (!this.has(id)) { - return; - } - - const updater: LinksUpdater = (navLinks) => - new Map( - [...navLinks.entries()].map(([linkId, link]) => { - return [linkId, link.id === id ? link.update(values) : link] as [ - string, - NavLinkWrapper - ]; - }) - ); - - linkUpdaters$.next([...linkUpdaters$.value, updater]); - return this.get(id); - }, - enableForcedAppSwitcherNavigation() { forceAppSwitcherNavigation$.next(true); }, diff --git a/src/core/public/chrome/nav_links/to_nav_link.test.ts b/src/core/public/chrome/nav_links/to_nav_link.test.ts index 41c4ff178d737..db783d0028f07 100644 --- a/src/core/public/chrome/nav_links/to_nav_link.test.ts +++ b/src/core/public/chrome/nav_links/to_nav_link.test.ts @@ -17,10 +17,8 @@ const app = (props: Partial = {}): PublicAppInfo => ({ status: AppStatus.accessible, navLinkStatus: AppNavLinkStatus.default, appRoute: `/app/some-id`, - meta: { - keywords: [], - searchDeepLinks: [], - }, + keywords: [], + deepLinks: [], ...props, }); diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index b2eec43cc5ad7..36d613ec82f9e 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -131,6 +131,7 @@ export class DocLinksService { introduction: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/index-patterns.html`, fieldFormattersNumber: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/numeral.html`, fieldFormattersString: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/field-formatters-string.html`, + runtimeFields: `${KIBANA_DOCS}managing-index-patterns.html#runtime-fields`, }, addData: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/connect-to-elasticsearch.html`, kibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/index.html`, @@ -512,6 +513,7 @@ export interface DocLinksStart { readonly introduction: string; readonly fieldFormattersNumber: string; readonly fieldFormattersString: string; + readonly runtimeFields: string; }; readonly addData: string; readonly kibana: string; diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 60d40aaf81036..24b48683cdd93 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -41,7 +41,6 @@ import { ChromeNavControls, ChromeNavLink, ChromeNavLinks, - ChromeNavLinkUpdateableFields, ChromeDocTitle, ChromeStart, ChromeRecentlyAccessed, @@ -90,13 +89,11 @@ export type { AppLeaveAction, AppLeaveDefaultAction, AppLeaveConfirmAction, - AppMeta, AppUpdatableFields, AppUpdater, - AppSearchDeepLink, + AppDeepLink, PublicAppInfo, - PublicAppMetaInfo, - PublicAppSearchDeepLinkInfo, + PublicAppDeepLinkInfo, NavigateToAppOptions, } from './application'; @@ -298,7 +295,6 @@ export type { ChromeNavControls, ChromeNavLink, ChromeNavLinks, - ChromeNavLinkUpdateableFields, ChromeDocTitle, ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 449afa8869f6f..667863d29623e 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -58,12 +58,13 @@ export interface App { capabilities?: Partial; category?: AppCategory; chromeless?: boolean; + deepLinks?: AppDeepLink[]; defaultPath?: string; euiIconType?: string; exactRoute?: boolean; icon?: string; id: string; - meta?: AppMeta; + keywords?: string[]; mount: AppMount; navLinkStatus?: AppNavLinkStatus; order?: number; @@ -85,6 +86,20 @@ export interface AppCategory { order?: number; } +// @public +export type AppDeepLink = { + id: string; + title: string; + keywords?: string[]; + navLinkStatus?: AppNavLinkStatus; +} & ({ + path: string; + deepLinks?: AppDeepLink[]; +} | { + path?: string; + deepLinks: AppDeepLink[]; +}); + // @public export type AppLeaveAction = AppLeaveDefaultAction | AppLeaveConfirmAction; @@ -142,12 +157,6 @@ export interface ApplicationStart { navigateToUrl(url: string): Promise; } -// @public -export interface AppMeta { - keywords?: string[]; - searchDeepLinks?: AppSearchDeepLink[]; -} - // @public export type AppMount = (params: AppMountParameters) => AppUnmount | Promise; @@ -170,20 +179,6 @@ export enum AppNavLinkStatus { visible = 1 } -// @public -export type AppSearchDeepLink = { - id: string; - title: string; -} & ({ - path: string; - searchDeepLinks?: AppSearchDeepLink[]; - keywords?: string[]; -} | { - path?: string; - searchDeepLinks: AppSearchDeepLink[]; - keywords?: string[]; -}); - // @public export enum AppStatus { accessible = 0, @@ -194,7 +189,7 @@ export enum AppStatus { export type AppUnmount = () => void; // @public -export type AppUpdatableFields = Pick; +export type AppUpdatableFields = Pick; // @public export type AppUpdater = (app: App) => Partial | undefined; @@ -332,15 +327,8 @@ export interface ChromeNavLinks { getNavLinks$(): Observable>>; has(id: string): boolean; showOnly(id: string): void; - // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "AppBase" - // - // @deprecated - update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined; } -// @public (undocumented) -export type ChromeNavLinkUpdateableFields = Partial>; - // @public export interface ChromeRecentlyAccessed { // Warning: (ae-unresolved-link) The @link reference could not be resolved: No member was found with name "basePath" @@ -596,6 +584,7 @@ export interface DocLinksStart { readonly introduction: string; readonly fieldFormattersNumber: string; readonly fieldFormattersString: string; + readonly runtimeFields: string; }; readonly addData: string; readonly kibana: string; @@ -1078,23 +1067,19 @@ export interface PluginInitializerContext export type PluginOpaqueId = symbol; // @public -export type PublicAppInfo = Omit & { - status: AppStatus; - navLinkStatus: AppNavLinkStatus; - appRoute: string; - meta: PublicAppMetaInfo; -}; - -// @public -export type PublicAppMetaInfo = Omit & { +export type PublicAppDeepLinkInfo = Omit & { + deepLinks: PublicAppDeepLinkInfo[]; keywords: string[]; - searchDeepLinks: PublicAppSearchDeepLinkInfo[]; + navLinkStatus: AppNavLinkStatus; }; // @public -export type PublicAppSearchDeepLinkInfo = Omit & { - searchDeepLinks: PublicAppSearchDeepLinkInfo[]; +export type PublicAppInfo = Omit & { + status: AppStatus; + navLinkStatus: AppNavLinkStatus; + appRoute: string; keywords: string[]; + deepLinks: PublicAppDeepLinkInfo[]; }; // @public diff --git a/src/core/public/rendering/_base.scss b/src/core/public/rendering/_base.scss index 936b41e7682bb..3a748f3ceb6fd 100644 --- a/src/core/public/rendering/_base.scss +++ b/src/core/public/rendering/_base.scss @@ -49,6 +49,13 @@ top: $headerHeight; height: calc(100% - #{$headerHeight}); } + + @include euiBreakpoint('m', 'l', 'xl') { + .euiPageSideBar--sticky { + max-height: calc(100vh - #{$headerHeight}); + top: #{$headerHeight}; + } + } } .kbnBody { diff --git a/src/core/server/config/test_utils.ts b/src/core/server/config/test_utils.ts index 8e20e87e6f7d8..ab06ff50012b7 100644 --- a/src/core/server/config/test_utils.ts +++ b/src/core/server/config/test_utils.ts @@ -16,7 +16,7 @@ function collectDeprecations( ) { const deprecations = provider(configDeprecationFactory); const deprecationMessages: string[] = []; - const migrated = applyDeprecations( + const { config: migrated } = applyDeprecations( settings, deprecations.map((deprecation) => ({ deprecation, diff --git a/src/core/server/core_usage_data/core_usage_data_service.mock.ts b/src/core/server/core_usage_data/core_usage_data_service.mock.ts index e09f595747c30..5fa67fecb2a8a 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.mock.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.mock.ts @@ -116,6 +116,10 @@ const createStartContractMock = () => { maxImportExportSize: 10000, maxImportPayloadBytes: 26214400, }, + deprecatedKeys: { + set: ['path.to.a.prop'], + unset: [], + }, }, environment: { memory: { diff --git a/src/core/server/core_usage_data/core_usage_data_service.test.ts b/src/core/server/core_usage_data/core_usage_data_service.test.ts index dc74b65c8dcfc..95dd392016c17 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.test.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.test.ts @@ -91,7 +91,8 @@ describe('CoreUsageDataService', () => { const savedObjectsStartPromise = Promise.resolve( savedObjectsServiceMock.createStartContract() ); - service.setup({ http, metrics, savedObjectsStartPromise }); + const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$(); + service.setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ }); const savedObjects = await savedObjectsStartPromise; expect(savedObjects.createInternalRepository).toHaveBeenCalledTimes(1); @@ -105,7 +106,13 @@ describe('CoreUsageDataService', () => { const savedObjectsStartPromise = Promise.resolve( savedObjectsServiceMock.createStartContract() ); - const coreUsageData = service.setup({ http, metrics, savedObjectsStartPromise }); + const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$(); + const coreUsageData = service.setup({ + http, + metrics, + savedObjectsStartPromise, + changedDeprecatedConfigPath$, + }); const typeRegistry = typeRegistryMock.create(); coreUsageData.registerType(typeRegistry); @@ -126,7 +133,13 @@ describe('CoreUsageDataService', () => { const savedObjectsStartPromise = Promise.resolve( savedObjectsServiceMock.createStartContract() ); - const coreUsageData = service.setup({ http, metrics, savedObjectsStartPromise }); + const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$(); + const coreUsageData = service.setup({ + http, + metrics, + savedObjectsStartPromise, + changedDeprecatedConfigPath$, + }); const usageStatsClient = coreUsageData.getClient(); expect(usageStatsClient).toBeInstanceOf(CoreUsageStatsClient); @@ -142,7 +155,11 @@ describe('CoreUsageDataService', () => { const savedObjectsStartPromise = Promise.resolve( savedObjectsServiceMock.createStartContract() ); - service.setup({ http, metrics, savedObjectsStartPromise }); + const changedDeprecatedConfigPath$ = new BehaviorSubject({ + set: ['new.path'], + unset: ['deprecated.path'], + }); + service.setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ }); const elasticsearch = elasticsearchServiceMock.createStart(); elasticsearch.client.asInternalUser.cat.indices.mockResolvedValueOnce({ body: [ @@ -180,6 +197,14 @@ describe('CoreUsageDataService', () => { expect(getCoreUsageData()).resolves.toMatchInlineSnapshot(` Object { "config": Object { + "deprecatedKeys": Object { + "set": Array [ + "new.path", + ], + "unset": Array [ + "deprecated.path", + ], + }, "elasticsearch": Object { "apiVersion": "master", "customHeadersConfigured": false, @@ -381,12 +406,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.enabled": "[redacted]", - "pluginAB.enabled": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.enabled': '[redacted]', + 'pluginAB.enabled': '[redacted]', + }); }); it('returns an object of plugin config usage', async () => { @@ -418,23 +441,21 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "elasticsearch.password": "[redacted]", - "elasticsearch.username": "[redacted]", - "logging.json": false, - "pluginA.arrayOfNumbers": "[redacted]", - "pluginA.enabled": true, - "pluginA.objectConfig.debug": true, - "pluginA.objectConfig.username": "[redacted]", - "pluginAB.enabled": false, - "pluginB.arrayOfObjects": "[redacted]", - "plugins.paths": "[redacted]", - "server.basePath": "/zvt", - "server.port": 5603, - "server.rewriteBasePath": true, - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'elasticsearch.password': '[redacted]', + 'elasticsearch.username': '[redacted]', + 'logging.json': false, + 'pluginA.arrayOfNumbers': '[redacted]', + 'pluginA.enabled': true, + 'pluginA.objectConfig.debug': true, + 'pluginA.objectConfig.username': '[redacted]', + 'pluginAB.enabled': false, + 'pluginB.arrayOfObjects': '[redacted]', + 'plugins.paths': '[redacted]', + 'server.basePath': '/zvt', + 'server.port': 5603, + 'server.rewriteBasePath': true, + }); }); describe('config explicitly exposed to usage', () => { @@ -457,12 +478,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.objectConfig.debug": "[redacted]", - "server.basePath": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.objectConfig.debug': '[redacted]', + 'server.basePath': '[redacted]', + }); }); it('returns config value on safe complete match', async () => { @@ -478,11 +497,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "server.basePath": "/zvt", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'server.basePath': '/zvt', + }); }); it('returns [redacted] on unsafe parent match', async () => { @@ -501,12 +518,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.objectConfig.debug": "[redacted]", - "pluginA.objectConfig.username": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.objectConfig.debug': '[redacted]', + 'pluginA.objectConfig.username': '[redacted]', + }); }); it('returns config value on safe parent match', async () => { @@ -525,12 +540,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.objectConfig.debug": true, - "pluginA.objectConfig.username": "some_user", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.objectConfig.debug': true, + 'pluginA.objectConfig.username': 'some_user', + }); }); it('returns [redacted] on explicitly marked as safe array of objects', async () => { @@ -546,11 +559,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginB.arrayOfObjects": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginB.arrayOfObjects': '[redacted]', + }); }); it('returns values on explicitly marked as safe array of numbers', async () => { @@ -566,15 +577,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.arrayOfNumbers": Array [ - 1, - 2, - 3, - ], - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.arrayOfNumbers': [1, 2, 3], + }); }); it('returns values on explicitly marked as safe array of strings', async () => { @@ -590,15 +595,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "plugins.paths": Array [ - "pluginA", - "pluginAB", - "pluginB", - ], - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'plugins.paths': ['pluginA', 'pluginAB', 'pluginB'], + }); }); }); @@ -619,12 +618,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.objectConfig.debug": "[redacted]", - "pluginA.objectConfig.username": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.objectConfig.debug': '[redacted]', + 'pluginA.objectConfig.username': '[redacted]', + }); }); it('returns config value on safe parent match', async () => { @@ -640,13 +637,11 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "elasticsearch.password": "[redacted]", - "elasticsearch.username": "[redacted]", - "pluginA.objectConfig.username": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'elasticsearch.password': '[redacted]', + 'elasticsearch.username': '[redacted]', + 'pluginA.objectConfig.username': '[redacted]', + }); }); it('returns [redacted] on implicit array of objects', async () => { @@ -658,11 +653,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginB.arrayOfObjects": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginB.arrayOfObjects': '[redacted]', + }); }); it('returns values on implicit array of numbers', async () => { @@ -674,16 +667,11 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "pluginA.arrayOfNumbers": Array [ - 1, - 2, - 3, - ], - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'pluginA.arrayOfNumbers': [1, 2, 3], + }); }); + it('returns [redacted] on implicit array of strings', async () => { configService.getUsedPaths.mockResolvedValue(['plugins.paths']); @@ -693,11 +681,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "plugins.paths": "[redacted]", - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'plugins.paths': '[redacted]', + }); }); it('returns config value for numbers', async () => { @@ -709,11 +695,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "server.port": 5603, - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'server.port': 5603, + }); }); it('returns config value for booleans', async () => { @@ -728,12 +712,10 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "logging.json": false, - "pluginA.objectConfig.debug": true, - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'logging.json': false, + 'pluginA.objectConfig.debug': true, + }); }); it('ignores exposed to usage configs but not used', async () => { @@ -749,11 +731,9 @@ describe('CoreUsageDataService', () => { elasticsearch, }); - await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(` - Object { - "logging.json": false, - } - `); + await expect(getConfigsUsageData()).resolves.toEqual({ + 'logging.json': false, + }); }); }); }); @@ -779,7 +759,8 @@ describe('CoreUsageDataService', () => { savedObjectsServiceMock.createStartContract() ); - service.setup({ http, metrics, savedObjectsStartPromise }); + const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$(); + service.setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ }); // Use the stopTimer$ to delay calling stop() until the third frame const stopTimer$ = cold('---a|'); diff --git a/src/core/server/core_usage_data/core_usage_data_service.ts b/src/core/server/core_usage_data/core_usage_data_service.ts index 85abdca9ea5dc..dc24f889cd8dd 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import { Subject } from 'rxjs'; +import { Subject, Observable } from 'rxjs'; import { takeUntil, first } from 'rxjs/operators'; import { get } from 'lodash'; -import { hasConfigPathIntersection } from '@kbn/config'; +import { hasConfigPathIntersection, ChangedDeprecatedPaths } from '@kbn/config'; import { CoreService } from 'src/core/types'; import { Logger, SavedObjectsServiceStart, SavedObjectTypeRegistry } from 'src/core/server'; @@ -39,6 +39,7 @@ export interface SetupDeps { http: InternalHttpServiceSetup; metrics: MetricsServiceSetup; savedObjectsStartPromise: Promise; + changedDeprecatedConfigPath$: Observable; } export interface StartDeps { @@ -89,6 +90,7 @@ export class CoreUsageDataService implements CoreService); } - setup({ http, metrics, savedObjectsStartPromise }: SetupDeps) { + setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ }: SetupDeps) { metrics .getOpsMetrics$() .pipe(takeUntil(this.stop$)) @@ -417,6 +421,10 @@ export class CoreUsageDataService implements CoreService (this.deprecatedConfigPaths = deprecatedConfigPaths)); + const internalRepositoryPromise = savedObjectsStartPromise.then((savedObjects) => savedObjects.createInternalRepository([CORE_USAGE_STATS_TYPE]) ); diff --git a/src/core/server/core_usage_data/types.ts b/src/core/server/core_usage_data/types.ts index 1d5ef6d893f53..affd3d5c66ab7 100644 --- a/src/core/server/core_usage_data/types.ts +++ b/src/core/server/core_usage_data/types.ts @@ -254,6 +254,11 @@ export interface CoreConfigUsageData { // uiSettings: { // overridesCount: number; // }; + + deprecatedKeys: { + set: string[]; + unset: string[]; + }; } /** @internal */ diff --git a/src/core/server/http/http_server.mocks.ts b/src/core/server/http/http_server.mocks.ts index 52dab28accb33..9e2f0821a2219 100644 --- a/src/core/server/http/http_server.mocks.ts +++ b/src/core/server/http/http_server.mocks.ts @@ -27,7 +27,10 @@ import { OnPreResponseToolkit } from './lifecycle/on_pre_response'; import { OnPostAuthToolkit } from './lifecycle/on_post_auth'; import { OnPreRoutingToolkit } from './lifecycle/on_pre_routing'; -interface RequestFixtureOptions

{ +/** + * @internal + */ +export interface RequestFixtureOptions

{ auth?: { isAuthenticated: boolean }; headers?: Record; params?: Record; diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts index 71e5565ebcbef..79f5bd09889db 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts @@ -748,14 +748,8 @@ describe('DocumentMigrator', () => { migrator.migrate(_.cloneDeep(failedDoc)); expect('Did not throw').toEqual('But it should have!'); } catch (error) { - expect(error.message).toMatchInlineSnapshot(` - "Failed to transform document smelly. Transform: dog:1.2.3 - Doc: {\\"id\\":\\"smelly\\",\\"type\\":\\"dog\\",\\"attributes\\":{},\\"migrationVersion\\":{}}" - `); + expect(error.message).toBe('Dang diggity!'); expect(error).toBeInstanceOf(TransformSavedObjectDocumentError); - expect(loggingSystemMock.collect(mockLoggerFactory).error[0][0]).toMatchInlineSnapshot( - `[Error: Dang diggity!]` - ); } }); diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts index c96de6ebbfcdd..a32cc999c5559 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts @@ -679,19 +679,8 @@ function wrapWithTry( return { transformedDoc: result, additionalDocs: [] }; } catch (error) { - const failedTransform = `${type.name}:${version}`; - const failedDoc = JSON.stringify(doc); log.error(error); - // To make debugging failed migrations easier, we add items needed to convert the - // saved object id to the full raw id (the id only contains the uuid part) and the full error itself - throw new TransformSavedObjectDocumentError( - doc.id, - doc.type, - doc.namespace, - failedTransform, - failedDoc, - error - ); + throw new TransformSavedObjectDocumentError(error); } }; } diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts index 1d43e2f54a726..7a6f72a881cd6 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts @@ -233,34 +233,7 @@ describe('migrateRawDocsSafely', () => { test('instance of Either.left containing transform errors when the transform function throws a TransformSavedObjectDocument error', async () => { const transform = jest.fn((doc: any) => { - throw new TransformSavedObjectDocumentError( - `${doc.id}`, - `${doc.type}`, - `${doc.namespace}`, - `${doc.type}1.2.3`, - JSON.stringify(doc), - new Error('error during transform') - ); - }); - const task = migrateRawDocsSafely( - new SavedObjectsSerializer(new SavedObjectTypeRegistry()), - transform, - [{ _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }] // this is the raw doc - ); - const result = (await task()) as Either.Left; - expect(transform).toHaveBeenCalledTimes(1); - expect(result._tag).toEqual('Left'); - expect(result.left.corruptDocumentIds.length).toEqual(0); - expect(result.left.transformErrors.length).toEqual(1); - expect(result.left.transformErrors[0].err.message).toMatchInlineSnapshot(` - "Failed to transform document b. Transform: a1.2.3 - Doc: {\\"type\\":\\"a\\",\\"id\\":\\"b\\",\\"attributes\\":{\\"name\\":\\"AAA\\"},\\"references\\":[],\\"migrationVersion\\":{}}" - `); - }); - - test("instance of Either.left containing errors when the transform function throws an error that isn't a TransformSavedObjectDocument error", async () => { - const transform = jest.fn((doc: any) => { - throw new Error('error during transform'); + throw new TransformSavedObjectDocumentError(new Error('error during transform')); }); const task = migrateRawDocsSafely( new SavedObjectsSerializer(new SavedObjectTypeRegistry()), diff --git a/src/core/server/saved_objects/migrations/core/transform_saved_object_document_error.test.ts b/src/core/server/saved_objects/migrations/core/transform_saved_object_document_error.test.ts index 80c670edd39ba..1efb1bd726216 100644 --- a/src/core/server/saved_objects/migrations/core/transform_saved_object_document_error.test.ts +++ b/src/core/server/saved_objects/migrations/core/transform_saved_object_document_error.test.ts @@ -10,51 +10,10 @@ import { TransformSavedObjectDocumentError } from './transform_saved_object_docu describe('TransformSavedObjectDocumentError', () => { it('is a special error', () => { const originalError = new Error('Dang diggity!'); - const err = new TransformSavedObjectDocumentError( - 'id', - 'type', - 'namespace', - 'failedTransform', - 'failedDoc', - originalError - ); + const err = new TransformSavedObjectDocumentError(originalError); expect(err).toBeInstanceOf(TransformSavedObjectDocumentError); - expect(err.id).toEqual('id'); - expect(err.namespace).toEqual('namespace'); expect(err.stack).not.toBeNull(); - }); - it('constructs an special error message', () => { - const originalError = new Error('Dang diggity!'); - const err = new TransformSavedObjectDocumentError( - 'id', - 'type', - 'namespace', - 'failedTransform', - 'failedDoc', - originalError - ); - expect(err.message).toMatchInlineSnapshot( - ` - "Failed to transform document id. Transform: failedTransform - Doc: failedDoc" - ` - ); - }); - it('handles undefined namespace', () => { - const originalError = new Error('Dang diggity!'); - const err = new TransformSavedObjectDocumentError( - 'id', - 'type', - undefined, - 'failedTransform', - 'failedDoc', - originalError - ); - expect(err.message).toMatchInlineSnapshot( - ` - "Failed to transform document id. Transform: failedTransform - Doc: failedDoc" - ` - ); + expect(err.originalError).toBe(originalError); + expect(err.message).toMatchInlineSnapshot(`"Dang diggity!"`); }); }); diff --git a/src/core/server/saved_objects/migrations/core/transform_saved_object_document_error.ts b/src/core/server/saved_objects/migrations/core/transform_saved_object_document_error.ts index 6a6f87ea1eeb2..2dc553545a08d 100644 --- a/src/core/server/saved_objects/migrations/core/transform_saved_object_document_error.ts +++ b/src/core/server/saved_objects/migrations/core/transform_saved_object_document_error.ts @@ -9,24 +9,10 @@ /** * Error thrown when saved object migrations encounter a transformation error. * Transformation errors happen when a transform function throws an error for an unsanitized saved object - * The id (doc.id) reported in this error class is just the uuid part and doesn't tell users what the full elasticsearch id is. - * in order to convert the id to the serialized version further upstream using serializer.generateRawId, we need to provide the following items: - * - namespace: doc.namespace, - * - type: doc.type, - * - id: doc.id, - * The new error class helps with v2 migrations. - * For backward compatibility with v1 migrations, the error message is the same as what was previously thrown as a plain error */ export class TransformSavedObjectDocumentError extends Error { - constructor( - public readonly id: string, - public readonly type: string, - public readonly namespace: string | undefined, - public readonly failedTransform: string, // created by document_migrator wrapWithTry as `${type.name}:${version}`; - public readonly failedDoc: string, - public readonly originalError: Error - ) { - super(`Failed to transform document ${id}. Transform: ${failedTransform}\nDoc: ${failedDoc}`); + constructor(public readonly originalError: Error) { + super(`${originalError.message}`); } } diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/archives/7_13_corrupt_and_transform_failures_docs.zip b/src/core/server/saved_objects/migrationsv2/integration_tests/archives/7_13_corrupt_and_transform_failures_docs.zip new file mode 100644 index 0000000000000..30ee6ee23dbf3 Binary files /dev/null and b/src/core/server/saved_objects/migrationsv2/integration_tests/archives/7_13_corrupt_and_transform_failures_docs.zip differ diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/cleanup.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/cleanup.test.ts index 1e494d4b55861..91d86353b48fa 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/cleanup.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/cleanup.test.ts @@ -53,7 +53,8 @@ function createRoot() { ); } -describe('migration v2', () => { +// FAILING: https://github.com/elastic/kibana/issues/98352 +describe.skip('migration v2', () => { let esServer: kbnTestServer.TestElasticsearchUtils; let root: Root; diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts index e48f1e65c120f..9a09fb47d0609 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts @@ -55,7 +55,7 @@ describe('migration v2 with corrupt saved object documents', () => { // }, // original corrupt SO example: // { - // id: 'bar:123' + // id: 'bar:123' // '123' etc // type: 'foo', // foo: {}, // migrationVersion: { @@ -107,16 +107,39 @@ describe('migration v2 with corrupt saved object documents', () => { try { await root.start(); } catch (err) { - const corruptFooSOs = /foo:/g; - const corruptBarSOs = /bar:/g; - const corruptBazSOs = /baz:/g; + const errorMessage = err.message; expect( - [ - ...err.message.matchAll(corruptFooSOs), - ...err.message.matchAll(corruptBarSOs), - ...err.message.matchAll(corruptBazSOs), - ].length - ).toEqual(16); + errorMessage.startsWith( + 'Unable to complete saved object migrations for the [.kibana] index: Migrations failed. Reason: Corrupt saved object documents: ' + ) + ).toBeTruthy(); + expect( + errorMessage.endsWith(' To allow migrations to proceed, please delete these documents.') + ).toBeTruthy(); + const expectedCorruptDocIds = [ + '"foo:my_name"', + '"123"', + '"456"', + '"789"', + '"foo:other_name"', + '"bar:123"', + '"baz:123"', + '"bar:345"', + '"bar:890"', + '"baz:456"', + '"baz:789"', + '"bar:other_name"', + '"baz:other_name"', + '"bar:my_name"', + '"baz:my_name"', + '"foo:123"', + '"foo:456"', + '"foo:789"', + '"foo:other"', + ]; + for (const corruptDocId of expectedCorruptDocIds) { + expect(errorMessage.includes(corruptDocId)).toBeTruthy(); + } } }); }); diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/migration.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/migration.test.ts index 53ce21192142d..5e4b8feefbd95 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/migration.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/migration.test.ts @@ -189,7 +189,8 @@ describe('migration v2', () => { }); }); - describe('migrating from the same Kibana version', () => { + // FLAKY: https://github.com/elastic/kibana/issues/91107 + describe.skip('migrating from the same Kibana version', () => { const migratedIndex = `.kibana_${kibanaVersion}_001`; beforeAll(async () => { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/migration_7_13_0_transform_failures.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/migration_7_13_0_transform_failures.test.ts new file mode 100644 index 0000000000000..c014f7de395e0 --- /dev/null +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/migration_7_13_0_transform_failures.test.ts @@ -0,0 +1,157 @@ +/* + * Copyright 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 Path from 'path'; +import Fs from 'fs'; +import Util from 'util'; +import * as kbnTestServer from '../../../../test_helpers/kbn_server'; +import { Root } from '../../../root'; + +const logFilePath = Path.join(__dirname, '7_13_corrupt_transform_failures_test.log'); + +const asyncUnlink = Util.promisify(Fs.unlink); +async function removeLogFile() { + // ignore errors if it doesn't exist + await asyncUnlink(logFilePath).catch(() => void 0); +} + +describe('migration v2', () => { + let esServer: kbnTestServer.TestElasticsearchUtils; + let root: Root; + + beforeAll(async () => { + await removeLogFile(); + }); + + afterAll(async () => { + if (root) { + await root.shutdown(); + } + if (esServer) { + await esServer.stop(); + } + + await new Promise((resolve) => setTimeout(resolve, 10000)); + }); + + it('migrates the documents to the highest version', async () => { + const { startES } = kbnTestServer.createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + // example of original 'foo' SO with corrupt id: + // _id: one + // { + // foo: { + // name: 'one', + // }, + // type: 'foo', + // references: [], + // migrationVersion: { + // foo: '7.13.0', + // }, + // "coreMigrationVersion": "7.13.0", + // "updated_at": "2021-05-16T18:16:45.450Z" + // }, + + // SO that will fail transformation: + // { + // type: 'space', + // space: {}, + // }, + // + // + dataArchive: Path.join( + __dirname, + 'archives', + '7_13_corrupt_and_transform_failures_docs.zip' + ), + }, + }, + }); + + root = createRoot(); + + esServer = await startES(); + const coreSetup = await root.setup(); + + coreSetup.savedObjects.registerType({ + name: 'foo', + hidden: false, + mappings: { + properties: {}, + }, + namespaceType: 'agnostic', + migrations: { + '7.14.0': (doc) => doc, + }, + }); + try { + await root.start(); + } catch (err) { + const errorMessage = err.message; + expect( + errorMessage.startsWith( + 'Unable to complete saved object migrations for the [.kibana] index: Migrations failed. Reason: Corrupt saved object documents: ' + ) + ).toBeTruthy(); + expect( + errorMessage.endsWith(' To allow migrations to proceed, please delete these documents.') + ).toBeTruthy(); + + const expectedCorruptDocIds = [ + 'P2SQfHkBs3dBRGh--No5', + 'QGSZfHkBs3dBRGh-ANoD', + 'QWSZfHkBs3dBRGh-hNob', + 'QmSZfHkBs3dBRGh-w9qH', + 'one', + 'two', + 'Q2SZfHkBs3dBRGh-9dp2', + ]; + for (const corruptDocId of expectedCorruptDocIds) { + expect(errorMessage.includes(corruptDocId)).toBeTruthy(); + } + const expectedTransformErrorMessage = + 'Transformation errors: space:default: Document "default" has property "space" which belongs to a more recent version of Kibana [6.6.0]. The last known version is [undefined]'; + expect(errorMessage.includes(expectedTransformErrorMessage)).toBeTruthy(); + } + }); +}); + +function createRoot() { + return kbnTestServer.createRootWithCorePlugins( + { + migrations: { + skip: false, + enableV2: true, + batchSize: 5, + }, + logging: { + appenders: { + file: { + type: 'file', + fileName: logFilePath, + layout: { + type: 'json', + }, + }, + }, + loggers: [ + { + name: 'root', + appenders: ['file'], + }, + ], + }, + }, + { + oss: true, + } + ); +} diff --git a/src/core/server/saved_objects/migrationsv2/model.test.ts b/src/core/server/saved_objects/migrationsv2/model.test.ts index 7a47e58f1947c..186cb24b4a34a 100644 --- a/src/core/server/saved_objects/migrationsv2/model.test.ts +++ b/src/core/server/saved_objects/migrationsv2/model.test.ts @@ -1158,14 +1158,7 @@ describe('migrations v2 model', () => { it('OUTDATED_DOCUMENTS_SEARCH_READ -> FATAL if no outdated documents to transform and we have failed document migrations', () => { const corruptDocumentIdsCarriedOver = ['a:somethingelse']; const originalTransformError = new Error('something went wrong'); - const transFormErr = new TransformSavedObjectDocumentError( - '123', - 'vis', - undefined, - 'randomvis: 7.12.0', - 'failedDoc', - originalTransformError - ); + const transFormErr = new TransformSavedObjectDocumentError(originalTransformError); const transformationErrors = [ { rawId: 'bob:tail', err: transFormErr }, ] as TransformErrorObjects[]; @@ -1184,7 +1177,7 @@ describe('migrations v2 model', () => { expect(newState.reason.includes('Migrations failed. Reason:')).toBe(true); expect(newState.reason.includes('Corrupt saved object documents: ')).toBe(true); expect(newState.reason.includes('Transformation errors: ')).toBe(true); - expect(newState.reason.includes('randomvis: 7.12.0')).toBe(true); + expect(newState.reason.includes('bob:tail')).toBe(true); expect(newState.logs).toStrictEqual([]); // No logs because no hits }); }); @@ -1229,14 +1222,7 @@ describe('migrations v2 model', () => { const outdatedDocuments = [{ _id: '1', _source: { type: 'vis' } }]; const corruptDocumentIds = ['a:somethingelse']; const originalTransformError = new Error('Dang diggity!'); - const transFormErr = new TransformSavedObjectDocumentError( - 'id', - 'type', - 'namespace', - 'failedTransform', - 'failedDoc', - originalTransformError - ); + const transFormErr = new TransformSavedObjectDocumentError(originalTransformError); const transformationErrors = [ { rawId: 'bob:tail', err: transFormErr }, ] as TransformErrorObjects[]; diff --git a/src/core/server/saved_objects/migrationsv2/model.ts b/src/core/server/saved_objects/migrationsv2/model.ts index f4185225ae073..252d7424c339c 100644 --- a/src/core/server/saved_objects/migrationsv2/model.ts +++ b/src/core/server/saved_objects/migrationsv2/model.ts @@ -109,7 +109,7 @@ function getAliases(indices: FetchIndexResponse) { function extractTransformFailuresReason( corruptDocumentIds: string[], transformErrors: TransformErrorObjects[] -): { corruptDocsReason: string; transformErrsReason: string } { +): string { const corruptDocumentIdReason = corruptDocumentIds.length > 0 ? ` Corrupt saved object documents: ${corruptDocumentIds.join(',')}` @@ -122,10 +122,7 @@ function extractTransformFailuresReason( .map((errObj) => `${errObj.rawId}: ${errObj.err.message}\n ${errObj.err.stack ?? ''}`) .join('/n') : ''; - return { - corruptDocsReason: corruptDocumentIdReason, - transformErrsReason: transformErrorsReason, - }; + return `Migrations failed. Reason:${corruptDocumentIdReason}${transformErrorsReason}. To allow migrations to proceed, please delete these documents.`; } const delayRetryState = ( @@ -538,14 +535,14 @@ export const model = (currentState: State, resW: ResponseType): } else { // we don't have any more outdated documents and need to either fail or move on to updating the target mappings. if (stateP.corruptDocumentIds.length > 0 || stateP.transformErrors.length > 0) { - const { corruptDocsReason, transformErrsReason } = extractTransformFailuresReason( + const transformFailureReason = extractTransformFailuresReason( stateP.corruptDocumentIds, stateP.transformErrors ); return { ...stateP, controlState: 'FATAL', - reason: `Migrations failed. Reason:${corruptDocsReason}${transformErrsReason}. To allow migrations to proceed, please delete these documents.`, + reason: transformFailureReason, }; } else { // we don't have any more outdated documents and we haven't encountered any document transformation issues. @@ -722,14 +719,14 @@ export const model = (currentState: State, resW: ResponseType): } else { // we don't have any more outdated documents and need to either fail or move on to updating the target mappings. if (stateP.corruptDocumentIds.length > 0 || stateP.transformErrors.length > 0) { - const { corruptDocsReason, transformErrsReason } = extractTransformFailuresReason( + const transformFailureReason = extractTransformFailuresReason( stateP.corruptDocumentIds, stateP.transformErrors ); return { ...stateP, controlState: 'FATAL', - reason: `Migrations failed. Reason:${corruptDocsReason}${transformErrsReason}. To allow migrations to proceed, please delete these documents.`, + reason: transformFailureReason, }; } else { // If there are no more results we have transformed all outdated diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index d9ad24a4a2c0c..7f108dbeb0086 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -399,6 +399,11 @@ export interface ContextSetup { // @internal export interface CoreConfigUsageData { + // (undocumented) + deprecatedKeys: { + set: string[]; + unset: string[]; + }; // (undocumented) elasticsearch: { sniffOnStart: boolean; diff --git a/src/core/server/server.ts b/src/core/server/server.ts index fcfca3a5e0e2f..4d99368f9bf70 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -153,6 +153,7 @@ export class Server { http: httpSetup, metrics: metricsSetup, savedObjectsStartPromise: this.savedObjectsStartPromise, + changedDeprecatedConfigPath$: this.configService.getDeprecatedConfigPath$(), }); const savedObjectsSetup = await this.savedObjects.setup({ @@ -265,6 +266,7 @@ export class Server { await this.http.start(); startTransaction?.end(); + return this.coreStart; } diff --git a/src/dev/build/tasks/clean_tasks.ts b/src/dev/build/tasks/clean_tasks.ts index 3051579d2e6f8..d4b4f98ed295b 100644 --- a/src/dev/build/tasks/clean_tasks.ts +++ b/src/dev/build/tasks/clean_tasks.ts @@ -62,6 +62,7 @@ export const CleanExtraFilesFromModules: Task = { // tests '**/test', '**/tests', + '**/jest.config.js', '**/__tests__', '**/*.test.js', '**/*.snap', diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index 3b2feeecabb7c..2f54bd1d818b5 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -276,7 +276,7 @@ kibana_vars=( xpack.reporting.roles.allow xpack.reporting.roles.enabled xpack.rollup.enabled - xpack.ruleRegistry.unsafe.write.enabled + xpack.ruleRegistry.write.enabled xpack.searchprofiler.enabled xpack.security.audit.enabled xpack.security.audit.appender.type diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts b/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts index c8eb16530507f..e22d8ecdd4fd8 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/kibana_yml.template.ts @@ -18,6 +18,7 @@ function generator({ imageFlavor }: TemplateContext) { # Default Kibana configuration for docker target server.host: "0.0.0.0" + server.shutdownTimeout: "5s" elasticsearch.hosts: [ "http://elasticsearch:9200" ] ${!imageFlavor ? 'monitoring.ui.container.elasticsearch.enabled: true' : ''} `); diff --git a/src/plugins/charts/public/static/utils/transform_click_event.ts b/src/plugins/charts/public/static/utils/transform_click_event.ts index 0c303b92bf1a1..844e2c3b301fa 100644 --- a/src/plugins/charts/public/static/utils/transform_click_event.ts +++ b/src/plugins/charts/public/static/utils/transform_click_event.ts @@ -152,9 +152,9 @@ const rowFindPredicate = ( ) => (row: Datatable['rows'][number]): boolean => (geometry === null || (xAccessor !== null && - getAccessorValue(row, xAccessor) === geometry.x && + getAccessorValue(row, xAccessor) === getAccessorValue(geometry.datum, xAccessor) && yAccessor !== null && - getAccessorValue(row, yAccessor) === geometry.y && + getAccessorValue(row, yAccessor) === getAccessorValue(geometry.datum, yAccessor) && (splitChartAccessor === undefined || (splitChartValue !== undefined && getAccessorValue(row, splitChartAccessor) === splitChartValue)))) && diff --git a/src/plugins/data/common/es_query/filters/meta_filter.ts b/src/plugins/data/common/es_query/filters/meta_filter.ts index c47dcb245cbf0..87455cf1cb763 100644 --- a/src/plugins/data/common/es_query/filters/meta_filter.ts +++ b/src/plugins/data/common/es_query/filters/meta_filter.ts @@ -31,6 +31,7 @@ export type FilterMeta = { controlledBy?: string; // index and type are optional only because when you create a new filter, there are no defaults index?: string; + isMultiIndex?: boolean; type?: string; key?: string; params?: any; diff --git a/src/plugins/data/public/actions/apply_filter_action.ts b/src/plugins/data/public/actions/apply_filter_action.ts index d4ac72294e257..43445d9448f2c 100644 --- a/src/plugins/data/public/actions/apply_filter_action.ts +++ b/src/plugins/data/public/actions/apply_filter_action.ts @@ -21,6 +21,9 @@ export interface ApplyGlobalFilterActionContext { // Need to make this unknown to prevent circular dependencies. // Apps using this property will need to cast to `IEmbeddable`. embeddable?: unknown; + // controlledBy is an optional key in filter.meta that identifies the owner of a filter + // Pass controlledBy to cleanup an existing filter(s) owned by embeddable prior to adding new filters + controlledBy?: string; } async function isCompatible(context: ApplyGlobalFilterActionContext) { @@ -42,7 +45,7 @@ export function createFilterAction( }); }, isCompatible, - execute: async ({ filters, timeFieldName }: ApplyGlobalFilterActionContext) => { + execute: async ({ filters, timeFieldName, controlledBy }: ApplyGlobalFilterActionContext) => { if (!filters) { throw new Error('Applying a filter requires a filter'); } @@ -85,6 +88,15 @@ export function createFilterAction( selectedFilters = await filterSelectionPromise; } + // remove existing filters for control prior to adding new filtes for control + if (controlledBy) { + filterManager.getFilters().forEach((filter) => { + if (filter.meta.controlledBy === controlledBy) { + filterManager.removeFilter(filter); + } + }); + } + if (timeFieldName) { const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter( timeFieldName, diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 8561d7bf8d6f5..57aa2298039da 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -492,6 +492,8 @@ export const APPLY_FILTER_TRIGGER = "FILTER_TRIGGER"; // // @public (undocumented) export interface ApplyGlobalFilterActionContext { + // (undocumented) + controlledBy?: string; // (undocumented) embeddable?: unknown; // (undocumented) @@ -763,6 +765,7 @@ export const esFilters: { disabled: boolean; controlledBy?: string | undefined; index?: string | undefined; + isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; @@ -2689,8 +2692,8 @@ export interface WaitUntilNextSessionCompletesOptions { // src/plugins/data/common/es_query/filters/exists_filter.ts:19:3 - (ae-forgotten-export) The symbol "ExistsFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/exists_filter.ts:20:3 - (ae-forgotten-export) The symbol "FilterExistsProperty" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/match_all_filter.ts:17:3 - (ae-forgotten-export) The symbol "MatchAllFilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/meta_filter.ts:42:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:44:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrase_filter.ts:22:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrases_filter.ts:20:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/ui/filter_bar/filter_item.tsx b/src/plugins/data/public/ui/filter_bar/filter_item.tsx index 5ad88e6fdf5be..9e5090f945182 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_item.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_item.tsx @@ -286,6 +286,11 @@ export function FilterItem(props: FilterItemProps) { message: '', status: FILTER_ITEM_OK, }; + + if (filter.meta?.isMultiIndex) { + return label; + } + if (indexPatternExists === false) { label.status = FILTER_ITEM_ERROR; label.title = props.intl.formatMessage({ diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index f52c622c48ed0..a651d7b3bf105 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -401,6 +401,7 @@ export class SearchService implements Plugin { savedObjectsClient, esClient: elasticsearch.client.asScoped(request), uiSettingsClient: uiSettings.asScopedToClient(savedObjectsClient), + request, }; return { search: < diff --git a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts index cec4b9a2dbf9f..ab6162f756ea8 100644 --- a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts +++ b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts @@ -38,11 +38,13 @@ import { export const enhancedEsSearchStrategyProvider = ( legacyConfig$: Observable, logger: Logger, - usage?: SearchUsage + usage?: SearchUsage, + useInternalUser: boolean = false ): ISearchStrategy => { async function cancelAsyncSearch(id: string, esClient: IScopedClusterClient) { try { - await esClient.asCurrentUser.asyncSearch.delete({ id }); + const client = useInternalUser ? esClient.asInternalUser : esClient.asCurrentUser; + await client.asyncSearch.delete({ id }); } catch (e) { throw getKbnServerError(e); } @@ -53,7 +55,7 @@ export const enhancedEsSearchStrategyProvider = ( options: IAsyncSearchOptions, { esClient, uiSettingsClient, searchSessionsClient }: SearchStrategyDependencies ) { - const client = esClient.asCurrentUser.asyncSearch; + const client = useInternalUser ? esClient.asInternalUser : esClient.asCurrentUser; const search = async () => { const params = id @@ -66,7 +68,9 @@ export const enhancedEsSearchStrategyProvider = ( )), ...request.params, }; - const promise = id ? client.get({ ...params, id }) : client.submit(params); + const promise = id + ? client.asyncSearch.get({ ...params, id }) + : client.asyncSearch.submit(params); const { body } = await shimAbortSignal(promise, options.abortSignal); const response = shimHitsTotal(body.response, options); @@ -96,6 +100,7 @@ export const enhancedEsSearchStrategyProvider = ( options: ISearchOptions, { esClient, uiSettingsClient }: SearchStrategyDependencies ): Promise { + const client = useInternalUser ? esClient.asInternalUser : esClient.asCurrentUser; const legacyConfig = await legacyConfig$.pipe(first()).toPromise(); const { body, index, ...params } = request.params!; const method = 'POST'; @@ -108,7 +113,7 @@ export const enhancedEsSearchStrategyProvider = ( }; try { - const promise = esClient.asCurrentUser.transport.request({ + const promise = client.transport.request({ method, path, body, @@ -169,7 +174,11 @@ export const enhancedEsSearchStrategyProvider = ( extend: async (id, keepAlive, options, { esClient }) => { logger.debug(`extend ${id} by ${keepAlive}`); try { - await esClient.asCurrentUser.asyncSearch.get({ id, body: { keep_alive: keepAlive } }); + const client = useInternalUser ? esClient.asInternalUser : esClient.asCurrentUser; + await client.asyncSearch.get({ + id, + body: { keep_alive: keepAlive }, + }); } catch (e) { throw getKbnServerError(e); } diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index 229c581bf09f1..6192045fa04c7 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -37,6 +37,7 @@ export interface SearchStrategyDependencies { esClient: IScopedClusterClient; uiSettingsClient: IUiSettingsClient; searchSessionsClient: IScopedSearchSessionsClient; + request: KibanaRequest; } export interface ISearchSetup { diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index ffdff2e33cf9c..b1c90667c2d71 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -37,8 +37,8 @@ import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource } from 'src/plugins/data/public'; import { IUiSettingsClient } from 'src/core/server'; import { IUiSettingsClient as IUiSettingsClient_3 } from 'kibana/server'; -import { KibanaRequest } from 'kibana/server'; -import { KibanaRequest as KibanaRequest_2 } from 'src/core/server'; +import { KibanaRequest } from 'src/core/server'; +import { KibanaRequest as KibanaRequest_2 } from 'kibana/server'; import { Logger } from 'src/core/server'; import { Logger as Logger_2 } from 'kibana/server'; import { LoggerFactory } from '@kbn/logging'; @@ -1040,7 +1040,7 @@ export interface ISearchOptions { // @public (undocumented) export interface ISearchSessionService { // (undocumented) - asScopedProvider: (core: CoreStart) => (request: KibanaRequest) => IScopedSearchSessionsClient; + asScopedProvider: (core: CoreStart) => (request: KibanaRequest_2) => IScopedSearchSessionsClient; } // Warning: (ae-missing-release-tag) "ISearchSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1068,11 +1068,11 @@ export interface ISearchStart IScopedSearchClient; + asScoped: (request: KibanaRequest) => IScopedSearchClient; getSearchStrategy: (name?: string) => ISearchStrategy; // (undocumented) searchSource: { - asScoped: (request: KibanaRequest_2) => Promise; + asScoped: (request: KibanaRequest) => Promise; }; } @@ -1391,6 +1391,8 @@ export interface SearchStrategyDependencies { // (undocumented) esClient: IScopedClusterClient; // (undocumented) + request: KibanaRequest; + // (undocumented) savedObjectsClient: SavedObjectsClientContract; // (undocumented) searchSessionsClient: IScopedSearchSessionsClient; @@ -1509,8 +1511,8 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // Warnings were encountered during analysis: // -// src/plugins/data/common/es_query/filters/meta_filter.ts:42:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:44:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:52:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts @@ -1551,7 +1553,7 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // src/plugins/data/server/index.ts:269:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:270:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts // src/plugins/data/server/plugin.ts:81:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/search/types.ts:114:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/search/types.ts:115:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/server/ui_settings.ts b/src/plugins/data/server/ui_settings.ts index ed3510c5776e0..e0d475e7124fd 100644 --- a/src/plugins/data/server/ui_settings.ts +++ b/src/plugins/data/server/ui_settings.ts @@ -605,35 +605,35 @@ export function getUiSettings(): Record> { }), }, { - from: 'now-24h', + from: 'now-24h/h', to: 'now', display: i18n.translate('data.advancedSettings.timepicker.last24Hours', { defaultMessage: 'Last 24 hours', }), }, { - from: 'now-7d', + from: 'now-7d/d', to: 'now', display: i18n.translate('data.advancedSettings.timepicker.last7Days', { defaultMessage: 'Last 7 days', }), }, { - from: 'now-30d', + from: 'now-30d/d', to: 'now', display: i18n.translate('data.advancedSettings.timepicker.last30Days', { defaultMessage: 'Last 30 days', }), }, { - from: 'now-90d', + from: 'now-90d/d', to: 'now', display: i18n.translate('data.advancedSettings.timepicker.last90Days', { defaultMessage: 'Last 90 days', }), }, { - from: 'now-1y', + from: 'now-1y/d', to: 'now', display: i18n.translate('data.advancedSettings.timepicker.last1Year', { defaultMessage: 'Last 1 year', diff --git a/src/plugins/dev_tools/public/plugin.ts b/src/plugins/dev_tools/public/plugin.ts index e9f5d206de918..5ccf614533164 100644 --- a/src/plugins/dev_tools/public/plugin.ts +++ b/src/plugins/dev_tools/public/plugin.ts @@ -7,7 +7,7 @@ */ import { BehaviorSubject } from 'rxjs'; -import { Plugin, CoreSetup, AppMountParameters, AppSearchDeepLink } from 'src/core/public'; +import { Plugin, CoreSetup, AppMountParameters, AppDeepLink } from 'src/core/public'; import { AppUpdater } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { sortBy } from 'lodash'; @@ -86,7 +86,7 @@ export class DevToolsPlugin implements Plugin { this.appStateUpdater.next(() => ({ navLinkStatus: AppNavLinkStatus.hidden })); } else { this.appStateUpdater.next(() => { - const deepLinks: AppSearchDeepLink[] = [...this.devTools.values()] + const deepLinks: AppDeepLink[] = [...this.devTools.values()] .filter( // Some tools do not use a string title, so we filter those out (tool) => !tool.enableRouting && !tool.isDisabled() && typeof tool.title === 'string' @@ -96,7 +96,7 @@ export class DevToolsPlugin implements Plugin { title: tool.title as string, path: `#/${tool.id}`, })); - return { meta: { searchDeepLinks: deepLinks } }; + return { deepLinks }; }); } } diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.test.tsx index b3fada3dbd00f..dfea1a94de7fa 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.test.tsx @@ -14,12 +14,11 @@ import { registerTestBed, TestBed, getCommonActions } from '../../test_utils'; import { RuntimeFieldPainlessError } from '../../lib'; import { Field } from '../../types'; import { FieldEditor, Props, FieldEditorFormState } from './field_editor'; +import { docLinksServiceMock } from '../../../../../core/public/mocks'; const defaultProps: Props = { onChange: jest.fn(), - links: { - runtimePainless: 'https://elastic.co', - }, + links: docLinksServiceMock.createStartContract() as any, ctx: { existingConcreteFields: [], namesNotAllowed: [], diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.test.ts b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.test.ts index 286931ad0e854..ed71e40fc80a9 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.test.ts +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.test.ts @@ -8,15 +8,16 @@ import { act } from 'react-dom/test-utils'; import '../test_utils/setup_environment'; -import { registerTestBed, TestBed, noop, docLinks, getCommonActions } from '../test_utils'; +import { registerTestBed, TestBed, noop, getCommonActions } from '../test_utils'; import { FieldEditor } from './field_editor'; import { FieldEditorFlyoutContent, Props } from './field_editor_flyout_content'; +import { docLinksServiceMock } from '../../../../core/public/mocks'; const defaultProps: Props = { onSave: noop, onCancel: noop, - docLinks, + docLinks: docLinksServiceMock.createStartContract() as any, FieldEditor, indexPattern: { fields: [] } as any, uiSettings: {} as any, diff --git a/src/plugins/index_pattern_field_editor/public/lib/documentation.ts b/src/plugins/index_pattern_field_editor/public/lib/documentation.ts index 70f180d7cb5f2..a18a75f63d6b8 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/documentation.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/documentation.ts @@ -8,14 +8,9 @@ import { DocLinksStart } from 'src/core/public'; -export const getLinks = (docLinks: DocLinksStart) => { - const { DOC_LINK_VERSION, ELASTIC_WEBSITE_URL } = docLinks; - const docsBase = `${ELASTIC_WEBSITE_URL}guide/en`; - const painlessDocsBase = `${docsBase}/elasticsearch/painless/${DOC_LINK_VERSION}`; - const kibanaDocsBase = `${docsBase}/kibana/${DOC_LINK_VERSION}`; - +export const getLinks = ({ links }: DocLinksStart) => { return { - runtimePainless: `${kibanaDocsBase}/managing-index-patterns.html#runtime-fields`, - painlessSyntax: `${painlessDocsBase}/painless-lang-spec.html`, + runtimePainless: links.indexPatterns.runtimeFields, + painlessSyntax: links.scriptedFields.painlessLangSpec, }; }; diff --git a/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap b/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap index 89fa05615a039..a80e3a67fb2db 100644 --- a/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap +++ b/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap @@ -2,6 +2,7 @@ exports[`KibanaPageTemplate render basic template 1`] = ` `; exports[`KibanaPageTemplate render custom empty prompt only 1`] = ` @@ -33,6 +45,7 @@ exports[`KibanaPageTemplate render custom empty prompt only 1`] = ` exports[`KibanaPageTemplate render custom empty prompt with page header 1`] = ` @@ -58,6 +76,12 @@ exports[`KibanaPageTemplate render custom empty prompt with page header 1`] = ` exports[`KibanaPageTemplate render default empty prompt 1`] = ` @@ -72,7 +96,76 @@ exports[`KibanaPageTemplate render default empty prompt 1`] = ` test

} + iconColor="" iconType="test" /> `; + +exports[`KibanaPageTemplate render solutionNav 1`] = ` + + } + pageSideBarProps={ + Object { + "className": "kbnPageTemplate__pageSideBar", + } + } + restrictWidth={true} +/> +`; diff --git a/src/plugins/kibana_react/public/page_template/page_template.scss b/src/plugins/kibana_react/public/page_template/page_template.scss new file mode 100644 index 0000000000000..4b8513311114d --- /dev/null +++ b/src/plugins/kibana_react/public/page_template/page_template.scss @@ -0,0 +1,15 @@ +$euiSideNavEmphasizedBackgroundColor: transparentize($euiColorLightShade, .7); + +.kbnPageTemplate__pageSideBar { + padding: $euiSizeL; + background: + linear-gradient(160deg, $euiSideNavEmphasizedBackgroundColor 0, $euiSideNavEmphasizedBackgroundColor $euiSizeXL, rgba(#FFF, 0) 0), + linear-gradient(175deg, $euiSideNavEmphasizedBackgroundColor 0, $euiSideNavEmphasizedBackgroundColor $euiSize, rgba(#FFF, 0) 0); +} + +@include euiBreakpoint('xs','s') { + .kbnPageTemplate__pageSideBar { + width: auto; + padding: 0; + } +} diff --git a/src/plugins/kibana_react/public/page_template/page_template.test.tsx b/src/plugins/kibana_react/public/page_template/page_template.test.tsx index 2ad9a81e7916c..43f96a6c2b98c 100644 --- a/src/plugins/kibana_react/public/page_template/page_template.test.tsx +++ b/src/plugins/kibana_react/public/page_template/page_template.test.tsx @@ -10,6 +10,46 @@ import React from 'react'; import { shallow } from 'enzyme'; import { KibanaPageTemplate } from './page_template'; import { EuiEmptyPrompt } from '@elastic/eui'; +import { KibanaPageTemplateSolutionNavProps } from './solution_nav'; + +const navItems: KibanaPageTemplateSolutionNavProps['items'] = [ + { + name: 'Ingest', + id: '1', + items: [ + { + name: 'Ingest Node Pipelines', + id: '1.1', + }, + { + name: 'Logstash Pipelines', + id: '1.2', + }, + { + name: 'Beats Central Management', + id: '1.3', + }, + ], + }, + { + name: 'Data', + id: '2', + items: [ + { + name: 'Index Management', + id: '2.1', + }, + { + name: 'Index Lifecycle Policies', + id: '2.2', + }, + { + name: 'Snapshot and Restore', + id: '2.3', + }, + ], + }, +]; describe('KibanaPageTemplate', () => { test('render default empty prompt', () => { @@ -66,4 +106,23 @@ describe('KibanaPageTemplate', () => { ); expect(component).toMatchSnapshot(); }); + + test('render solutionNav', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); }); diff --git a/src/plugins/kibana_react/public/page_template/page_template.tsx b/src/plugins/kibana_react/public/page_template/page_template.tsx index eb834d00402ef..0bbf97ca6ddb5 100644 --- a/src/plugins/kibana_react/public/page_template/page_template.tsx +++ b/src/plugins/kibana_react/public/page_template/page_template.tsx @@ -5,10 +5,21 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import './page_template.scss'; -import { EuiEmptyPrompt, EuiPageTemplate, EuiPageTemplateProps } from '@elastic/eui'; import React, { FunctionComponent } from 'react'; +import classNames from 'classnames'; + +import { EuiEmptyPrompt, EuiPageTemplate, EuiPageTemplateProps } from '@elastic/eui'; + +import { + KibanaPageTemplateSolutionNav, + KibanaPageTemplateSolutionNavProps, +} from './solution_nav/solution_nav'; +/** + * A thin wrapper around EuiPageTemplate with a few Kibana specific additions + */ export type KibanaPageTemplateProps = EuiPageTemplateProps & { /** * Changes the template type depending on other props provided. @@ -17,6 +28,10 @@ export type KibanaPageTemplateProps = EuiPageTemplateProps & { * With `pageHeader` and `children`: Uses `centeredContent` */ isEmptyState?: boolean; + /** + * Quick creation of EuiSideNav. Hooks up mobile instance too + */ + solutionNav?: KibanaPageTemplateSolutionNavProps; }; export const KibanaPageTemplate: FunctionComponent = ({ @@ -27,6 +42,8 @@ export const KibanaPageTemplate: FunctionComponent = ({ restrictWidth = true, bottomBar, bottomBarProps, + pageSideBar, + solutionNav, ...rest }) => { // Needed for differentiating between union types @@ -38,6 +55,13 @@ export const KibanaPageTemplate: FunctionComponent = ({ }; } + /** + * Create the solution nav component + */ + if (solutionNav) { + pageSideBar = ; + } + /** * An easy way to create the right content for empty pages */ @@ -48,6 +72,7 @@ export const KibanaPageTemplate: FunctionComponent = ({ children = ( {pageTitle} : undefined} body={description ?

{description}

: undefined} actions={rightSideItems} @@ -62,8 +87,14 @@ export const KibanaPageTemplate: FunctionComponent = ({ return ( diff --git a/src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap b/src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap new file mode 100644 index 0000000000000..0267357709534 --- /dev/null +++ b/src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap @@ -0,0 +1,238 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`KibanaPageTemplateSolutionNav accepts EuiSideNavProps 1`] = ` +
+ +

+ + Solution + +

+
+ + + + } + toggleOpenOnMobile={[Function]} + /> +
+`; + +exports[`KibanaPageTemplateSolutionNav renders 1`] = ` +
+ +

+ + Solution + +

+
+ + + + } + toggleOpenOnMobile={[Function]} + /> +
+`; + +exports[`KibanaPageTemplateSolutionNav renders with icon 1`] = ` +
+ +

+ + + Solution + +

+
+ + + + + } + toggleOpenOnMobile={[Function]} + /> +
+`; diff --git a/src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav_avatar.test.tsx.snap b/src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav_avatar.test.tsx.snap new file mode 100644 index 0000000000000..ede09c5652c31 --- /dev/null +++ b/src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav_avatar.test.tsx.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`KibanaPageTemplateSolutionNavAvatar renders 1`] = ` + +`; diff --git a/src/plugins/kibana_react/public/page_template/solution_nav/index.ts b/src/plugins/kibana_react/public/page_template/solution_nav/index.ts new file mode 100644 index 0000000000000..abbcde9a08486 --- /dev/null +++ b/src/plugins/kibana_react/public/page_template/solution_nav/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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. + */ + +export { KibanaPageTemplateSolutionNav, KibanaPageTemplateSolutionNavProps } from './solution_nav'; +export { + KibanaPageTemplateSolutionNavAvatar, + KibanaPageTemplateSolutionNavAvatarProps, +} from './solution_nav_avatar'; diff --git a/src/plugins/kibana_react/public/page_template/solution_nav/solution_nav.scss b/src/plugins/kibana_react/public/page_template/solution_nav/solution_nav.scss new file mode 100644 index 0000000000000..bdb88b2ab7baa --- /dev/null +++ b/src/plugins/kibana_react/public/page_template/solution_nav/solution_nav.scss @@ -0,0 +1,22 @@ +.kbnPageTemplateSolutionNav__title { + margin-bottom: $euiSizeL; +} + +@include euiBreakpoint('xs','s') { + .kbnPageTemplateSolutionNav { + // TODO: Fix in EUI + .euiSideNav__mobileToggle { + height: auto; + font-size: $euiFontSizeM; + + .euiButtonEmpty__text { + overflow: visible; + } + } + } + + // Rely on the `mobileToggle` of the EuiSideNav component to title the navigation list + .kbnPageTemplateSolutionNav__title { + display: none; + } +} diff --git a/src/plugins/kibana_react/public/page_template/solution_nav/solution_nav.test.tsx b/src/plugins/kibana_react/public/page_template/solution_nav/solution_nav.test.tsx new file mode 100644 index 0000000000000..1ba6cc924cda1 --- /dev/null +++ b/src/plugins/kibana_react/public/page_template/solution_nav/solution_nav.test.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 { KibanaPageTemplateSolutionNav, KibanaPageTemplateSolutionNavProps } from './solution_nav'; + +const items: KibanaPageTemplateSolutionNavProps['items'] = [ + { + name: 'Ingest', + id: '1', + items: [ + { + name: 'Ingest Node Pipelines', + id: '1.1', + }, + { + name: 'Logstash Pipelines', + id: '1.2', + }, + { + name: 'Beats Central Management', + id: '1.3', + }, + ], + }, + { + name: 'Data', + id: '2', + items: [ + { + name: 'Index Management', + id: '2.1', + }, + { + name: 'Index Lifecycle Policies', + id: '2.2', + }, + { + name: 'Snapshot and Restore', + id: '2.3', + }, + ], + }, +]; + +describe('KibanaPageTemplateSolutionNav', () => { + test('renders', () => { + const component = shallow(); + expect(component).toMatchSnapshot(); + }); + + test('renders with icon', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); + + test('accepts EuiSideNavProps', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/kibana_react/public/page_template/solution_nav/solution_nav.tsx b/src/plugins/kibana_react/public/page_template/solution_nav/solution_nav.tsx new file mode 100644 index 0000000000000..4aa456f716dbd --- /dev/null +++ b/src/plugins/kibana_react/public/page_template/solution_nav/solution_nav.tsx @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the 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 './solution_nav.scss'; + +import React, { FunctionComponent, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { EuiTitle, EuiSideNav, EuiSideNavProps, htmlIdGenerator } from '@elastic/eui'; + +import { + KibanaPageTemplateSolutionNavAvatar, + KibanaPageTemplateSolutionNavAvatarProps, +} from './solution_nav_avatar'; + +export type KibanaPageTemplateSolutionNavProps = EuiSideNavProps<{}> & { + /** + * Name of the solution, i.e. "Observability" + */ + name: KibanaPageTemplateSolutionNavAvatarProps['name']; + /** + * Solution logo, i.e. "logoObservability" + */ + icon?: KibanaPageTemplateSolutionNavAvatarProps['iconType']; +}; + +/** + * A wrapper around EuiSideNav but also creates the appropriate title with optional solution logo + */ +export const KibanaPageTemplateSolutionNav: FunctionComponent = ({ + name, + icon, + items, + ...rest +}) => { + const [isSideNavOpenOnMobile, setisSideNavOpenOnMobile] = useState(false); + const toggleOpenOnMobile = () => { + setisSideNavOpenOnMobile(!isSideNavOpenOnMobile); + }; + + /** + * Create the avatar. + */ + let solutionAvatar; + if (icon) { + solutionAvatar = ; + } + + /** + * Create the required title. + * a11y: Since the heading can't be nested inside `