diff --git a/.ci/Dockerfile b/.ci/Dockerfile
index 1c59d6d9aaaf815..3e6dfaefed92f1d 100644
--- a/.ci/Dockerfile
+++ b/.ci/Dockerfile
@@ -1,7 +1,7 @@
# NOTE: This Dockerfile is ONLY used to run certain tasks in CI. It is not used to run Kibana or as a distributable.
# If you're looking for the Kibana Docker image distributable, please see: src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts
-ARG NODE_VERSION=14.16.1
+ARG NODE_VERSION=14.17.0
FROM node:${NODE_VERSION} AS base
diff --git a/.ci/Jenkinsfile_flaky b/.ci/Jenkinsfile_flaky
index 7eafc66465bc726..8121405e5ae249d 100644
--- a/.ci/Jenkinsfile_flaky
+++ b/.ci/Jenkinsfile_flaky
@@ -73,11 +73,7 @@ def agentProcess(Map params = [:]) {
]) {
task {
if (config.needBuild) {
- if (!config.isXpack) {
- kibanaPipeline.buildOss()
- } else {
- kibanaPipeline.buildXpack()
- }
+ kibanaPipeline.buildKibana()
}
for(def i = 0; i < config.agentExecutions; i++) {
diff --git a/.ci/Jenkinsfile_security_cypress b/.ci/Jenkinsfile_security_cypress
index 811af44d1ca56f8..d48b9965919dc2f 100644
--- a/.ci/Jenkinsfile_security_cypress
+++ b/.ci/Jenkinsfile_security_cypress
@@ -16,7 +16,7 @@ kibanaPipeline(timeoutMinutes: 180) {
def job = 'xpack-securityCypress'
workers.ci(name: job, size: 'l', ramDisk: true) {
- kibanaPipeline.bash('test/scripts/jenkins_xpack_build_kibana.sh', 'Build Default Distributable')
+ kibanaPipeline.bash('test/scripts/jenkins_build_kibana.sh', 'Build Distributable')
kibanaPipeline.functionalTestProcess(job, 'test/scripts/jenkins_security_solution_cypress_chrome.sh')()
// Temporarily disabled to figure out test flake
// kibanaPipeline.functionalTestProcess(job, 'test/scripts/jenkins_security_solution_cypress_firefox.sh')()
diff --git a/.ci/es-snapshots/Jenkinsfile_verify_es b/.ci/es-snapshots/Jenkinsfile_verify_es
index d56ec61314ac760..dc3a3cde7d658a1 100644
--- a/.ci/es-snapshots/Jenkinsfile_verify_es
+++ b/.ci/es-snapshots/Jenkinsfile_verify_es
@@ -37,12 +37,8 @@ kibanaPipeline(timeoutMinutes: 210) {
])
task {
- kibanaPipeline.buildOss(6)
+ kibanaPipeline.buildKibana(16)
tasks.ossCiGroups()
- }
-
- task {
- kibanaPipeline.buildXpack(10, true)
tasks.xpackCiGroups()
tasks.xpackCiGroupDocker()
}
diff --git a/.eslintrc.js b/.eslintrc.js
index 20875a2c2913df1..a4ce657d523d955 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -711,6 +711,33 @@ module.exports = {
name: 'lodash/fp/assocPath',
message: 'Please use @elastic/safer-lodash-set instead',
},
+ {
+ name: 'lodash',
+ importNames: ['template'],
+ message:
+ 'lodash.template is unsafe, and not compatible with our content security policy.',
+ },
+ {
+ name: 'lodash.template',
+ message:
+ 'lodash.template is unsafe, and not compatible with our content security policy.',
+ },
+ {
+ name: 'lodash/template',
+ message:
+ 'lodash.template is unsafe, and not compatible with our content security policy.',
+ },
+ {
+ name: 'lodash/fp',
+ importNames: ['template'],
+ message:
+ 'lodash.template is unsafe, and not compatible with our content security policy.',
+ },
+ {
+ name: 'lodash/fp/template',
+ message:
+ 'lodash.template is unsafe, and not compatible with our content security policy.',
+ },
{
name: 'react-use',
message: 'Please use react-use/lib/{method} instead.',
@@ -730,6 +757,11 @@ module.exports = {
name: 'lodash.setwith',
message: 'Please use @elastic/safer-lodash-set instead',
},
+ {
+ name: 'lodash.template',
+ message:
+ 'lodash.template is unsafe, and not compatible with our content security policy.',
+ },
{
name: 'lodash/set',
message: 'Please use @elastic/safer-lodash-set instead',
@@ -738,6 +770,11 @@ module.exports = {
name: 'lodash/setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
+ {
+ name: 'lodash/template',
+ message:
+ 'lodash.template is unsafe, and not compatible with our content security policy.',
+ },
],
},
],
@@ -753,6 +790,18 @@ module.exports = {
property: 'set',
message: 'Please use @elastic/safer-lodash-set instead',
},
+ {
+ object: 'lodash',
+ property: 'template',
+ message:
+ 'lodash.template is unsafe, and not compatible with our content security policy.',
+ },
+ {
+ object: '_',
+ property: 'template',
+ message:
+ 'lodash.template is unsafe, and not compatible with our content security policy.',
+ },
{
object: 'lodash',
property: 'setWith',
@@ -1576,20 +1625,5 @@ module.exports = {
'@typescript-eslint/prefer-ts-expect-error': 'error',
},
},
- {
- files: [
- '**/public/**/*.{js,mjs,ts,tsx}',
- '**/common/**/*.{js,mjs,ts,tsx}',
- 'packages/**/*.{js,mjs,ts,tsx}',
- ],
- rules: {
- 'no-restricted-imports': [
- 'error',
- {
- patterns: ['lodash/*', '!lodash/fp', 'rxjs/internal-compatibility'],
- },
- ],
- },
- },
],
};
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index bafa023cf3f35fe..39daa5780436f3b 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -188,6 +188,7 @@
/src/core/ @elastic/kibana-core
/src/plugins/saved_objects_tagging_oss @elastic/kibana-core
/config/kibana.yml @elastic/kibana-core
+/x-pack/plugins/banners/ @elastic/kibana-core
/x-pack/plugins/features/ @elastic/kibana-core
/x-pack/plugins/licensing/ @elastic/kibana-core
/x-pack/plugins/global_search/ @elastic/kibana-core
@@ -202,7 +203,6 @@
/packages/kbn-legacy-logging/ @elastic/kibana-core
/packages/kbn-crypto/ @elastic/kibana-core
/packages/kbn-http-tools/ @elastic/kibana-core
-/src/plugins/status_page/ @elastic/kibana-core
/src/plugins/saved_objects_management/ @elastic/kibana-core
/src/dev/run_check_published_api_changes.ts @elastic/kibana-core
/src/plugins/home/public @elastic/kibana-core
@@ -214,7 +214,6 @@
#CC# /src/plugins/legacy_export/ @elastic/kibana-core
#CC# /src/plugins/xpack_legacy/ @elastic/kibana-core
#CC# /src/plugins/saved_objects/ @elastic/kibana-core
-#CC# /src/plugins/status_page/ @elastic/kibana-core
#CC# /x-pack/plugins/cloud/ @elastic/kibana-core
#CC# /x-pack/plugins/features/ @elastic/kibana-core
#CC# /x-pack/plugins/global_search/ @elastic/kibana-core
diff --git a/.node-version b/.node-version
index 6b17d228d335175..62df50f1eefe197 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1 @@
-14.16.1
+14.17.0
diff --git a/.nvmrc b/.nvmrc
index 6b17d228d335175..62df50f1eefe197 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-14.16.1
+14.17.0
diff --git a/Jenkinsfile b/Jenkinsfile
index 4c8f126b4883b38..db5ae306e6e2efb 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -6,7 +6,7 @@ kibanaLibrary.load()
kibanaPipeline(timeoutMinutes: 210, checkPrChanges: true, setCommitStatus: true) {
slackNotifications.onFailure(disabled: !params.NOTIFY_ON_FAILURE) {
githubPr.withDefaultPrComments {
- ciStats.trackBuild(requireSuccess: githubPr.isPr()) {
+ ciStats.trackBuild(requireSuccess: githubPr.isTrackedBranchPr()) {
catchError {
retryable.enable()
kibanaPipeline.allCiTasks()
diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md
deleted file mode 100644
index cb75452a28cd296..000000000000000
--- a/STYLEGUIDE.md
+++ /dev/null
@@ -1,685 +0,0 @@
-# Kibana Style Guide
-
-This guide applies to all development within the Kibana project and is
-recommended for the development of all Kibana plugins.
-
-- [General](#general)
-- [HTML](#html)
-- [API endpoints](#api-endpoints)
-- [TypeScript/JavaScript](#typeScript/javaScript)
-- [SASS files](#sass-files)
-- [React](#react)
-
-Besides the content in this style guide, the following style guides may also apply
-to all development within the Kibana project. Please make sure to also read them:
-
-- [Accessibility style guide (EUI Docs)](https://elastic.github.io/eui/#/guidelines/accessibility)
-- [SASS style guide (EUI Docs)](https://elastic.github.io/eui/#/guidelines/sass)
-
-## General
-
-### Filenames
-
-All filenames should use `snake_case`.
-
-**Right:** `src/kibana/index_patterns/index_pattern.js`
-
-**Wrong:** `src/kibana/IndexPatterns/IndexPattern.js`
-
-### Do not comment out code
-
-We use a version management system. If a line of code is no longer needed,
-remove it, don't simply comment it out.
-
-### Prettier and Linting
-
-We are gradually moving the Kibana code base over to Prettier. All TypeScript code
-and some JavaScript code (check `.eslintrc.js`) is using Prettier to format code. You
-can run `node script/eslint --fix` to fix linting issues and apply Prettier formatting.
-We recommend you to enable running ESLint via your IDE.
-
-Whenever possible we are trying to use Prettier and linting over written style guide rules.
-Consider every linting rule and every Prettier rule to be also part of our style guide
-and disable them only in exceptional cases and ideally leave a comment why they are
-disabled at that specific place.
-
-## HTML
-
-This part contains style guide rules around general (framework agnostic) HTML usage.
-
-### Camel case `id` and `data-test-subj`
-
-Use camel case for the values of attributes such as `id` and `data-test-subj` selectors.
-
-```html
-
-```
-
-The only exception is in cases where you're dynamically creating the value, and you need to use
-hyphens as delimiters:
-
-```jsx
-buttons.map(btn => (
-
-)
-```
-
-### Capitalization in HTML and CSS should always match
-
-It's important that when you write CSS/SASS selectors using classes, IDs, and attributes
-(keeping in mind that we should _never_ use IDs and attributes in our selectors), that the
-capitalization in the CSS matches that used in the HTML. HTML and CSS follow different case sensitivity rules, and we can avoid subtle gotchas by ensuring we use the
-same capitalization in both of them.
-
-### How to generate ids?
-
-When labeling elements (and for some other accessibility tasks) you will often need
-ids. Ids must be unique within the page i.e. no duplicate ids in the rendered DOM
-at any time.
-
-Since we have some components that are used multiple times on the page, you must
-make sure every instance of that component has a unique `id`. To make the generation
-of those `id`s easier, you can use the `htmlIdGenerator` service in the `@elastic/eui`.
-
-A React component could use it as follows:
-
-```jsx
-import { htmlIdGenerator } from '@elastic/eui';
-
-render() {
- // Create a new generator that will create ids deterministic
- const htmlId = htmlIdGenerator();
- return (
-
-
-
);
-}
-```
-
-Each id generator you create by calling `htmlIdGenerator()` will generate unique but
-deterministic ids. As you can see in the above example, that single generator
-created the same id in the label's `htmlFor` as well as the input's `id`.
-
-A single generator instance will create the same id when passed the same argument
-to the function multiple times. But two different generators will produce two different
-ids for the same argument to the function, as you can see in the following example:
-
-```js
-const generatorOne = htmlIdGenerator();
-const generatorTwo = htmlIdGenerator();
-
-// Those statements are always true:
-// Same generator
-generatorOne('foo') === generatorOne('foo');
-generatorOne('foo') !== generatorOne('bar');
-
-// Different generator
-generatorOne('foo') !== generatorTwo('foo');
-```
-
-This allows multiple instances of a single React component to now have different ids.
-If you include the above React component multiple times in the same page,
-each component instance will have a unique id, because each render method will use a different
-id generator.
-
-You can also use this service outside of React.
-
-## API endpoints
-
-The following style guide rules are targeting development of server side API endpoints.
-
-### Paths
-
-API routes must start with the `/api/` path segment, and should be followed by the plugin id if applicable:
-
-**Right:** `/api/marvel/nodes`
-
-**Wrong:** `/marvel/api/nodes`
-
-### snake_case
-
-Kibana uses `snake_case` for the entire API, just like Elasticsearch. All urls, paths, query string parameters, values, and bodies should be `snake_case` formatted.
-
-_Right:_
-
-```
-POST /api/kibana/index_patterns
-{
- "id": "...",
- "time_field_name": "...",
- "fields": [
- ...
- ]
-}
-```
-
-## TypeScript/JavaScript
-
-The following style guide rules apply for working with TypeScript/JavaScript files.
-
-### TypeScript vs. JavaScript
-
-Whenever possible, write code in TypeScript instead of JavaScript, especially if it's new code.
-Check out [TYPESCRIPT.md](TYPESCRIPT.md) for help with this process.
-
-### Prefer modern JavaScript/TypeScript syntax
-
-You should prefer modern language features in a lot of cases, e.g.:
-
-- Prefer `class` over `prototype` inheritance
-- Prefer arrow function over function expressions
-- Prefer arrow function over storing `this` (no `const self = this;`)
-- Prefer template strings over string concatenation
-- Prefer the spread operator for copying arrays (`[...arr]`) over `arr.slice()`
-- Use optional chaining (`?.`) and nullish Coalescing (`??`) over `lodash.get` (and similar utilities)
-
-### Avoid mutability and state
-
-Wherever possible, do not rely on mutable state. This means you should not
-reassign variables, modify object properties, or push values to arrays.
-Instead, create new variables, and shallow copies of objects and arrays:
-
-```js
-// good
-function addBar(foos, foo) {
- const newFoo = { ...foo, name: 'bar' };
- return [...foos, newFoo];
-}
-
-// bad
-function addBar(foos, foo) {
- foo.name = 'bar';
- foos.push(foo);
-}
-```
-
-### Avoid `any` whenever possible
-
-Since TypeScript 3.0 and the introduction of the
-[`unknown` type](https://mariusschulz.com/blog/the-unknown-type-in-typescript) there are rarely any
-reasons to use `any` as a type. Nearly all places of former `any` usage can be replace by either a
-generic or `unknown` (in cases the type is really not known).
-
-You should always prefer using those mechanisms over using `any`, since they are stricter typed and
-less likely to introduce bugs in the future due to insufficient types.
-
-If you’re not having `any` in your plugin or are starting a new plugin, you should enable the
-[`@typescript-eslint/no-explicit-any`](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-explicit-any.md)
-linting rule for your plugin via the [`.eslintrc.js`](https://github.com/elastic/kibana/blob/master/.eslintrc.js) config.
-
-### Avoid non-null assertions
-
-You should try avoiding non-null assertions (`!.`) wherever possible. By using them you tell
-TypeScript, that something is not null even though by it’s type it could be. Usage of non-null
-assertions is most often a side-effect of you actually checked that the variable is not `null`
-but TypeScript doesn’t correctly carry on that information till the usage of the variable.
-
-In most cases it’s possible to replace the non-null assertion by structuring your code/checks slightly different
-or using [user defined type guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
-to properly tell TypeScript what type a variable has.
-
-Using non-null assertion increases the risk for future bugs. In case the condition under which we assumed that the
-variable can’t be null has changed (potentially even due to changes in compeltely different files), the non-null
-assertion would now wrongly disable proper type checking for us.
-
-If you’re not using non-null assertions in your plugin or are starting a new plugin, consider enabling the
-[`@typescript-eslint/no-non-null-assertion`](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-non-null-assertion.md)
-linting rule for you plugin in the [`.eslintrc.js`](https://github.com/elastic/kibana/blob/master/.eslintrc.js) config.
-
-### Return/throw early from functions
-
-To avoid deep nesting of if-statements, always return a function's value as early
-as possible. And where possible, do any assertions first:
-
-```js
-// good
-function doStuff(val) {
- if (val > 100) {
- throw new Error('Too big');
- }
-
- if (val < 0) {
- return false;
- }
-
- // ... stuff
-}
-
-// bad
-function doStuff(val) {
- if (val >= 0) {
- if (val < 100) {
- // ... stuff
- } else {
- throw new Error('Too big');
- }
- } else {
- return false;
- }
-}
-```
-
-### Use object destructuring
-
-This helps avoid temporary references and helps prevent typo-related bugs.
-
-```js
-// best
-function fullName({ first, last }) {
- return `${first} ${last}`;
-}
-
-// good
-function fullName(user) {
- const { first, last } = user;
- return `${first} ${last}`;
-}
-
-// bad
-function fullName(user) {
- const first = user.first;
- const last = user.last;
- return `${first} ${last}`;
-}
-```
-
-### Use array destructuring
-
-Directly accessing array values via index should be avoided, but if it is
-necessary, use array destructuring:
-
-```js
-const arr = [1, 2, 3];
-
-// good
-const [first, second] = arr;
-
-// bad
-const first = arr[0];
-const second = arr[1];
-```
-
-### Magic numbers/strings
-
-These are numbers (or other values) simply used in line in your code. _Do not
-use these_, give them a variable name so they can be understood and changed
-easily.
-
-```js
-// good
-const minWidth = 300;
-
-if (width < minWidth) {
- ...
-}
-
-// bad
-if (width < 300) {
- ...
-}
-```
-
-### Modules
-
-Module dependencies should be written using native ES2015 syntax wherever
-possible (which is almost everywhere):
-
-```js
-// good
-import { mapValues } from 'lodash';
-export mapValues;
-
-// bad
-const _ = require('lodash');
-module.exports = _.mapValues;
-
-// worse
-define(['lodash'], function (_) {
- ...
-});
-```
-
-In those extremely rare cases where you're writing server-side JavaScript in a
-file that does not pass run through webpack, then use CommonJS modules.
-
-In those even rarer cases where you're writing client-side code that does not
-run through webpack, then do not use a module loader at all.
-
-#### Import only top-level modules
-
-The files inside a module are implementation details of that module. They
-should never be imported directly. Instead, you must only import the top-level
-API that's exported by the module itself.
-
-Without a clear mechanism in place in JS to encapsulate protected code, we make
-a broad assumption that anything beyond the root of a module is an
-implementation detail of that module.
-
-On the other hand, a module should be able to import parent and sibling
-modules.
-
-```js
-// good
-import foo from 'foo';
-import child from './child';
-import parent from '../';
-import ancestor from '../../../';
-import sibling from '../foo';
-
-// bad
-import inFoo from 'foo/child';
-import inSibling from '../foo/child';
-```
-
-### Global definitions
-
-Don't do this. Everything should be wrapped in a module that can be depended on
-by other modules. Even things as simple as a single value should be a module.
-
-### Only use ternary operators for small, simple code
-
-And _never_ use multiple ternaries together, because they make it more
-difficult to reason about how different values flow through the conditions
-involved. Instead, structure the logic for maximum readability.
-
-```js
-// good, a situation where only 1 ternary is needed
-const foo = a === b ? 1 : 2;
-
-// bad
-const foo = a === b ? 1 : a === c ? 2 : 3;
-```
-
-### Use descriptive conditions
-
-Any non-trivial conditions should be converted to functions or assigned to
-descriptively named variables. By breaking up logic into smaller,
-self-contained blocks, it becomes easier to reason about the higher-level
-logic. Additionally, these blocks become good candidates for extraction into
-their own modules, with unit-tests.
-
-```js
-// best
-function isShape(thing) {
- return thing instanceof Shape;
-}
-function notSquare(thing) {
- return !(thing instanceof Square);
-}
-if (isShape(thing) && notSquare(thing)) {
- ...
-}
-
-// good
-const isShape = thing instanceof Shape;
-const notSquare = !(thing instanceof Square);
-if (isShape && notSquare) {
- ...
-}
-
-// bad
-if (thing instanceof Shape && !(thing instanceof Square)) {
- ...
-}
-```
-
-### Name regular expressions
-
-```js
-// good
-const validPassword = /^(?=.*\d).{4,}$/;
-
-if (password.length >= 4 && validPassword.test(password)) {
- console.log('password is valid');
-}
-
-// bad
-if (password.length >= 4 && /^(?=.*\d).{4,}$/.test(password)) {
- console.log('losing');
-}
-```
-
-### Write small functions
-
-Keep your functions short. A good function fits on a slide that the people in
-the last row of a big room can comfortably read. So don't count on them having
-perfect vision and limit yourself to ~15 lines of code per function.
-
-### Use "rest" syntax rather than built-in `arguments`
-
-For expressiveness sake, and so you can be mix dynamic and explicit arguments.
-
-```js
-// good
-function something(foo, ...args) {
- ...
-}
-
-// bad
-function something(foo) {
- const args = Array.from(arguments).slice(1);
- ...
-}
-```
-
-### Default argument syntax
-
-Always use the default argument syntax for optional arguments.
-
-```js
-// good
-function foo(options = {}) {
- ...
-}
-
-// bad
-function foo(options) {
- if (typeof options === 'undefined') {
- options = {};
- }
- ...
-}
-```
-
-And put your optional arguments at the end.
-
-```js
-// good
-function foo(bar, options = {}) {
- ...
-}
-
-// bad
-function foo(options = {}, bar) {
- ...
-}
-```
-
-### Use thunks to create closures, where possible
-
-For trivial examples (like the one that follows), thunks will seem like
-overkill, but they encourage isolating the implementation details of a closure
-from the business logic of the calling code.
-
-```js
-// good
-function connectHandler(client, callback) {
- return () => client.connect(callback);
-}
-setTimeout(connectHandler(client, afterConnect), 1000);
-
-// not as good
-setTimeout(() => {
- client.connect(afterConnect);
-}, 1000);
-
-// bad
-setTimeout(() => {
- client.connect(() => {
- ...
- });
-}, 1000);
-```
-
-### Use slashes for comments
-
-Use slashes for both single line and multi line comments. Try to write
-comments that explain higher level mechanisms or clarify difficult
-segments of your code. _Don't use comments to restate trivial things_.
-
-_Exception:_ Comment blocks describing a function and its arguments
-(docblock) should start with `/**`, contain a single `*` at the beginning of
-each line, and end with `*/`.
-
-```js
-// good
-
-// 'ID_SOMETHING=VALUE' -> ['ID_SOMETHING=VALUE', 'SOMETHING', 'VALUE']
-const matches = item.match(/ID_([^\n]+)=([^\n]+)/));
-
-/**
- * Fetches a user from...
- * @param {string} id - id of the user
- * @return {Promise}
- */
-function loadUser(id) {
- // This function has a nasty side effect where a failure to increment a
- // redis counter used for statistics will cause an exception. This needs
- // to be fixed in a later iteration.
-
- ...
-}
-
-const isSessionValid = (session.expires < Date.now());
-if (isSessionValid) {
- ...
-}
-
-// bad
-
-// Execute a regex
-const matches = item.match(/ID_([^\n]+)=([^\n]+)/));
-
-// Usage: loadUser(5, function() { ... })
-function loadUser(id, cb) {
- // ...
-}
-
-// Check if the session is valid
-const isSessionValid = (session.expires < Date.now());
-// If the session is valid
-if (isSessionValid) {
- ...
-}
-```
-
-### Getters and Setters
-
-Feel free to use getters that are free from [side effects][sideeffect], like
-providing a length property for a collection class.
-
-Do not use setters, they cause more problems than they can solve.
-
-[sideeffect]: http://en.wikipedia.org/wiki/Side_effect_(computer_science)
-
-### Avoid circular dependencies
-
-As part of a future effort to use correct and idempotent build tools we need our code to be
-able to be represented as a directed acyclic graph. We must avoid having circular dependencies
-both on code and type imports to achieve that. One of the most critical parts is the plugins
-code. We've developed a tool to identify plugins with circular dependencies which
-has allowed us to build a list of plugins who have circular dependencies
-between each other.
-
-When building plugins we should avoid importing from plugins
-who are known to have circular dependencies at the moment as well as introducing
-new circular dependencies. You can run the same tool we use on our CI locally by
-typing `node scripts/find_plugins_with_circular_deps --debug`. It will error out in
-case new circular dependencies has been added with your changes
-(which will also happen in the CI) as well as print out the current list of
-the known circular dependencies which, as mentioned before, should not be imported
-by your code until the circular dependencies on these have been solved.
-
-## SASS files
-
-When writing a new component, create a sibling SASS file of the same name and import directly into the **top** of the JS/TS component file. Doing so ensures the styles are never separated or lost on import and allows for better modularization (smaller individual plugin asset footprint).
-
-All SASS (.scss) files will automatically build with the [EUI](https://elastic.github.io/eui/#/guidelines/sass) & Kibana invisibles (SASS variables, mixins, functions) from the [`globals_[theme].scss` file](src/core/public/core_app/styles/_globals_v7light.scss).
-
-While the styles for this component will only be loaded if the component exists on the page,
-the styles **will** be global and so it is recommended to use a three letter prefix on your
-classes to ensure proper scope.
-
-**Example:**
-
-```tsx
-// component.tsx
-
-import './component.scss';
-// All other imports below the SASS import
-
-export const Component = () => {
- return (
-
- );
-}
-```
-
-```scss
-// component.scss
-
-.plgComponent { ... }
-```
-
-Do not use the underscore `_` SASS file naming pattern when importing directly into a javascript file.
-
-## React
-
-The following style guide rules are specific for working with the React framework.
-
-### Prefer reactDirective over react-component
-
-When using `ngReact` to embed your react components inside Angular HTML, prefer the
-`reactDirective` service over the `react-component` directive.
-You can read more about these two ngReact methods [here](https://github.com/ngReact/ngReact#features).
-
-Using `react-component` means adding a bunch of components into angular, while `reactDirective` keeps them isolated, and is also a more succinct syntax.
-
-**Good:**
-
-```html
-
-```
-
-**Bad:**
-
-```html
-
-```
-
-### Action function names and prop function names
-
-Name action functions in the form of a strong verb and passed properties in the form of on. E.g:
-
-```jsx
-
-
-```
-
-## Attribution
-
-Parts of the JavaScript style guide were initially forked from the
-[node style guide](https://github.com/felixge/node-style-guide) created by [Felix Geisendörfer](http://felixge.de/) which is
-licensed under the [CC BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/)
-license.
diff --git a/STYLEGUIDE.mdx b/STYLEGUIDE.mdx
new file mode 100644
index 000000000000000..afe00476640b3c6
--- /dev/null
+++ b/STYLEGUIDE.mdx
@@ -0,0 +1,695 @@
+---
+id: kibStyleGuide
+slug: /kibana-dev-docs/styleguide
+title: Style Guide
+summary: JavaScript/TypeScript styleguide.
+date: 2021-05-06
+tags: ['kibana', 'onboarding', 'dev', 'styleguide', 'typescript', 'javascript']
+---
+
+This guide applies to all development within the Kibana project and is
+recommended for the development of all Kibana plugins.
+
+Besides the content in this style guide, the following style guides may also apply
+to all development within the Kibana project. Please make sure to also read them:
+
+- [Accessibility style guide (EUI Docs)](https://elastic.github.io/eui/#/guidelines/accessibility)
+- [SASS style guide (EUI Docs)](https://elastic.github.io/eui/#/guidelines/sass)
+
+## General
+
+### Filenames
+
+All filenames should use `snake_case`.
+
+**Right:** `src/kibana/index_patterns/index_pattern.js`
+
+**Wrong:** `src/kibana/IndexPatterns/IndexPattern.js`
+
+### Do not comment out code
+
+We use a version management system. If a line of code is no longer needed,
+remove it, don't simply comment it out.
+
+### Prettier and Linting
+
+We are gradually moving the Kibana code base over to Prettier. All TypeScript code
+and some JavaScript code (check `.eslintrc.js`) is using Prettier to format code. You
+can run `node script/eslint --fix` to fix linting issues and apply Prettier formatting.
+We recommend you to enable running ESLint via your IDE.
+
+Whenever possible we are trying to use Prettier and linting over written style guide rules.
+Consider every linting rule and every Prettier rule to be also part of our style guide
+and disable them only in exceptional cases and ideally leave a comment why they are
+disabled at that specific place.
+
+## HTML
+
+This part contains style guide rules around general (framework agnostic) HTML usage.
+
+### Camel case `id` and `data-test-subj`
+
+Use camel case for the values of attributes such as `id` and `data-test-subj` selectors.
+
+```html
+
+```
+
+The only exception is in cases where you're dynamically creating the value, and you need to use
+hyphens as delimiters:
+
+```jsx
+buttons.map(btn => (
+
+)
+```
+
+### Capitalization in HTML and CSS should always match
+
+It's important that when you write CSS/SASS selectors using classes, IDs, and attributes
+(keeping in mind that we should _never_ use IDs and attributes in our selectors), that the
+capitalization in the CSS matches that used in the HTML. HTML and CSS follow different case sensitivity rules, and we can avoid subtle gotchas by ensuring we use the
+same capitalization in both of them.
+
+### How to generate ids?
+
+When labeling elements (and for some other accessibility tasks) you will often need
+ids. Ids must be unique within the page i.e. no duplicate ids in the rendered DOM
+at any time.
+
+Since we have some components that are used multiple times on the page, you must
+make sure every instance of that component has a unique `id`. To make the generation
+of those `id`s easier, you can use the `htmlIdGenerator` service in the `@elastic/eui`.
+
+A React component could use it as follows:
+
+```jsx
+import { htmlIdGenerator } from '@elastic/eui';
+
+render() {
+ // Create a new generator that will create ids deterministic
+ const htmlId = htmlIdGenerator();
+ return (
+
+
+
);
+}
+```
+
+Each id generator you create by calling `htmlIdGenerator()` will generate unique but
+deterministic ids. As you can see in the above example, that single generator
+created the same id in the label's `htmlFor` as well as the input's `id`.
+
+A single generator instance will create the same id when passed the same argument
+to the function multiple times. But two different generators will produce two different
+ids for the same argument to the function, as you can see in the following example:
+
+```js
+const generatorOne = htmlIdGenerator();
+const generatorTwo = htmlIdGenerator();
+
+// Those statements are always true:
+// Same generator
+generatorOne('foo') === generatorOne('foo');
+generatorOne('foo') !== generatorOne('bar');
+
+// Different generator
+generatorOne('foo') !== generatorTwo('foo');
+```
+
+This allows multiple instances of a single React component to now have different ids.
+If you include the above React component multiple times in the same page,
+each component instance will have a unique id, because each render method will use a different
+id generator.
+
+You can also use this service outside of React.
+
+## API endpoints
+
+The following style guide rules are targeting development of server side API endpoints.
+
+### Paths
+
+API routes must start with the `/api/` path segment, and should be followed by the plugin id if applicable:
+
+**Right:** `/api/marvel/nodes`
+
+**Wrong:** `/marvel/api/nodes`
+
+### snake_case
+
+Kibana uses `snake_case` for the entire API, just like Elasticsearch. All urls, paths, query string parameters, values, and bodies should be `snake_case` formatted.
+
+_Right:_
+
+```
+POST /api/kibana/index_patterns
+{
+ "id": "...",
+ "time_field_name": "...",
+ "fields": [
+ ...
+ ]
+}
+```
+
+## TypeScript/JavaScript
+
+The following style guide rules apply for working with TypeScript/JavaScript files.
+
+### TypeScript vs. JavaScript
+
+Whenever possible, write code in TypeScript instead of JavaScript, especially if it's new code.
+Check out [TYPESCRIPT.md](TYPESCRIPT.md) for help with this process.
+
+### Prefer modern JavaScript/TypeScript syntax
+
+You should prefer modern language features in a lot of cases, e.g.:
+
+- Prefer `class` over `prototype` inheritance
+- Prefer arrow function over function expressions
+- Prefer arrow function over storing `this` (no `const self = this;`)
+- Prefer template strings over string concatenation
+- Prefer the spread operator for copying arrays (`[...arr]`) over `arr.slice()`
+- Use optional chaining (`?.`) and nullish Coalescing (`??`) over `lodash.get` (and similar utilities)
+
+### Avoid mutability and state
+
+Wherever possible, do not rely on mutable state. This means you should not
+reassign variables, modify object properties, or push values to arrays.
+Instead, create new variables, and shallow copies of objects and arrays:
+
+```js
+// good
+function addBar(foos, foo) {
+ const newFoo = { ...foo, name: 'bar' };
+ return [...foos, newFoo];
+}
+
+// bad
+function addBar(foos, foo) {
+ foo.name = 'bar';
+ foos.push(foo);
+}
+```
+
+### Avoid `any` whenever possible
+
+Since TypeScript 3.0 and the introduction of the
+[`unknown` type](https://mariusschulz.com/blog/the-unknown-type-in-typescript) there are rarely any
+reasons to use `any` as a type. Nearly all places of former `any` usage can be replace by either a
+generic or `unknown` (in cases the type is really not known).
+
+You should always prefer using those mechanisms over using `any`, since they are stricter typed and
+less likely to introduce bugs in the future due to insufficient types.
+
+If you’re not having `any` in your plugin or are starting a new plugin, you should enable the
+[`@typescript-eslint/no-explicit-any`](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-explicit-any.md)
+linting rule for your plugin via the [`.eslintrc.js`](https://github.com/elastic/kibana/blob/master/.eslintrc.js) config.
+
+### Avoid non-null assertions
+
+You should try avoiding non-null assertions (`!.`) wherever possible. By using them you tell
+TypeScript, that something is not null even though by it’s type it could be. Usage of non-null
+assertions is most often a side-effect of you actually checked that the variable is not `null`
+but TypeScript doesn’t correctly carry on that information till the usage of the variable.
+
+In most cases it’s possible to replace the non-null assertion by structuring your code/checks slightly different
+or using [user defined type guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
+to properly tell TypeScript what type a variable has.
+
+Using non-null assertion increases the risk for future bugs. In case the condition under which we assumed that the
+variable can’t be null has changed (potentially even due to changes in compeltely different files), the non-null
+assertion would now wrongly disable proper type checking for us.
+
+If you’re not using non-null assertions in your plugin or are starting a new plugin, consider enabling the
+[`@typescript-eslint/no-non-null-assertion`](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-non-null-assertion.md)
+linting rule for you plugin in the [`.eslintrc.js`](https://github.com/elastic/kibana/blob/master/.eslintrc.js) config.
+
+### Return/throw early from functions
+
+To avoid deep nesting of if-statements, always return a function's value as early
+as possible. And where possible, do any assertions first:
+
+```js
+// good
+function doStuff(val) {
+ if (val > 100) {
+ throw new Error('Too big');
+ }
+
+ if (val < 0) {
+ return false;
+ }
+
+ // ... stuff
+}
+
+// bad
+function doStuff(val) {
+ if (val >= 0) {
+ if (val < 100) {
+ // ... stuff
+ } else {
+ throw new Error('Too big');
+ }
+ } else {
+ return false;
+ }
+}
+```
+
+### Use object destructuring
+
+This helps avoid temporary references and helps prevent typo-related bugs.
+
+```js
+// best
+function fullName({ first, last }) {
+ return `${first} ${last}`;
+}
+
+// good
+function fullName(user) {
+ const { first, last } = user;
+ return `${first} ${last}`;
+}
+
+// bad
+function fullName(user) {
+ const first = user.first;
+ const last = user.last;
+ return `${first} ${last}`;
+}
+```
+
+### Use array destructuring
+
+Directly accessing array values via index should be avoided, but if it is
+necessary, use array destructuring:
+
+```js
+const arr = [1, 2, 3];
+
+// good
+const [first, second] = arr;
+
+// bad
+const first = arr[0];
+const second = arr[1];
+```
+
+### Magic numbers/strings
+
+These are numbers (or other values) simply used in line in your code. _Do not
+use these_, give them a variable name so they can be understood and changed
+easily.
+
+```js
+// good
+const minWidth = 300;
+
+if (width < minWidth) {
+ ...
+}
+
+// bad
+if (width < 300) {
+ ...
+}
+```
+
+### Modules
+
+Module dependencies should be written using native ES2015 syntax wherever
+possible (which is almost everywhere):
+
+```js
+// good
+import { mapValues } from 'lodash';
+export mapValues;
+
+// bad
+const _ = require('lodash');
+module.exports = _.mapValues;
+
+// worse
+define(['lodash'], function (_) {
+ ...
+});
+```
+
+In those extremely rare cases where you're writing server-side JavaScript in a
+file that does not pass run through webpack, then use CommonJS modules.
+
+In those even rarer cases where you're writing client-side code that does not
+run through webpack, then do not use a module loader at all.
+
+#### Import only top-level modules
+
+The files inside a module are implementation details of that module. They
+should never be imported directly. Instead, you must only import the top-level
+API that's exported by the module itself.
+
+Without a clear mechanism in place in JS to encapsulate protected code, we make
+a broad assumption that anything beyond the root of a module is an
+implementation detail of that module.
+
+On the other hand, a module should be able to import parent and sibling
+modules.
+
+```js
+// good
+import foo from 'foo';
+import child from './child';
+import parent from '../';
+import ancestor from '../../../';
+import sibling from '../foo';
+
+// bad
+import inFoo from 'foo/child';
+import inSibling from '../foo/child';
+```
+
+#### Avoid export \* in top level index.ts files
+
+The exports in `common/index.ts`, `public/index.ts` and `server/index.ts` dictate a plugin's public API. The public API should be carefully controlled, and using `export *` makes it very easy for a developer working on internal changes to export a new public API unintentionally.
+
+```js
+// good
+export { foo } from 'foo';
+export { child } from './child';
+
+// bad
+export * from 'foo/child';
+export * from '../foo/child';
+```
+
+### Global definitions
+
+Don't do this. Everything should be wrapped in a module that can be depended on
+by other modules. Even things as simple as a single value should be a module.
+
+### Only use ternary operators for small, simple code
+
+And _never_ use multiple ternaries together, because they make it more
+difficult to reason about how different values flow through the conditions
+involved. Instead, structure the logic for maximum readability.
+
+```js
+// good, a situation where only 1 ternary is needed
+const foo = a === b ? 1 : 2;
+
+// bad
+const foo = a === b ? 1 : a === c ? 2 : 3;
+```
+
+### Use descriptive conditions
+
+Any non-trivial conditions should be converted to functions or assigned to
+descriptively named variables. By breaking up logic into smaller,
+self-contained blocks, it becomes easier to reason about the higher-level
+logic. Additionally, these blocks become good candidates for extraction into
+their own modules, with unit-tests.
+
+```js
+// best
+function isShape(thing) {
+ return thing instanceof Shape;
+}
+function notSquare(thing) {
+ return !(thing instanceof Square);
+}
+if (isShape(thing) && notSquare(thing)) {
+ ...
+}
+
+// good
+const isShape = thing instanceof Shape;
+const notSquare = !(thing instanceof Square);
+if (isShape && notSquare) {
+ ...
+}
+
+// bad
+if (thing instanceof Shape && !(thing instanceof Square)) {
+ ...
+}
+```
+
+### Name regular expressions
+
+```js
+// good
+const validPassword = /^(?=.*\d).{4,}$/;
+
+if (password.length >= 4 && validPassword.test(password)) {
+ console.log('password is valid');
+}
+
+// bad
+if (password.length >= 4 && /^(?=.*\d).{4,}$/.test(password)) {
+ console.log('losing');
+}
+```
+
+### Write small functions
+
+Keep your functions short. A good function fits on a slide that the people in
+the last row of a big room can comfortably read. So don't count on them having
+perfect vision and limit yourself to ~15 lines of code per function.
+
+### Use "rest" syntax rather than built-in `arguments`
+
+For expressiveness sake, and so you can be mix dynamic and explicit arguments.
+
+```js
+// good
+function something(foo, ...args) {
+ ...
+}
+
+// bad
+function something(foo) {
+ const args = Array.from(arguments).slice(1);
+ ...
+}
+```
+
+### Default argument syntax
+
+Always use the default argument syntax for optional arguments.
+
+```js
+// good
+function foo(options = {}) {
+ ...
+}
+
+// bad
+function foo(options) {
+ if (typeof options === 'undefined') {
+ options = {};
+ }
+ ...
+}
+```
+
+And put your optional arguments at the end.
+
+```js
+// good
+function foo(bar, options = {}) {
+ ...
+}
+
+// bad
+function foo(options = {}, bar) {
+ ...
+}
+```
+
+### Use thunks to create closures, where possible
+
+For trivial examples (like the one that follows), thunks will seem like
+overkill, but they encourage isolating the implementation details of a closure
+from the business logic of the calling code.
+
+```js
+// good
+function connectHandler(client, callback) {
+ return () => client.connect(callback);
+}
+setTimeout(connectHandler(client, afterConnect), 1000);
+
+// not as good
+setTimeout(() => {
+ client.connect(afterConnect);
+}, 1000);
+
+// bad
+setTimeout(() => {
+ client.connect(() => {
+ ...
+ });
+}, 1000);
+```
+
+### Use slashes for comments
+
+Use slashes for both single line and multi line comments. Try to write
+comments that explain higher level mechanisms or clarify difficult
+segments of your code. _Don't use comments to restate trivial things_.
+
+_Exception:_ Comment blocks describing a function and its arguments
+(docblock) should start with `/**`, contain a single `*` at the beginning of
+each line, and end with `*/`.
+
+```js
+// good
+
+// 'ID_SOMETHING=VALUE' -> ['ID_SOMETHING=VALUE', 'SOMETHING', 'VALUE']
+const matches = item.match(/ID_([^\n]+)=([^\n]+)/));
+
+/**
+ * Fetches a user from...
+ * @param {string} id - id of the user
+ * @return {Promise}
+ */
+function loadUser(id) {
+ // This function has a nasty side effect where a failure to increment a
+ // redis counter used for statistics will cause an exception. This needs
+ // to be fixed in a later iteration.
+
+ ...
+}
+
+const isSessionValid = (session.expires < Date.now());
+if (isSessionValid) {
+ ...
+}
+
+// bad
+
+// Execute a regex
+const matches = item.match(/ID_([^\n]+)=([^\n]+)/));
+
+// Usage: loadUser(5, function() { ... })
+function loadUser(id, cb) {
+ // ...
+}
+
+// Check if the session is valid
+const isSessionValid = (session.expires < Date.now());
+// If the session is valid
+if (isSessionValid) {
+ ...
+}
+```
+
+### Getters and Setters
+
+Feel free to use getters that are free from [side effects][sideeffect], like
+providing a length property for a collection class.
+
+Do not use setters, they cause more problems than they can solve.
+
+[sideeffect]: http://en.wikipedia.org/wiki/Side_effect_(computer_science)
+
+### Avoid circular dependencies
+
+As part of a future effort to use correct and idempotent build tools we need our code to be
+able to be represented as a directed acyclic graph. We must avoid having circular dependencies
+both on code and type imports to achieve that. One of the most critical parts is the plugins
+code. We've developed a tool to identify plugins with circular dependencies which
+has allowed us to build a list of plugins who have circular dependencies
+between each other.
+
+When building plugins we should avoid importing from plugins
+who are known to have circular dependencies at the moment as well as introducing
+new circular dependencies. You can run the same tool we use on our CI locally by
+typing `node scripts/find_plugins_with_circular_deps --debug`. It will error out in
+case new circular dependencies has been added with your changes
+(which will also happen in the CI) as well as print out the current list of
+the known circular dependencies which, as mentioned before, should not be imported
+by your code until the circular dependencies on these have been solved.
+
+## SASS files
+
+When writing a new component, create a sibling SASS file of the same name and import directly into the **top** of the JS/TS component file. Doing so ensures the styles are never separated or lost on import and allows for better modularization (smaller individual plugin asset footprint).
+
+All SASS (.scss) files will automatically build with the [EUI](https://elastic.github.io/eui/#/guidelines/sass) & Kibana invisibles (SASS variables, mixins, functions) from the [`globals_[theme].scss` file](src/core/public/core_app/styles/_globals_v7light.scss).
+
+While the styles for this component will only be loaded if the component exists on the page,
+the styles **will** be global and so it is recommended to use a three letter prefix on your
+classes to ensure proper scope.
+
+**Example:**
+
+```tsx
+// component.tsx
+
+import './component.scss';
+// All other imports below the SASS import
+
+export const Component = () => {
+ return ;
+};
+```
+
+```scss
+// component.scss
+
+.plgComponent { ... }
+```
+
+Do not use the underscore `_` SASS file naming pattern when importing directly into a javascript file.
+
+## React
+
+The following style guide rules are specific for working with the React framework.
+
+### Prefer reactDirective over react-component
+
+When using `ngReact` to embed your react components inside Angular HTML, prefer the
+`reactDirective` service over the `react-component` directive.
+You can read more about these two ngReact methods [here](https://github.com/ngReact/ngReact#features).
+
+Using `react-component` means adding a bunch of components into angular, while `reactDirective` keeps them isolated, and is also a more succinct syntax.
+
+**Good:**
+
+```html
+
+```
+
+**Bad:**
+
+```html
+
+```
+
+### Action function names and prop function names
+
+Name action functions in the form of a strong verb and passed properties in the form of `on`. E.g:
+
+```jsx
+
+
+```
+
+## Attribution
+
+Parts of the JavaScript style guide were initially forked from the
+[node style guide](https://github.com/felixge/node-style-guide) created by [Felix Geisendörfer](http://felixge.de/) which is
+licensed under the [CC BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/)
+license.
diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel
index d334d7979ed591b..d80ad948cbb553a 100644
--- a/WORKSPACE.bazel
+++ b/WORKSPACE.bazel
@@ -10,15 +10,15 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Fetch Node.js rules
http_archive(
name = "build_bazel_rules_nodejs",
- sha256 = "65067dcad93a61deb593be7d3d9a32a4577d09665536d8da536d731da5cd15e2",
- urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/3.4.2/rules_nodejs-3.4.2.tar.gz"],
+ sha256 = "10f534e1c80f795cffe1f2822becd4897754d18564612510c59b3c73544ae7c6",
+ urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/3.5.0/rules_nodejs-3.5.0.tar.gz"],
)
# Now that we have the rules let's import from them to complete the work
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
# Assure we have at least a given rules_nodejs version
-check_rules_nodejs_version(minimum_version_string = "3.4.2")
+check_rules_nodejs_version(minimum_version_string = "3.5.0")
# Setup the Node.js toolchain for the architectures we want to support
#
@@ -27,13 +27,13 @@ check_rules_nodejs_version(minimum_version_string = "3.4.2")
# we can update that rule.
node_repositories(
node_repositories = {
- "14.16.1-darwin_amd64": ("node-v14.16.1-darwin-x64.tar.gz", "node-v14.16.1-darwin-x64", "b762b72fc149629b7e394ea9b75a093cad709a9f2f71480942945d8da0fc1218"),
- "14.16.1-linux_arm64": ("node-v14.16.1-linux-arm64.tar.xz", "node-v14.16.1-linux-arm64", "b4d474e79f7d33b3b4430fad25c3f836b82ce2d5bb30d4a2c9fa20df027e40da"),
- "14.16.1-linux_s390x": ("node-v14.16.1-linux-s390x.tar.xz", "node-v14.16.1-linux-s390x", "af9982fef32e4a3e4a5d66741dcf30ac9c27613bd73582fa1dae1fb25003047a"),
- "14.16.1-linux_amd64": ("node-v14.16.1-linux-x64.tar.xz", "node-v14.16.1-linux-x64", "85a89d2f68855282c87851c882d4c4bbea4cd7f888f603722f0240a6e53d89df"),
- "14.16.1-windows_amd64": ("node-v14.16.1-win-x64.zip", "node-v14.16.1-win-x64", "e469db37b4df74627842d809566c651042d86f0e6006688f0f5fe3532c6dfa41"),
+ "14.17.0-darwin_amd64": ("node-v14.17.0-darwin-x64.tar.gz", "node-v14.17.0-darwin-x64", "7b210652e11d1ee25650c164cf32381895e1dcb3e0ff1d0841d8abc1f47ac73e"),
+ "14.17.0-linux_arm64": ("node-v14.17.0-linux-arm64.tar.xz", "node-v14.17.0-linux-arm64", "712e5575cee20570a0a56f4d4b4572cb0f2ee2f4bce49433de18be0393e7df22"),
+ "14.17.0-linux_s390x": ("node-v14.17.0-linux-s390x.tar.xz", "node-v14.17.0-linux-s390x", "6419372b9e9ad37e0bce188dc5740f2f060aaa44454418e462b4088a310a1c0b"),
+ "14.17.0-linux_amd64": ("node-v14.17.0-linux-x64.tar.xz", "node-v14.17.0-linux-x64", "494b161759a3d19c70e3172d33ce1918dd8df9ad20d29d1652a8387a84e2d308"),
+ "14.17.0-windows_amd64": ("node-v14.17.0-win-x64.zip", "node-v14.17.0-win-x64", "6582a7259c433e9f667dcc4ed3e5d68bc514caba2eed40e4626c8b4c7e5ecd5c"),
},
- node_version = "14.16.1",
+ node_version = "14.17.0",
node_urls = [
"https://nodejs.org/dist/v{version}/{filename}",
],
diff --git a/api_docs/features.json b/api_docs/features.json
index 7ee722488221319..427525db6c4168c 100644
--- a/api_docs/features.json
+++ b/api_docs/features.json
@@ -2101,7 +2101,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 33
+ "lineNumber": 41
},
"deprecated": false,
"children": [
@@ -2125,7 +2125,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 34
+ "lineNumber": 42
},
"deprecated": false,
"children": [
@@ -2147,7 +2147,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 34
+ "lineNumber": 42
},
"deprecated": false,
"isRequired": true
@@ -2175,7 +2175,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 35
+ "lineNumber": 43
},
"deprecated": false,
"children": [
@@ -2197,7 +2197,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 35
+ "lineNumber": 43
},
"deprecated": false,
"isRequired": true
@@ -2225,7 +2225,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 41
+ "lineNumber": 49
},
"deprecated": false,
"children": [],
@@ -2251,7 +2251,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 47
+ "lineNumber": 55
},
"deprecated": false,
"children": [],
@@ -2270,7 +2270,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 48
+ "lineNumber": 56
},
"deprecated": false,
"children": [],
@@ -2288,11 +2288,47 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 56
+ "lineNumber": 64
},
"deprecated": false,
"children": [],
"returnComment": []
+ },
+ {
+ "parentPluginId": "features",
+ "id": "def-server.PluginSetupContract.featurePrivilegeIterator",
+ "type": "Function",
+ "tags": [],
+ "label": "featurePrivilegeIterator",
+ "description": [
+ "\nUtility for iterating through all privileges belonging to a specific feature.\n{@see FeaturePrivilegeIterator }"
+ ],
+ "signature": [
+ "FeaturePrivilegeIterator"
+ ],
+ "source": {
+ "path": "x-pack/plugins/features/server/plugin.ts",
+ "lineNumber": 70
+ },
+ "deprecated": false
+ },
+ {
+ "parentPluginId": "features",
+ "id": "def-server.PluginSetupContract.subFeaturePrivilegeIterator",
+ "type": "Function",
+ "tags": [],
+ "label": "subFeaturePrivilegeIterator",
+ "description": [
+ "\nUtility for iterating through all sub-feature privileges belonging to a specific feature.\n{@see SubFeaturePrivilegeIterator }"
+ ],
+ "signature": [
+ "SubFeaturePrivilegeIterator"
+ ],
+ "source": {
+ "path": "x-pack/plugins/features/server/plugin.ts",
+ "lineNumber": 76
+ },
+ "deprecated": false
}
],
"initialIsOpen": false
@@ -2306,7 +2342,7 @@
"description": [],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 59
+ "lineNumber": 79
},
"deprecated": false,
"children": [
@@ -2330,7 +2366,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 60
+ "lineNumber": 80
},
"deprecated": false,
"children": [],
@@ -2356,7 +2392,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
- "lineNumber": 61
+ "lineNumber": 81
},
"deprecated": false,
"children": [],
diff --git a/api_docs/spaces.json b/api_docs/spaces.json
index d53b69d5bd6b55a..940bbcf88a484f8 100644
--- a/api_docs/spaces.json
+++ b/api_docs/spaces.json
@@ -1867,7 +1867,7 @@
"section": "def-server.SavedObjectsRepository",
"text": "SavedObjectsRepository"
},
- ", \"get\" | \"delete\" | \"create\" | \"bulkCreate\" | \"checkConflicts\" | \"deleteByNamespace\" | \"find\" | \"bulkGet\" | \"resolve\" | \"update\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"bulkUpdate\" | \"removeReferencesTo\" | \"incrementCounter\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">"
+ ", \"get\" | \"delete\" | \"create\" | \"bulkCreate\" | \"checkConflicts\" | \"deleteByNamespace\" | \"find\" | \"bulkGet\" | \"resolve\" | \"update\" | \"collectMultiNamespaceReferences\" | \"updateObjectsSpaces\" | \"bulkUpdate\" | \"removeReferencesTo\" | \"incrementCounter\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">"
],
"source": {
"path": "x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts",
diff --git a/api_docs/spaces_oss.json b/api_docs/spaces_oss.json
index bce557db8e51673..a0ed4297ddc39bc 100644
--- a/api_docs/spaces_oss.json
+++ b/api_docs/spaces_oss.json
@@ -577,15 +577,11 @@
"deprecated": false,
"children": [
{
- "parentPluginId": "spacesOss",
- "id": "def-public.SpacesApi.activeSpace$",
- "type": "Object",
- "tags": [],
- "label": "activeSpace$",
- "description": [
- "\nObservable representing the currently active space.\nThe details of the space can change without a full page reload (such as display name, color, etc.)"
- ],
+ "id": "def-public.SpacesApi.getActiveSpace$",
+ "type": "Function",
+ "label": "getActiveSpace$",
"signature": [
+ "() => ",
"Observable",
"<",
{
@@ -597,11 +593,16 @@
},
">"
],
+ "description": [
+ "\nObservable representing the currently active space.\nThe details of the space can change without a full page reload (such as display name, color, etc.)"
+ ],
+ "children": [],
+ "tags": [],
+ "returnComment": [],
"source": {
"path": "src/plugins/spaces_oss/public/api.ts",
"lineNumber": 22
- },
- "deprecated": false
+ }
},
{
"parentPluginId": "spacesOss",
diff --git a/dev_docs/assets/ml_csv_upload.png b/dev_docs/assets/ml_csv_upload.png
new file mode 100644
index 000000000000000..dd82e8d40fcc832
Binary files /dev/null and b/dev_docs/assets/ml_csv_upload.png differ
diff --git a/dev_docs/assets/sample_data.png b/dev_docs/assets/sample_data.png
new file mode 100644
index 000000000000000..69bf138f9a1d793
Binary files /dev/null and b/dev_docs/assets/sample_data.png differ
diff --git a/dev_docs/building_blocks.mdx b/dev_docs/building_blocks.mdx
index 95851ea66b8cb58..327492a20d5b887 100644
--- a/dev_docs/building_blocks.mdx
+++ b/dev_docs/building_blocks.mdx
@@ -74,7 +74,7 @@ Check out the Map Embeddable if you wish to embed a map in your application.
All Kibana pages should use KibanaPageTemplate to setup their pages. It's 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.
-Check out for more implementation guidance.
+Check out for more implementation guidance.
**Github labels**: `EUI`
diff --git a/dev_docs/tutorials/kibana_page_template.mdx b/dev_docs/tutorials/kibana_page_template.mdx
index ec78fa49aa231e8..aa38890a8ac9ecc 100644
--- a/dev_docs/tutorials/kibana_page_template.mdx
+++ b/dev_docs/tutorials/kibana_page_template.mdx
@@ -1,13 +1,13 @@
---
-id: kibDevDocsKBLTutorial
-slug: /kibana-dev-docs/tutorials/kibana-page-layout
-title: KibanaPageLayout component
+id: kibDevDocsKPTTutorial
+slug: /kibana-dev-docs/tutorials/kibana-page-template
+title: KibanaPageTemplate component
summary: Learn how to create pages in Kibana
date: 2021-03-20
tags: ['kibana', 'dev', 'ui', 'tutorials']
---
-`KibanaPageLayout` 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.
+`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.
@@ -18,7 +18,7 @@ Use the `isEmptyState` prop for when there is no page content to show. For examp
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
-
+No data}
body="You have no data. Would you like some of ours?"
@@ -55,7 +55,7 @@ You can also provide a custom empty prompt to replace the pre-built one. You'll
,
]}
/>
-
+
```
![Screenshot of demo custom empty state code. Shows the Kibana navigation bars and a centered empty state with the a level 1 heading "No data", body text "You have no data. Would you like some of ours?", and a button that says "Get sample data".](../assets/kibana_custom_empty_state.png)
@@ -65,7 +65,7 @@ You can also provide a custom empty prompt to replace the pre-built one. You'll
When passing both a `pageHeader` configuration and `isEmptyState`, the component will render the proper template (`centeredContent`). Be sure to reduce the heading level within your child empty prompt to `
`.
```tsx
-,
]}
/>
-
+
```
![Screenshot of demo custom empty state code with a page header. Shows the Kibana navigation bars, a level 1 heading "Dashboards", and a centered empty state with the a level 2 heading "No data", body text "You have no data. Would you like some of ours?", and a button that says "Get sample data".](../assets/kibana_header_and_empty_state.png)
diff --git a/dev_docs/tutorials/sample_data.mdx b/dev_docs/tutorials/sample_data.mdx
new file mode 100644
index 000000000000000..75afaaaea6f327b
--- /dev/null
+++ b/dev_docs/tutorials/sample_data.mdx
@@ -0,0 +1,33 @@
+---
+id: kibDevTutorialSampleData
+slug: /kibana-dev-docs/tutorial/sample-data
+title: Add sample data
+summary: Learn how to add sample data to Kibana
+date: 2021-04-26
+tags: ['kibana', 'onboarding', 'dev', 'architecture', 'tutorials']
+---
+
+## Installation from the UI
+
+1. Navigate to the home page.
+2. Click **Add data**.
+3. Click on the **Sample data** tab.
+4. Select a dataset by clicking on the **Add data** button.
+
+![Sample Data](../assets/sample_data.png)
+
+## CSV Upload
+
+1. Navigate to the **Machine Learning** application.
+2. Click on the **Data Visualizer** tab.
+3. Click on **Select file** in the **Import data** container.
+
+![CSV Upload](../assets/ml_csv_upload.png)
+
+## makelogs
+
+The makelogs script generates sample web server logs. Make sure Elasticsearch is running before running the script.
+
+```sh
+node scripts/makelogs --auth :
+```
\ No newline at end of file
diff --git a/docs/concepts/index-patterns.asciidoc b/docs/concepts/index-patterns.asciidoc
index 158fa6282e6fa64..03bad72a317c6b5 100644
--- a/docs/concepts/index-patterns.asciidoc
+++ b/docs/concepts/index-patterns.asciidoc
@@ -10,10 +10,9 @@ or all indices that contain your data. It can also point to a
You’ll learn how to:
-* Create an index pattern
-* Explore and configure the data fields
+* Create index patterns
* Set the default index pattern
-* Delete an index pattern
+* Delete index patterns
[float]
[[index-patterns-read-only-access]]
@@ -133,77 +132,23 @@ To exclude a cluster, use `cluster_*:logstash-*,cluster_one:-*`.
Once an index pattern is configured using the {ccs} syntax, all searches and
aggregations using that index pattern in {kib} take advantage of {ccs}.
-
-[float]
-[[reload-fields]]
-=== Explore and configure the data fields
-
-To explore and configure the data fields in your index pattern, open the main menu, then click
-*Stack Management > Index Patterns*. Each field has a {ref}/mapping.html[mapping],
-which indicates the type of data the field contains in {es},
-such as strings or boolean values. The field mapping also determines
-how you can use the field, such as whether it can be searched or aggregated.
-
-When a new field is added to the index, the index pattern field list is updated
-the next time the index pattern is loaded, for example, when you load the page or
-move between {kib} apps.
-
-[role="screenshot"]
-image:management/index-patterns/images/new-index-pattern.png["Create index pattern"]
-
-[float]
-=== Format the display of common field types
-
-Whenever possible, {kib} uses the same field type for display as
-{es}. However, some field types that {es} supports are not available
-in {kib}. Using field formatters, you can manually change the field type in {kib} to display your data the way you prefer
-to see it, regardless of how it is stored in {es}.
-
-For example, if you store
-date values in {es}, you can use a {kib} field formatter to change the display to mm/dd/yyyy format.
-{kib} has field formatters for
-<>,
-<>,
-<>,
-and <>.
-
-To customize the displayed field name provided by {es}, you can
-use *Custom Label* .
-
-A popularity counter keeps track of the fields you use most often.
-The top five most popular fields and their values are displayed in <>.
-
-To edit the field display, click the edit icon
-(image:management/index-patterns/images/edit_icon.png[]) in the index pattern detail view.
-
-[role="screenshot"]
-image:management/index-patterns/images/edit-field-format.png["Edit field format"]
-
[float]
-[[default-index-pattern]]
-=== Set the default index pattern
+[[delete-index-pattern]]
+=== Delete index patterns
-The first index pattern you create is automatically designated as the default pattern,
-but you can set any index pattern as the default. The default index pattern is automatically selected when you first open <> or create a visualization from scratch.
+When you delete an index pattern, you are unable to recover the associated field formatters, scripted fields, source filters,
+and field popularity data. Deleting an index pattern does not remove any indices or data documents from {es}.
-. In *Index patterns*, click the index pattern name.
-. Click the star icon (image:management/index-patterns/images/star.png[Star icon]).
+WARNING: Deleting an index pattern breaks all visualizations, saved searches, and other saved objects that reference the index pattern.
-[float]
-[[delete-index-pattern]]
-=== Delete an index pattern
+. Open the main menu, then click *Stack Management > Index Patterns*.
-This action removes the pattern from the list of saved objects in {kib}.
-You will not be able to recover field formatters, scripted fields, source filters,
-and field popularity data associated with the index pattern. Deleting an
-index pattern does not remove any indices or data documents from {es}.
+. Click the index pattern you want to delete.
-WARNING: Deleting an index pattern breaks all visualizations, saved searches, and other saved objects that reference the pattern.
-
-. In *Index patterns*, click the index pattern name.
-. Click the delete icon (image:management/index-patterns/images/delete.png[Delete icon]).
+. Delete (image:management/index-patterns/images/delete.png[Delete icon]) the index pattern.
[float]
+[[reload-fields]]
=== What’s next
-* Learn about <> and how to create data on the fly.
+Learn how to <> in your index patterns.
diff --git a/docs/concepts/index.asciidoc b/docs/concepts/index.asciidoc
index 74e5bd4d4fb2f8a..cb37dceb535649b 100644
--- a/docs/concepts/index.asciidoc
+++ b/docs/concepts/index.asciidoc
@@ -49,10 +49,9 @@ that accesses the {kib} API.
{kib} uses the index pattern to show you a list of fields, such as
`event.duration`. You can customize the display name and format for each field.
-For example, you can tell Kibana to display `event.duration` in seconds.
+For example, you can tell {kib} to display `event.duration` in seconds.
{kib} has <> for strings,
-dates, geopoints,
-and numbers.
+dates, geopoints, and numbers.
[float]
[[kibana-concepts-searching-your-data]]
diff --git a/docs/developer/getting-started/monorepo-packages.asciidoc b/docs/developer/getting-started/monorepo-packages.asciidoc
index cba5b9bfadf98d8..e81875d7893dd8b 100644
--- a/docs/developer/getting-started/monorepo-packages.asciidoc
+++ b/docs/developer/getting-started/monorepo-packages.asciidoc
@@ -74,17 +74,24 @@ yarn kbn watch-bazel
- @kbn/config-schema
- @kbn/crypto
- @kbn/dev-utils
+- @kbn/docs-utils
- @kbn/es
- @kbn/eslint-import-resolver-kibana
- @kbn/eslint-plugin-eslint
- @kbn/expect
+- @kbn/i18n
- @kbn/legacy-logging
- @kbn/logging
- @kbn/securitysolution-constants
-- @kbn/securitysolution-utils
+- @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-utils
+- @kbn/server-http-tools
- @kbn/std
+- @kbn/telemetry-utils
- @kbn/tinymath
- @kbn/utility-types
- @kbn/utils
-
diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc
index 067b6b206fa1447..4ba5e32eec8b536 100644
--- a/docs/developer/plugin-list.asciidoc
+++ b/docs/developer/plugin-list.asciidoc
@@ -181,6 +181,10 @@ Content is fetched from the remote (https://feeds.elastic.co and https://feeds-s
oss plugins.
+|{kib-repo}blob/{branch}/src/plugins/screenshot_mode/README.md[screenshotMode]
+|The service exposed by this plugin informs consumers whether they should optimize for non-interactivity. In this way plugins can avoid loading unnecessary code, data or other services.
+
+
|{kib-repo}blob/{branch}/src/plugins/security_oss/README.md[securityOss]
|securityOss is responsible for educating users about Elastic's free security features,
so they can properly protect the data within their clusters.
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 180d376ceaf51c3..0448ad42c94fa92 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
@@ -144,6 +144,7 @@ readonly links: {
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
+ createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: 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 91ef8358b5fd227..78d2d8daa3d4575 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; 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 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 b868a7f8216df69..5280d85f3d3b38a 100644
--- a/docs/development/core/public/kibana-plugin-core-public.md
+++ b/docs/development/core/public/kibana-plugin-core-public.md
@@ -103,12 +103,14 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [SavedObjectAttributes](./kibana-plugin-core-public.savedobjectattributes.md) | The data for a Saved Object is stored as an object in the attributes property. |
| [SavedObjectError](./kibana-plugin-core-public.savedobjecterror.md) | |
| [SavedObjectReference](./kibana-plugin-core-public.savedobjectreference.md) | A reference to another saved object. |
+| [SavedObjectReferenceWithContext](./kibana-plugin-core-public.savedobjectreferencewithcontext.md) | A returned input object or one of its references, with additional context. |
| [SavedObjectsBaseOptions](./kibana-plugin-core-public.savedobjectsbaseoptions.md) | |
| [SavedObjectsBatchResponse](./kibana-plugin-core-public.savedobjectsbatchresponse.md) | |
| [SavedObjectsBulkCreateObject](./kibana-plugin-core-public.savedobjectsbulkcreateobject.md) | |
| [SavedObjectsBulkCreateOptions](./kibana-plugin-core-public.savedobjectsbulkcreateoptions.md) | |
| [SavedObjectsBulkUpdateObject](./kibana-plugin-core-public.savedobjectsbulkupdateobject.md) | |
| [SavedObjectsBulkUpdateOptions](./kibana-plugin-core-public.savedobjectsbulkupdateoptions.md) | |
+| [SavedObjectsCollectMultiNamespaceReferencesResponse](./kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.md) | The response when object references are collected. |
| [SavedObjectsCreateOptions](./kibana-plugin-core-public.savedobjectscreateoptions.md) | |
| [SavedObjectsFindOptions](./kibana-plugin-core-public.savedobjectsfindoptions.md) | |
| [SavedObjectsFindOptionsReference](./kibana-plugin-core-public.savedobjectsfindoptionsreference.md) | |
diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.id.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.id.md
new file mode 100644
index 000000000000000..10e01d7e7a9316b
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.id.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-public.savedobjectreferencewithcontext.md) > [id](./kibana-plugin-core-public.savedobjectreferencewithcontext.id.md)
+
+## SavedObjectReferenceWithContext.id property
+
+The ID of the referenced object
+
+Signature:
+
+```typescript
+id: string;
+```
diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.inboundreferences.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.inboundreferences.md
new file mode 100644
index 000000000000000..722b11f0c7ba9fb
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.inboundreferences.md
@@ -0,0 +1,17 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-public.savedobjectreferencewithcontext.md) > [inboundReferences](./kibana-plugin-core-public.savedobjectreferencewithcontext.inboundreferences.md)
+
+## SavedObjectReferenceWithContext.inboundReferences property
+
+References to this object; note that this does not contain \_all inbound references everywhere for this object\_, it only contains inbound references for the scope of this operation
+
+Signature:
+
+```typescript
+inboundReferences: Array<{
+ type: string;
+ id: string;
+ name: string;
+ }>;
+```
diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.ismissing.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.ismissing.md
new file mode 100644
index 000000000000000..8a4b378850764ad
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.ismissing.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-public.savedobjectreferencewithcontext.md) > [isMissing](./kibana-plugin-core-public.savedobjectreferencewithcontext.ismissing.md)
+
+## SavedObjectReferenceWithContext.isMissing property
+
+Whether or not this object or reference is missing
+
+Signature:
+
+```typescript
+isMissing?: boolean;
+```
diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.md
new file mode 100644
index 000000000000000..a79fa96695e36b0
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.md
@@ -0,0 +1,25 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-public.savedobjectreferencewithcontext.md)
+
+## SavedObjectReferenceWithContext interface
+
+A returned input object or one of its references, with additional context.
+
+Signature:
+
+```typescript
+export interface SavedObjectReferenceWithContext
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [id](./kibana-plugin-core-public.savedobjectreferencewithcontext.id.md) | string | The ID of the referenced object |
+| [inboundReferences](./kibana-plugin-core-public.savedobjectreferencewithcontext.inboundreferences.md) | Array<{ type: string; id: string; name: string; }> | References to this object; note that this does not contain \_all inbound references everywhere for this object\_, it only contains inbound references for the scope of this operation |
+| [isMissing](./kibana-plugin-core-public.savedobjectreferencewithcontext.ismissing.md) | boolean | Whether or not this object or reference is missing |
+| [spaces](./kibana-plugin-core-public.savedobjectreferencewithcontext.spaces.md) | string[] | The space(s) that the referenced object exists in |
+| [spacesWithMatchingAliases](./kibana-plugin-core-public.savedobjectreferencewithcontext.spaceswithmatchingaliases.md) | string[] | The space(s) that legacy URL aliases matching this type/id exist in |
+| [type](./kibana-plugin-core-public.savedobjectreferencewithcontext.type.md) | string | The type of the referenced object |
+
diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.spaces.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.spaces.md
new file mode 100644
index 000000000000000..9140e94721f1e3b
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.spaces.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-public.savedobjectreferencewithcontext.md) > [spaces](./kibana-plugin-core-public.savedobjectreferencewithcontext.spaces.md)
+
+## SavedObjectReferenceWithContext.spaces property
+
+The space(s) that the referenced object exists in
+
+Signature:
+
+```typescript
+spaces: string[];
+```
diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.spaceswithmatchingaliases.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.spaceswithmatchingaliases.md
new file mode 100644
index 000000000000000..02b0c9c0949df44
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.spaceswithmatchingaliases.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-public.savedobjectreferencewithcontext.md) > [spacesWithMatchingAliases](./kibana-plugin-core-public.savedobjectreferencewithcontext.spaceswithmatchingaliases.md)
+
+## SavedObjectReferenceWithContext.spacesWithMatchingAliases property
+
+The space(s) that legacy URL aliases matching this type/id exist in
+
+Signature:
+
+```typescript
+spacesWithMatchingAliases?: string[];
+```
diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.type.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.type.md
new file mode 100644
index 000000000000000..d2e341627153caf
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectreferencewithcontext.type.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-public.savedobjectreferencewithcontext.md) > [type](./kibana-plugin-core-public.savedobjectreferencewithcontext.type.md)
+
+## SavedObjectReferenceWithContext.type property
+
+The type of the referenced object
+
+Signature:
+
+```typescript
+type: string;
+```
diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.md
new file mode 100644
index 000000000000000..a6e0a274008a655
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsCollectMultiNamespaceReferencesResponse](./kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.md)
+
+## SavedObjectsCollectMultiNamespaceReferencesResponse interface
+
+The response when object references are collected.
+
+Signature:
+
+```typescript
+export interface SavedObjectsCollectMultiNamespaceReferencesResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [objects](./kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.objects.md) | SavedObjectReferenceWithContext[] | |
+
diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.objects.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.objects.md
new file mode 100644
index 000000000000000..66a7a19d18288a9
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.objects.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsCollectMultiNamespaceReferencesResponse](./kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.md) > [objects](./kibana-plugin-core-public.savedobjectscollectmultinamespacereferencesresponse.objects.md)
+
+## SavedObjectsCollectMultiNamespaceReferencesResponse.objects property
+
+Signature:
+
+```typescript
+objects: SavedObjectReferenceWithContext[];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.assistanceapiresponse.md b/docs/development/core/server/kibana-plugin-core-server.assistanceapiresponse.md
index 4778c98493b5bd4..1daaf95a73d5dd3 100644
--- a/docs/development/core/server/kibana-plugin-core-server.assistanceapiresponse.md
+++ b/docs/development/core/server/kibana-plugin-core-server.assistanceapiresponse.md
@@ -6,6 +6,7 @@
> Warning: This API is now obsolete.
>
+> 7.16
>
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.assistantapiclientparams.md b/docs/development/core/server/kibana-plugin-core-server.assistantapiclientparams.md
index 6d3f8df2fa51828..1031d733fed4ab7 100644
--- a/docs/development/core/server/kibana-plugin-core-server.assistantapiclientparams.md
+++ b/docs/development/core/server/kibana-plugin-core-server.assistantapiclientparams.md
@@ -6,6 +6,7 @@
> Warning: This API is now obsolete.
>
+> 7.16
>
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationapiclientparams.md b/docs/development/core/server/kibana-plugin-core-server.deprecationapiclientparams.md
index ed64d61e75fabc9..fc1748d4db90799 100644
--- a/docs/development/core/server/kibana-plugin-core-server.deprecationapiclientparams.md
+++ b/docs/development/core/server/kibana-plugin-core-server.deprecationapiclientparams.md
@@ -6,6 +6,7 @@
> Warning: This API is now obsolete.
>
+> 7.16
>
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationapiresponse.md b/docs/development/core/server/kibana-plugin-core-server.deprecationapiresponse.md
index 1d837d9b4705d69..ce40bd7c750f019 100644
--- a/docs/development/core/server/kibana-plugin-core-server.deprecationapiresponse.md
+++ b/docs/development/core/server/kibana-plugin-core-server.deprecationapiresponse.md
@@ -6,6 +6,7 @@
> Warning: This API is now obsolete.
>
+> 7.16
>
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationinfo.md b/docs/development/core/server/kibana-plugin-core-server.deprecationinfo.md
index 8eeb5ef638a8294..d9d1c6c3edb41ef 100644
--- a/docs/development/core/server/kibana-plugin-core-server.deprecationinfo.md
+++ b/docs/development/core/server/kibana-plugin-core-server.deprecationinfo.md
@@ -6,6 +6,7 @@
> Warning: This API is now obsolete.
>
+> 7.16
>
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md
index 2398410fa4b8405..90aa2f0100d8832 100644
--- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md
@@ -16,5 +16,6 @@ export interface ElasticsearchStatusMeta
| Property | Type | Description |
| --- | --- | --- |
| [incompatibleNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.incompatiblenodes.md) | NodesVersionCompatibility['incompatibleNodes'] | |
+| [nodesInfoRequestError](./kibana-plugin-core-server.elasticsearchstatusmeta.nodesinforequesterror.md) | NodesVersionCompatibility['nodesInfoRequestError'] | |
| [warningNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.warningnodes.md) | NodesVersionCompatibility['warningNodes'] | |
diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.nodesinforequesterror.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.nodesinforequesterror.md
new file mode 100644
index 000000000000000..1b46078a1a45316
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.nodesinforequesterror.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) > [nodesInfoRequestError](./kibana-plugin-core-server.elasticsearchstatusmeta.nodesinforequesterror.md)
+
+## ElasticsearchStatusMeta.nodesInfoRequestError property
+
+Signature:
+
+```typescript
+nodesInfoRequestError?: NodesVersionCompatibility['nodesInfoRequestError'];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.ilegacyclusterclient.md b/docs/development/core/server/kibana-plugin-core-server.ilegacyclusterclient.md
index b5fbb3d54b972a0..d1e87feba0f0334 100644
--- a/docs/development/core/server/kibana-plugin-core-server.ilegacyclusterclient.md
+++ b/docs/development/core/server/kibana-plugin-core-server.ilegacyclusterclient.md
@@ -6,7 +6,7 @@
> Warning: This API is now obsolete.
>
-> Use [IClusterClient](./kibana-plugin-core-server.iclusterclient.md).
+> Use [IClusterClient](./kibana-plugin-core-server.iclusterclient.md). 7.16
>
Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via `asScoped(...)`).
diff --git a/docs/development/core/server/kibana-plugin-core-server.ilegacycustomclusterclient.md b/docs/development/core/server/kibana-plugin-core-server.ilegacycustomclusterclient.md
index 4da121984d08470..c004ad2548802ff 100644
--- a/docs/development/core/server/kibana-plugin-core-server.ilegacycustomclusterclient.md
+++ b/docs/development/core/server/kibana-plugin-core-server.ilegacycustomclusterclient.md
@@ -6,7 +6,7 @@
> Warning: This API is now obsolete.
>
-> Use [ICustomClusterClient](./kibana-plugin-core-server.icustomclusterclient.md).
+> Use [ICustomClusterClient](./kibana-plugin-core-server.icustomclusterclient.md). 7.16
>
Represents an Elasticsearch cluster API client created by a plugin. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via `asScoped(...)`).
diff --git a/docs/development/core/server/kibana-plugin-core-server.ilegacyscopedclusterclient.md b/docs/development/core/server/kibana-plugin-core-server.ilegacyscopedclusterclient.md
index 51d0b2e4882cb6b..8e7ecdb9f7ec2e5 100644
--- a/docs/development/core/server/kibana-plugin-core-server.ilegacyscopedclusterclient.md
+++ b/docs/development/core/server/kibana-plugin-core-server.ilegacyscopedclusterclient.md
@@ -6,7 +6,7 @@
> Warning: This API is now obsolete.
>
-> Use [IScopedClusterClient](./kibana-plugin-core-server.iscopedclusterclient.md).
+> Use [IScopedClusterClient](./kibana-plugin-core-server.iscopedclusterclient.md). 7.16
>
Serves the same purpose as "normal" `ClusterClient` but exposes additional `callAsCurrentUser` method that doesn't use credentials of the Kibana internal user (as `callAsInternalUser` does) to request Elasticsearch API, but rather passes HTTP headers extracted from the current user request to the API.
diff --git a/docs/development/core/server/kibana-plugin-core-server.indexsettingsdeprecationinfo.md b/docs/development/core/server/kibana-plugin-core-server.indexsettingsdeprecationinfo.md
index 706898c4ad9aa28..9103f9cfc674052 100644
--- a/docs/development/core/server/kibana-plugin-core-server.indexsettingsdeprecationinfo.md
+++ b/docs/development/core/server/kibana-plugin-core-server.indexsettingsdeprecationinfo.md
@@ -6,6 +6,7 @@
> Warning: This API is now obsolete.
>
+> 7.16
>
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.isavedobjectspointintimefinder.find.md b/docs/development/core/server/kibana-plugin-core-server.isavedobjectspointintimefinder.find.md
index 1755ff40c2bc06e..29d4668becffce5 100644
--- a/docs/development/core/server/kibana-plugin-core-server.isavedobjectspointintimefinder.find.md
+++ b/docs/development/core/server/kibana-plugin-core-server.isavedobjectspointintimefinder.find.md
@@ -9,5 +9,5 @@ An async generator which wraps calls to `savedObjectsClient.find` and iterates o
Signature:
```typescript
-find: () => AsyncGenerator;
+find: () => AsyncGenerator>;
```
diff --git a/docs/development/core/server/kibana-plugin-core-server.isavedobjectspointintimefinder.md b/docs/development/core/server/kibana-plugin-core-server.isavedobjectspointintimefinder.md
index 4686df18e01343a..950d6c078654cad 100644
--- a/docs/development/core/server/kibana-plugin-core-server.isavedobjectspointintimefinder.md
+++ b/docs/development/core/server/kibana-plugin-core-server.isavedobjectspointintimefinder.md
@@ -8,7 +8,7 @@
Signature:
```typescript
-export interface ISavedObjectsPointInTimeFinder
+export interface ISavedObjectsPointInTimeFinder
```
## Properties
@@ -16,5 +16,5 @@ export interface ISavedObjectsPointInTimeFinder
| Property | Type | Description |
| --- | --- | --- |
| [close](./kibana-plugin-core-server.isavedobjectspointintimefinder.close.md) | () => Promise<void> | Closes the Point-In-Time associated with this finder instance.Once you have retrieved all of the results you need, it is recommended to call close() to clean up the PIT and prevent Elasticsearch from consuming resources unnecessarily. This is only required if you are done iterating and have not yet paged through all of the results: the PIT will automatically be closed for you once you reach the last page of results, or if the underlying call to find fails for any reason. |
-| [find](./kibana-plugin-core-server.isavedobjectspointintimefinder.find.md) | () => AsyncGenerator<SavedObjectsFindResponse> | An async generator which wraps calls to savedObjectsClient.find and iterates over multiple pages of results using _pit and search_after. This will open a new Point-In-Time (PIT), and continue paging until a set of results is received that's smaller than the designated perPage size. |
+| [find](./kibana-plugin-core-server.isavedobjectspointintimefinder.find.md) | () => AsyncGenerator<SavedObjectsFindResponse<T, A>> | An async generator which wraps calls to savedObjectsClient.find and iterates over multiple pages of results using _pit and search_after. This will open a new Point-In-Time (PIT), and continue paging until a set of results is received that's smaller than the designated perPage size. |
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyapicaller.md b/docs/development/core/server/kibana-plugin-core-server.legacyapicaller.md
index 168209659046e7d..2378e61484da5ab 100644
--- a/docs/development/core/server/kibana-plugin-core-server.legacyapicaller.md
+++ b/docs/development/core/server/kibana-plugin-core-server.legacyapicaller.md
@@ -6,6 +6,7 @@
> Warning: This API is now obsolete.
>
+> 7.16
>
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacycallapioptions.md b/docs/development/core/server/kibana-plugin-core-server.legacycallapioptions.md
index 40def157114ef22..219180af26fd83f 100644
--- a/docs/development/core/server/kibana-plugin-core-server.legacycallapioptions.md
+++ b/docs/development/core/server/kibana-plugin-core-server.legacycallapioptions.md
@@ -6,6 +6,7 @@
> Warning: This API is now obsolete.
>
+> 7.16
>
The set of options that defines how API call should be made and result be processed.
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient.md b/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient.md
index 0872e5ba7c2197e..05855c31477c3e2 100644
--- a/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient.md
+++ b/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient.md
@@ -6,7 +6,7 @@
> Warning: This API is now obsolete.
>
-> Use [IClusterClient](./kibana-plugin-core-server.iclusterclient.md).
+> Use [IClusterClient](./kibana-plugin-core-server.iclusterclient.md). 7.16
>
Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via `asScoped(...)`).
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearcherror.md b/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearcherror.md
index 7c53356615ee9fa..7cf696ad8d73f2e 100644
--- a/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearcherror.md
+++ b/docs/development/core/server/kibana-plugin-core-server.legacyelasticsearcherror.md
@@ -4,7 +4,7 @@
## LegacyElasticsearchError interface
-@deprecated. The new elasticsearch client doesn't wrap errors anymore.
+@deprecated. The new elasticsearch client doesn't wrap errors anymore. 7.16
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.callascurrentuser.md b/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.callascurrentuser.md
index 7517acc59ac8011..0f2d653e41a5545 100644
--- a/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.callascurrentuser.md
+++ b/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.callascurrentuser.md
@@ -6,7 +6,7 @@
> Warning: This API is now obsolete.
>
-> Use [IScopedClusterClient.asCurrentUser](./kibana-plugin-core-server.iscopedclusterclient.ascurrentuser.md).
+> Use [IScopedClusterClient.asCurrentUser](./kibana-plugin-core-server.iscopedclusterclient.ascurrentuser.md). 7.16
>
Calls specified `endpoint` with provided `clientParams` on behalf of the user initiated request to the Kibana server (via HTTP request headers). See [LegacyAPICaller](./kibana-plugin-core-server.legacyapicaller.md).
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.callasinternaluser.md b/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.callasinternaluser.md
index b683d3945f9ff02..2c184b0fde5b329 100644
--- a/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.callasinternaluser.md
+++ b/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.callasinternaluser.md
@@ -6,7 +6,7 @@
> Warning: This API is now obsolete.
>
-> Use [IScopedClusterClient.asInternalUser](./kibana-plugin-core-server.iscopedclusterclient.asinternaluser.md).
+> Use [IScopedClusterClient.asInternalUser](./kibana-plugin-core-server.iscopedclusterclient.asinternaluser.md). 7.16
>
Calls specified `endpoint` with provided `clientParams` on behalf of the Kibana internal user. See [LegacyAPICaller](./kibana-plugin-core-server.legacyapicaller.md).
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.md b/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.md
index 6b6649e833a92d4..6678c3bc16d531d 100644
--- a/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.md
+++ b/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.md
@@ -6,7 +6,7 @@
> Warning: This API is now obsolete.
>
-> Use [scoped cluster client](./kibana-plugin-core-server.iscopedclusterclient.md).
+> Use [scoped cluster client](./kibana-plugin-core-server.iscopedclusterclient.md). 7.16
>
Serves the same purpose as the normal [cluster client](./kibana-plugin-core-server.iclusterclient.md) but exposes an additional `asCurrentUser` method that doesn't use credentials of the Kibana internal user (as `asInternalUser` does) to request Elasticsearch API, but rather passes HTTP headers extracted from the current user request to the API instead.
diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md
index 4df8d074ba9c87d..d638b84224e2345 100644
--- a/docs/development/core/server/kibana-plugin-core-server.md
+++ b/docs/development/core/server/kibana-plugin-core-server.md
@@ -108,7 +108,7 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [KibanaRequestRoute](./kibana-plugin-core-server.kibanarequestroute.md) | Request specific route information exposed to a handler. |
| [LegacyAPICaller](./kibana-plugin-core-server.legacyapicaller.md) | |
| [LegacyCallAPIOptions](./kibana-plugin-core-server.legacycallapioptions.md) | The set of options that defines how API call should be made and result be processed. |
-| [LegacyElasticsearchError](./kibana-plugin-core-server.legacyelasticsearcherror.md) | @deprecated. The new elasticsearch client doesn't wrap errors anymore. |
+| [LegacyElasticsearchError](./kibana-plugin-core-server.legacyelasticsearcherror.md) | @deprecated. The new elasticsearch client doesn't wrap errors anymore. 7.16 |
| [LegacyRequest](./kibana-plugin-core-server.legacyrequest.md) | |
| [LoggerContextConfigInput](./kibana-plugin-core-server.loggercontextconfiginput.md) | |
| [LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md) | Provides APIs to plugins for customizing the plugin's logger. |
@@ -144,8 +144,7 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [SavedObjectMigrationContext](./kibana-plugin-core-server.savedobjectmigrationcontext.md) | Migration context provided when invoking a [migration handler](./kibana-plugin-core-server.savedobjectmigrationfn.md) |
| [SavedObjectMigrationMap](./kibana-plugin-core-server.savedobjectmigrationmap.md) | A map of [migration functions](./kibana-plugin-core-server.savedobjectmigrationfn.md) to be used for a given type. The map's keys must be valid semver versions, and they cannot exceed the current Kibana version.For a given document, only migrations with a higher version number than that of the document will be applied. Migrations are executed in order, starting from the lowest version and ending with the highest one. |
| [SavedObjectReference](./kibana-plugin-core-server.savedobjectreference.md) | A reference to another saved object. |
-| [SavedObjectsAddToNamespacesOptions](./kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.md) | |
-| [SavedObjectsAddToNamespacesResponse](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md) | |
+| [SavedObjectReferenceWithContext](./kibana-plugin-core-server.savedobjectreferencewithcontext.md) | A returned input object or one of its references, with additional context. |
| [SavedObjectsBaseOptions](./kibana-plugin-core-server.savedobjectsbaseoptions.md) | |
| [SavedObjectsBulkCreateObject](./kibana-plugin-core-server.savedobjectsbulkcreateobject.md) | |
| [SavedObjectsBulkGetObject](./kibana-plugin-core-server.savedobjectsbulkgetobject.md) | |
@@ -158,13 +157,14 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [SavedObjectsClientProviderOptions](./kibana-plugin-core-server.savedobjectsclientprovideroptions.md) | Options to control the creation of the Saved Objects Client. |
| [SavedObjectsClientWrapperOptions](./kibana-plugin-core-server.savedobjectsclientwrapperoptions.md) | Options passed to each SavedObjectsClientWrapperFactory to aid in creating the wrapper instance. |
| [SavedObjectsClosePointInTimeResponse](./kibana-plugin-core-server.savedobjectsclosepointintimeresponse.md) | |
+| [SavedObjectsCollectMultiNamespaceReferencesObject](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.md) | An object to collect references for. It must be a multi-namespace type (in other words, the object type must be registered with the namespaceType: 'multi' or namespaceType: 'multi-isolated' option).Note: if options.purpose is 'updateObjectsSpaces', it must be a shareable type (in other words, the object type must be registered with the namespaceType: 'multi'). |
+| [SavedObjectsCollectMultiNamespaceReferencesOptions](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.md) | Options for collecting references. |
+| [SavedObjectsCollectMultiNamespaceReferencesResponse](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.md) | The response when object references are collected. |
| [SavedObjectsComplexFieldMapping](./kibana-plugin-core-server.savedobjectscomplexfieldmapping.md) | See [SavedObjectsFieldMapping](./kibana-plugin-core-server.savedobjectsfieldmapping.md) for documentation. |
| [SavedObjectsCoreFieldMapping](./kibana-plugin-core-server.savedobjectscorefieldmapping.md) | See [SavedObjectsFieldMapping](./kibana-plugin-core-server.savedobjectsfieldmapping.md) for documentation. |
| [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md) | |
| [SavedObjectsCreatePointInTimeFinderDependencies](./kibana-plugin-core-server.savedobjectscreatepointintimefinderdependencies.md) | |
| [SavedObjectsDeleteByNamespaceOptions](./kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.md) | |
-| [SavedObjectsDeleteFromNamespacesOptions](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.md) | |
-| [SavedObjectsDeleteFromNamespacesResponse](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md) | |
| [SavedObjectsDeleteOptions](./kibana-plugin-core-server.savedobjectsdeleteoptions.md) | |
| [SavedObjectsExportByObjectOptions](./kibana-plugin-core-server.savedobjectsexportbyobjectoptions.md) | Options for the [export by objects API](./kibana-plugin-core-server.savedobjectsexporter.exportbyobjects.md) |
| [SavedObjectsExportByTypeOptions](./kibana-plugin-core-server.savedobjectsexportbytypeoptions.md) | Options for the [export by type API](./kibana-plugin-core-server.savedobjectsexporter.exportbytypes.md) |
@@ -208,6 +208,10 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [SavedObjectsType](./kibana-plugin-core-server.savedobjectstype.md) | |
| [SavedObjectsTypeManagementDefinition](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.md) | Configuration options for the [type](./kibana-plugin-core-server.savedobjectstype.md)'s management section. |
| [SavedObjectsTypeMappingDefinition](./kibana-plugin-core-server.savedobjectstypemappingdefinition.md) | Describe a saved object type mapping. |
+| [SavedObjectsUpdateObjectsSpacesObject](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.md) | An object that should have its spaces updated. |
+| [SavedObjectsUpdateObjectsSpacesOptions](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.md) | Options for the update operation. |
+| [SavedObjectsUpdateObjectsSpacesResponse](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.md) | The response when objects' spaces are updated. |
+| [SavedObjectsUpdateObjectsSpacesResponseObject](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.md) | Details about a specific object's update result. |
| [SavedObjectsUpdateOptions](./kibana-plugin-core-server.savedobjectsupdateoptions.md) | |
| [SavedObjectsUpdateResponse](./kibana-plugin-core-server.savedobjectsupdateresponse.md) | |
| [SearchResponse](./kibana-plugin-core-server.searchresponse.md) | |
diff --git a/docs/development/core/server/kibana-plugin-core-server.migration_assistance_index_action.md b/docs/development/core/server/kibana-plugin-core-server.migration_assistance_index_action.md
index a924f0cea6b6b0c..ea0a277931eaf18 100644
--- a/docs/development/core/server/kibana-plugin-core-server.migration_assistance_index_action.md
+++ b/docs/development/core/server/kibana-plugin-core-server.migration_assistance_index_action.md
@@ -6,6 +6,7 @@
> Warning: This API is now obsolete.
>
+> 7.16
>
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.migration_deprecation_level.md b/docs/development/core/server/kibana-plugin-core-server.migration_deprecation_level.md
index 0fcae8c847cb40c..f71e6e78a4c34ba 100644
--- a/docs/development/core/server/kibana-plugin-core-server.migration_deprecation_level.md
+++ b/docs/development/core/server/kibana-plugin-core-server.migration_deprecation_level.md
@@ -6,6 +6,7 @@
> Warning: This API is now obsolete.
>
+> 7.16
>
Signature:
diff --git a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md
index 6fcfacc3bc9085e..cbdac9d5455b01c 100644
--- a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md
+++ b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md
@@ -18,5 +18,6 @@ export interface NodesVersionCompatibility
| [isCompatible](./kibana-plugin-core-server.nodesversioncompatibility.iscompatible.md) | boolean | |
| [kibanaVersion](./kibana-plugin-core-server.nodesversioncompatibility.kibanaversion.md) | string | |
| [message](./kibana-plugin-core-server.nodesversioncompatibility.message.md) | string | |
+| [nodesInfoRequestError](./kibana-plugin-core-server.nodesversioncompatibility.nodesinforequesterror.md) | Error | |
| [warningNodes](./kibana-plugin-core-server.nodesversioncompatibility.warningnodes.md) | NodeInfo[] | |
diff --git a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.nodesinforequesterror.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.nodesinforequesterror.md
new file mode 100644
index 000000000000000..aa9421afed6e84a
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.nodesinforequesterror.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [nodesInfoRequestError](./kibana-plugin-core-server.nodesversioncompatibility.nodesinforequesterror.md)
+
+## NodesVersionCompatibility.nodesInfoRequestError property
+
+Signature:
+
+```typescript
+nodesInfoRequestError?: Error;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.includenamespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.includenamespaces.md
new file mode 100644
index 000000000000000..8ac532c601efcb6
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.includenamespaces.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectExportBaseOptions](./kibana-plugin-core-server.savedobjectexportbaseoptions.md) > [includeNamespaces](./kibana-plugin-core-server.savedobjectexportbaseoptions.includenamespaces.md)
+
+## SavedObjectExportBaseOptions.includeNamespaces property
+
+Flag to also include namespace information in the export stream. By default, namespace information is not included in exported objects. This is only intended to be used internally during copy-to-space operations, and it is not exposed as an option for the external HTTP route for exports.
+
+Signature:
+
+```typescript
+includeNamespaces?: boolean;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.md
index 0e8fa73039d4008..cd0c352086425c2 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.md
@@ -16,6 +16,7 @@ export interface SavedObjectExportBaseOptions
| Property | Type | Description |
| --- | --- | --- |
| [excludeExportDetails](./kibana-plugin-core-server.savedobjectexportbaseoptions.excludeexportdetails.md) | boolean | flag to not append [export details](./kibana-plugin-core-server.savedobjectsexportresultdetails.md) to the end of the export stream. |
+| [includeNamespaces](./kibana-plugin-core-server.savedobjectexportbaseoptions.includenamespaces.md) | boolean | Flag to also include namespace information in the export stream. By default, namespace information is not included in exported objects. This is only intended to be used internally during copy-to-space operations, and it is not exposed as an option for the external HTTP route for exports. |
| [includeReferencesDeep](./kibana-plugin-core-server.savedobjectexportbaseoptions.includereferencesdeep.md) | boolean | flag to also include all related saved objects in the export stream. |
| [namespace](./kibana-plugin-core-server.savedobjectexportbaseoptions.namespace.md) | string | optional namespace to override the namespace used by the savedObjectsClient. |
| [request](./kibana-plugin-core-server.savedobjectexportbaseoptions.request.md) | KibanaRequest | The http request initiating the export. |
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.converttomultinamespacetypeversion.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.converttomultinamespacetypeversion.md
index 2a30693f4da84a2..9fe43a2f3f477d8 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.converttomultinamespacetypeversion.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.converttomultinamespacetypeversion.md
@@ -9,5 +9,5 @@ The version in which this object type is being converted to a multi-namespace ty
Signature:
```typescript
-convertToMultiNamespaceTypeVersion?: string;
+readonly convertToMultiNamespaceTypeVersion?: string;
```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.log.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.log.md
index a1b3378afc53b2f..20a0e99275a39e5 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.log.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.log.md
@@ -9,5 +9,5 @@ logger instance to be used by the migration handler
Signature:
```typescript
-log: SavedObjectsMigrationLogger;
+readonly log: SavedObjectsMigrationLogger;
```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.migrationversion.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.migrationversion.md
index 7b20ae41048f666..a1c2717e6e4a02f 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.migrationversion.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationcontext.migrationversion.md
@@ -9,5 +9,5 @@ The migration version that this migration function is defined for
Signature:
```typescript
-migrationVersion: string;
+readonly migrationVersion: string;
```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.id.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.id.md
new file mode 100644
index 000000000000000..7ef1a2fb1bd411d
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.id.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-server.savedobjectreferencewithcontext.md) > [id](./kibana-plugin-core-server.savedobjectreferencewithcontext.id.md)
+
+## SavedObjectReferenceWithContext.id property
+
+The ID of the referenced object
+
+Signature:
+
+```typescript
+id: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.inboundreferences.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.inboundreferences.md
new file mode 100644
index 000000000000000..058c27032d06534
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.inboundreferences.md
@@ -0,0 +1,17 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-server.savedobjectreferencewithcontext.md) > [inboundReferences](./kibana-plugin-core-server.savedobjectreferencewithcontext.inboundreferences.md)
+
+## SavedObjectReferenceWithContext.inboundReferences property
+
+References to this object; note that this does not contain \_all inbound references everywhere for this object\_, it only contains inbound references for the scope of this operation
+
+Signature:
+
+```typescript
+inboundReferences: Array<{
+ type: string;
+ id: string;
+ name: string;
+ }>;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.ismissing.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.ismissing.md
new file mode 100644
index 000000000000000..d46d5a6bf2a0a8d
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.ismissing.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-server.savedobjectreferencewithcontext.md) > [isMissing](./kibana-plugin-core-server.savedobjectreferencewithcontext.ismissing.md)
+
+## SavedObjectReferenceWithContext.isMissing property
+
+Whether or not this object or reference is missing
+
+Signature:
+
+```typescript
+isMissing?: boolean;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.md
new file mode 100644
index 000000000000000..1f8b33c6e94e8d2
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.md
@@ -0,0 +1,25 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-server.savedobjectreferencewithcontext.md)
+
+## SavedObjectReferenceWithContext interface
+
+A returned input object or one of its references, with additional context.
+
+Signature:
+
+```typescript
+export interface SavedObjectReferenceWithContext
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [id](./kibana-plugin-core-server.savedobjectreferencewithcontext.id.md) | string | The ID of the referenced object |
+| [inboundReferences](./kibana-plugin-core-server.savedobjectreferencewithcontext.inboundreferences.md) | Array<{ type: string; id: string; name: string; }> | References to this object; note that this does not contain \_all inbound references everywhere for this object\_, it only contains inbound references for the scope of this operation |
+| [isMissing](./kibana-plugin-core-server.savedobjectreferencewithcontext.ismissing.md) | boolean | Whether or not this object or reference is missing |
+| [spaces](./kibana-plugin-core-server.savedobjectreferencewithcontext.spaces.md) | string[] | The space(s) that the referenced object exists in |
+| [spacesWithMatchingAliases](./kibana-plugin-core-server.savedobjectreferencewithcontext.spaceswithmatchingaliases.md) | string[] | The space(s) that legacy URL aliases matching this type/id exist in |
+| [type](./kibana-plugin-core-server.savedobjectreferencewithcontext.type.md) | string | The type of the referenced object |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.spaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.spaces.md
new file mode 100644
index 000000000000000..2c2114103b29a7b
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.spaces.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-server.savedobjectreferencewithcontext.md) > [spaces](./kibana-plugin-core-server.savedobjectreferencewithcontext.spaces.md)
+
+## SavedObjectReferenceWithContext.spaces property
+
+The space(s) that the referenced object exists in
+
+Signature:
+
+```typescript
+spaces: string[];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.spaceswithmatchingaliases.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.spaceswithmatchingaliases.md
new file mode 100644
index 000000000000000..07f4158a8495047
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.spaceswithmatchingaliases.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-server.savedobjectreferencewithcontext.md) > [spacesWithMatchingAliases](./kibana-plugin-core-server.savedobjectreferencewithcontext.spaceswithmatchingaliases.md)
+
+## SavedObjectReferenceWithContext.spacesWithMatchingAliases property
+
+The space(s) that legacy URL aliases matching this type/id exist in
+
+Signature:
+
+```typescript
+spacesWithMatchingAliases?: string[];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.type.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.type.md
new file mode 100644
index 000000000000000..118d9744e4276e2
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectreferencewithcontext.type.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectReferenceWithContext](./kibana-plugin-core-server.savedobjectreferencewithcontext.md) > [type](./kibana-plugin-core-server.savedobjectreferencewithcontext.type.md)
+
+## SavedObjectReferenceWithContext.type property
+
+The type of the referenced object
+
+Signature:
+
+```typescript
+type: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.md
deleted file mode 100644
index 711588bdd608cd0..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsAddToNamespacesOptions](./kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.md)
-
-## SavedObjectsAddToNamespacesOptions interface
-
-
-Signature:
-
-```typescript
-export interface SavedObjectsAddToNamespacesOptions extends SavedObjectsBaseOptions
-```
-
-## Properties
-
-| Property | Type | Description |
-| --- | --- | --- |
-| [refresh](./kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.refresh.md) | MutatingOperationRefreshSetting | The Elasticsearch Refresh setting for this operation |
-| [version](./kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.version.md) | string | An opaque version number which changes on each successful write operation. Can be used for implementing optimistic concurrency control. |
-
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.refresh.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.refresh.md
deleted file mode 100644
index c0a1008ab533161..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.refresh.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsAddToNamespacesOptions](./kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.md) > [refresh](./kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.refresh.md)
-
-## SavedObjectsAddToNamespacesOptions.refresh property
-
-The Elasticsearch Refresh setting for this operation
-
-Signature:
-
-```typescript
-refresh?: MutatingOperationRefreshSetting;
-```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.version.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.version.md
deleted file mode 100644
index 9432b4bf80da6c6..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.version.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsAddToNamespacesOptions](./kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.md) > [version](./kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.version.md)
-
-## SavedObjectsAddToNamespacesOptions.version property
-
-An opaque version number which changes on each successful write operation. Can be used for implementing optimistic concurrency control.
-
-Signature:
-
-```typescript
-version?: string;
-```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md
deleted file mode 100644
index 306f502f0b0b3a4..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsAddToNamespacesResponse](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md)
-
-## SavedObjectsAddToNamespacesResponse interface
-
-
-Signature:
-
-```typescript
-export interface SavedObjectsAddToNamespacesResponse
-```
-
-## Properties
-
-| Property | Type | Description |
-| --- | --- | --- |
-| [namespaces](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.namespaces.md) | string[] | The namespaces the object exists in after this operation is complete. |
-
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.namespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.namespaces.md
deleted file mode 100644
index 4fc2e376304d485..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.namespaces.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsAddToNamespacesResponse](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md) > [namespaces](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.namespaces.md)
-
-## SavedObjectsAddToNamespacesResponse.namespaces property
-
-The namespaces the object exists in after this operation is complete.
-
-Signature:
-
-```typescript
-namespaces: string[];
-```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.addtonamespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.addtonamespaces.md
deleted file mode 100644
index 567390faba9b279..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.addtonamespaces.md
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsClient](./kibana-plugin-core-server.savedobjectsclient.md) > [addToNamespaces](./kibana-plugin-core-server.savedobjectsclient.addtonamespaces.md)
-
-## SavedObjectsClient.addToNamespaces() method
-
-Adds namespaces to a SavedObject
-
-Signature:
-
-```typescript
-addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise;
-```
-
-## Parameters
-
-| Parameter | Type | Description |
-| --- | --- | --- |
-| type | string | |
-| id | string | |
-| namespaces | string[] | |
-| options | SavedObjectsAddToNamespacesOptions | |
-
-Returns:
-
-`Promise`
-
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.collectmultinamespacereferences.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.collectmultinamespacereferences.md
new file mode 100644
index 000000000000000..155167d32a73834
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.collectmultinamespacereferences.md
@@ -0,0 +1,25 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsClient](./kibana-plugin-core-server.savedobjectsclient.md) > [collectMultiNamespaceReferences](./kibana-plugin-core-server.savedobjectsclient.collectmultinamespacereferences.md)
+
+## SavedObjectsClient.collectMultiNamespaceReferences() method
+
+Gets all references and transitive references of the listed objects. Ignores any object that is not a multi-namespace type.
+
+Signature:
+
+```typescript
+collectMultiNamespaceReferences(objects: SavedObjectsCollectMultiNamespaceReferencesObject[], options?: SavedObjectsCollectMultiNamespaceReferencesOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| objects | SavedObjectsCollectMultiNamespaceReferencesObject[] | |
+| options | SavedObjectsCollectMultiNamespaceReferencesOptions | |
+
+Returns:
+
+`Promise`
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.createpointintimefinder.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.createpointintimefinder.md
index 8afd9634645741c..39d09807e4f3b08 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.createpointintimefinder.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.createpointintimefinder.md
@@ -15,7 +15,7 @@ Once you have retrieved all of the results you need, it is recommended to call `
Signature:
```typescript
-createPointInTimeFinder(findOptions: SavedObjectsCreatePointInTimeFinderOptions, dependencies?: SavedObjectsCreatePointInTimeFinderDependencies): ISavedObjectsPointInTimeFinder;
+createPointInTimeFinder(findOptions: SavedObjectsCreatePointInTimeFinderOptions, dependencies?: SavedObjectsCreatePointInTimeFinderDependencies): ISavedObjectsPointInTimeFinder;
```
## Parameters
@@ -27,7 +27,7 @@ createPointInTimeFinder(findOptions: SavedObjectsCreatePointInTimeFinderOptions,
Returns:
-`ISavedObjectsPointInTimeFinder`
+`ISavedObjectsPointInTimeFinder`
## Example
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.deletefromnamespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.deletefromnamespaces.md
deleted file mode 100644
index 18ef5c3e6350c0a..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.deletefromnamespaces.md
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsClient](./kibana-plugin-core-server.savedobjectsclient.md) > [deleteFromNamespaces](./kibana-plugin-core-server.savedobjectsclient.deletefromnamespaces.md)
-
-## SavedObjectsClient.deleteFromNamespaces() method
-
-Removes namespaces from a SavedObject
-
-Signature:
-
-```typescript
-deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise;
-```
-
-## Parameters
-
-| Parameter | Type | Description |
-| --- | --- | --- |
-| type | string | |
-| id | string | |
-| namespaces | string[] | |
-| options | SavedObjectsDeleteFromNamespacesOptions | |
-
-Returns:
-
-`Promise`
-
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md
index 95c2251f72c9004..2e293889b179445 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md
@@ -25,20 +25,20 @@ The constructor for this class is marked as internal. Third-party code should no
| Method | Modifiers | Description |
| --- | --- | --- |
-| [addToNamespaces(type, id, namespaces, options)](./kibana-plugin-core-server.savedobjectsclient.addtonamespaces.md) | | Adds namespaces to a SavedObject |
| [bulkCreate(objects, options)](./kibana-plugin-core-server.savedobjectsclient.bulkcreate.md) | | Persists multiple documents batched together as a single request |
| [bulkGet(objects, options)](./kibana-plugin-core-server.savedobjectsclient.bulkget.md) | | Returns an array of objects by id |
| [bulkUpdate(objects, options)](./kibana-plugin-core-server.savedobjectsclient.bulkupdate.md) | | Bulk Updates multiple SavedObject at once |
| [checkConflicts(objects, options)](./kibana-plugin-core-server.savedobjectsclient.checkconflicts.md) | | Check what conflicts will result when creating a given array of saved objects. This includes "unresolvable conflicts", which are multi-namespace objects that exist in a different namespace; such conflicts cannot be resolved/overwritten. |
| [closePointInTime(id, options)](./kibana-plugin-core-server.savedobjectsclient.closepointintime.md) | | Closes a Point In Time (PIT) by ID. This simply proxies the request to ES via the Elasticsearch client, and is included in the Saved Objects Client as a convenience for consumers who are using [SavedObjectsClient.openPointInTimeForType()](./kibana-plugin-core-server.savedobjectsclient.openpointintimefortype.md).Only use this API if you have an advanced use case that's not solved by the [SavedObjectsClient.createPointInTimeFinder()](./kibana-plugin-core-server.savedobjectsclient.createpointintimefinder.md) method. |
+| [collectMultiNamespaceReferences(objects, options)](./kibana-plugin-core-server.savedobjectsclient.collectmultinamespacereferences.md) | | Gets all references and transitive references of the listed objects. Ignores any object that is not a multi-namespace type. |
| [create(type, attributes, options)](./kibana-plugin-core-server.savedobjectsclient.create.md) | | Persists a SavedObject |
| [createPointInTimeFinder(findOptions, dependencies)](./kibana-plugin-core-server.savedobjectsclient.createpointintimefinder.md) | | Returns a [ISavedObjectsPointInTimeFinder](./kibana-plugin-core-server.isavedobjectspointintimefinder.md) to help page through large sets of saved objects. We strongly recommend using this API for any find queries that might return more than 1000 saved objects, however this API is only intended for use in server-side "batch" processing of objects where you are collecting all objects in memory or streaming them back to the client.Do NOT use this API in a route handler to facilitate paging through saved objects on the client-side unless you are streaming all of the results back to the client at once. Because the returned generator is stateful, you cannot rely on subsequent http requests retrieving new pages from the same Kibana server in multi-instance deployments.The generator wraps calls to [SavedObjectsClient.find()](./kibana-plugin-core-server.savedobjectsclient.find.md) and iterates over multiple pages of results using _pit and search_after. This will open a new Point-In-Time (PIT), and continue paging until a set of results is received that's smaller than the designated perPage.Once you have retrieved all of the results you need, it is recommended to call close() to clean up the PIT and prevent Elasticsearch from consuming resources unnecessarily. This is only required if you are done iterating and have not yet paged through all of the results: the PIT will automatically be closed for you once you reach the last page of results, or if the underlying call to find fails for any reason. |
| [delete(type, id, options)](./kibana-plugin-core-server.savedobjectsclient.delete.md) | | Deletes a SavedObject |
-| [deleteFromNamespaces(type, id, namespaces, options)](./kibana-plugin-core-server.savedobjectsclient.deletefromnamespaces.md) | | Removes namespaces from a SavedObject |
| [find(options)](./kibana-plugin-core-server.savedobjectsclient.find.md) | | Find all SavedObjects matching the search query |
| [get(type, id, options)](./kibana-plugin-core-server.savedobjectsclient.get.md) | | Retrieves a single object |
| [openPointInTimeForType(type, options)](./kibana-plugin-core-server.savedobjectsclient.openpointintimefortype.md) | | Opens a Point In Time (PIT) against the indices for the specified Saved Object types. The returned id can then be passed to [SavedObjectsClient.find()](./kibana-plugin-core-server.savedobjectsclient.find.md) to search against that PIT.Only use this API if you have an advanced use case that's not solved by the [SavedObjectsClient.createPointInTimeFinder()](./kibana-plugin-core-server.savedobjectsclient.createpointintimefinder.md) method. |
| [removeReferencesTo(type, id, options)](./kibana-plugin-core-server.savedobjectsclient.removereferencesto.md) | | Updates all objects containing a reference to the given {type, id} tuple to remove the said reference. |
| [resolve(type, id, options)](./kibana-plugin-core-server.savedobjectsclient.resolve.md) | | Resolves a single object, using any legacy URL alias if it exists |
| [update(type, id, attributes, options)](./kibana-plugin-core-server.savedobjectsclient.update.md) | | Updates an SavedObject |
+| [updateObjectsSpaces(objects, spacesToAdd, spacesToRemove, options)](./kibana-plugin-core-server.savedobjectsclient.updateobjectsspaces.md) | | Updates one or more objects to add and/or remove them from specified spaces. |
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.updateobjectsspaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.updateobjectsspaces.md
new file mode 100644
index 000000000000000..7ababbbe1f535e0
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.updateobjectsspaces.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsClient](./kibana-plugin-core-server.savedobjectsclient.md) > [updateObjectsSpaces](./kibana-plugin-core-server.savedobjectsclient.updateobjectsspaces.md)
+
+## SavedObjectsClient.updateObjectsSpaces() method
+
+Updates one or more objects to add and/or remove them from specified spaces.
+
+Signature:
+
+```typescript
+updateObjectsSpaces(objects: SavedObjectsUpdateObjectsSpacesObject[], spacesToAdd: string[], spacesToRemove: string[], options?: SavedObjectsUpdateObjectsSpacesOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| objects | SavedObjectsUpdateObjectsSpacesObject[] | |
+| spacesToAdd | string[] | |
+| spacesToRemove | string[] | |
+| options | SavedObjectsUpdateObjectsSpacesOptions | |
+
+Returns:
+
+`Promise`
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.id.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.id.md
new file mode 100644
index 000000000000000..21522a0f32d6d00
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.id.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCollectMultiNamespaceReferencesObject](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.md) > [id](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.id.md)
+
+## SavedObjectsCollectMultiNamespaceReferencesObject.id property
+
+Signature:
+
+```typescript
+id: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.md
new file mode 100644
index 000000000000000..e675658f2bf7666
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCollectMultiNamespaceReferencesObject](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.md)
+
+## SavedObjectsCollectMultiNamespaceReferencesObject interface
+
+An object to collect references for. It must be a multi-namespace type (in other words, the object type must be registered with the `namespaceType: 'multi'` or `namespaceType: 'multi-isolated'` option).
+
+Note: if options.purpose is 'updateObjectsSpaces', it must be a shareable type (in other words, the object type must be registered with the `namespaceType: 'multi'`).
+
+Signature:
+
+```typescript
+export interface SavedObjectsCollectMultiNamespaceReferencesObject
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [id](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.id.md) | string | |
+| [type](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.type.md) | string | |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.type.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.type.md
new file mode 100644
index 000000000000000..c376a9e4258c803
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCollectMultiNamespaceReferencesObject](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.md) > [type](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesobject.type.md)
+
+## SavedObjectsCollectMultiNamespaceReferencesObject.type property
+
+Signature:
+
+```typescript
+type: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.md
new file mode 100644
index 000000000000000..9311a6626975339
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCollectMultiNamespaceReferencesOptions](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.md)
+
+## SavedObjectsCollectMultiNamespaceReferencesOptions interface
+
+Options for collecting references.
+
+Signature:
+
+```typescript
+export interface SavedObjectsCollectMultiNamespaceReferencesOptions extends SavedObjectsBaseOptions
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [purpose](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.purpose.md) | 'collectMultiNamespaceReferences' | 'updateObjectsSpaces' | Optional purpose used to determine filtering and authorization checks; default is 'collectMultiNamespaceReferences' |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.purpose.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.purpose.md
new file mode 100644
index 000000000000000..a36301a6451bc5e
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.purpose.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCollectMultiNamespaceReferencesOptions](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.md) > [purpose](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesoptions.purpose.md)
+
+## SavedObjectsCollectMultiNamespaceReferencesOptions.purpose property
+
+Optional purpose used to determine filtering and authorization checks; default is 'collectMultiNamespaceReferences'
+
+Signature:
+
+```typescript
+purpose?: 'collectMultiNamespaceReferences' | 'updateObjectsSpaces';
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.md
new file mode 100644
index 000000000000000..bc72e7399446819
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCollectMultiNamespaceReferencesResponse](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.md)
+
+## SavedObjectsCollectMultiNamespaceReferencesResponse interface
+
+The response when object references are collected.
+
+Signature:
+
+```typescript
+export interface SavedObjectsCollectMultiNamespaceReferencesResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [objects](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.objects.md) | SavedObjectReferenceWithContext[] | |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.objects.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.objects.md
new file mode 100644
index 000000000000000..4b5707d7228a55c
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.objects.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCollectMultiNamespaceReferencesResponse](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.md) > [objects](./kibana-plugin-core-server.savedobjectscollectmultinamespacereferencesresponse.objects.md)
+
+## SavedObjectsCollectMultiNamespaceReferencesResponse.objects property
+
+Signature:
+
+```typescript
+objects: SavedObjectReferenceWithContext[];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.md
deleted file mode 100644
index 8a2afe6656fa4c4..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsDeleteFromNamespacesOptions](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.md)
-
-## SavedObjectsDeleteFromNamespacesOptions interface
-
-
-Signature:
-
-```typescript
-export interface SavedObjectsDeleteFromNamespacesOptions extends SavedObjectsBaseOptions
-```
-
-## Properties
-
-| Property | Type | Description |
-| --- | --- | --- |
-| [refresh](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.refresh.md) | MutatingOperationRefreshSetting | The Elasticsearch Refresh setting for this operation |
-
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.refresh.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.refresh.md
deleted file mode 100644
index 1175b79bc1abdf5..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.refresh.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsDeleteFromNamespacesOptions](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.md) > [refresh](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.refresh.md)
-
-## SavedObjectsDeleteFromNamespacesOptions.refresh property
-
-The Elasticsearch Refresh setting for this operation
-
-Signature:
-
-```typescript
-refresh?: MutatingOperationRefreshSetting;
-```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md
deleted file mode 100644
index 6021c8866f01841..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsDeleteFromNamespacesResponse](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md)
-
-## SavedObjectsDeleteFromNamespacesResponse interface
-
-
-Signature:
-
-```typescript
-export interface SavedObjectsDeleteFromNamespacesResponse
-```
-
-## Properties
-
-| Property | Type | Description |
-| --- | --- | --- |
-| [namespaces](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.namespaces.md) | string[] | The namespaces the object exists in after this operation is complete. An empty array indicates the object was deleted. |
-
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.namespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.namespaces.md
deleted file mode 100644
index 9600a9e89138011..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.namespaces.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsDeleteFromNamespacesResponse](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md) > [namespaces](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.namespaces.md)
-
-## SavedObjectsDeleteFromNamespacesResponse.namespaces property
-
-The namespaces the object exists in after this operation is complete. An empty array indicates the object was deleted.
-
-Signature:
-
-```typescript
-namespaces: string[];
-```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md
deleted file mode 100644
index 4b69b10318ed3b3..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsRepository](./kibana-plugin-core-server.savedobjectsrepository.md) > [addToNamespaces](./kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md)
-
-## SavedObjectsRepository.addToNamespaces() method
-
-Adds one or more namespaces to a given multi-namespace saved object. This method and \[`deleteFromNamespaces`\][SavedObjectsRepository.deleteFromNamespaces()](./kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md) are the only ways to change which Spaces a multi-namespace saved object is shared to.
-
-Signature:
-
-```typescript
-addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise;
-```
-
-## Parameters
-
-| Parameter | Type | Description |
-| --- | --- | --- |
-| type | string | |
-| id | string | |
-| namespaces | string[] | |
-| options | SavedObjectsAddToNamespacesOptions | |
-
-Returns:
-
-`Promise`
-
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.collectmultinamespacereferences.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.collectmultinamespacereferences.md
new file mode 100644
index 000000000000000..450cd14a2052435
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.collectmultinamespacereferences.md
@@ -0,0 +1,25 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsRepository](./kibana-plugin-core-server.savedobjectsrepository.md) > [collectMultiNamespaceReferences](./kibana-plugin-core-server.savedobjectsrepository.collectmultinamespacereferences.md)
+
+## SavedObjectsRepository.collectMultiNamespaceReferences() method
+
+Gets all references and transitive references of the given objects. Ignores any object and/or reference that is not a multi-namespace type.
+
+Signature:
+
+```typescript
+collectMultiNamespaceReferences(objects: SavedObjectsCollectMultiNamespaceReferencesObject[], options?: SavedObjectsCollectMultiNamespaceReferencesOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| objects | SavedObjectsCollectMultiNamespaceReferencesObject[] | |
+| options | SavedObjectsCollectMultiNamespaceReferencesOptions | |
+
+Returns:
+
+`Promise`
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.createpointintimefinder.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.createpointintimefinder.md
index 5d9d2857f6e0b17..c92a1986966fd86 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.createpointintimefinder.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.createpointintimefinder.md
@@ -15,7 +15,7 @@ Once you have retrieved all of the results you need, it is recommended to call `
Signature:
```typescript
-createPointInTimeFinder(findOptions: SavedObjectsCreatePointInTimeFinderOptions, dependencies?: SavedObjectsCreatePointInTimeFinderDependencies): ISavedObjectsPointInTimeFinder;
+createPointInTimeFinder(findOptions: SavedObjectsCreatePointInTimeFinderOptions, dependencies?: SavedObjectsCreatePointInTimeFinderDependencies): ISavedObjectsPointInTimeFinder;
```
## Parameters
@@ -27,7 +27,7 @@ createPointInTimeFinder(findOptions: SavedObjectsCreatePointInTimeFinderOptions,
Returns:
-`ISavedObjectsPointInTimeFinder`
+`ISavedObjectsPointInTimeFinder`
## Example
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md
deleted file mode 100644
index d5ffb6d9ff9d888..000000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsRepository](./kibana-plugin-core-server.savedobjectsrepository.md) > [deleteFromNamespaces](./kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md)
-
-## SavedObjectsRepository.deleteFromNamespaces() method
-
-Removes one or more namespaces from a given multi-namespace saved object. If no namespaces remain, the saved object is deleted entirely. This method and \[`addToNamespaces`\][SavedObjectsRepository.addToNamespaces()](./kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md) are the only ways to change which Spaces a multi-namespace saved object is shared to.
-
-Signature:
-
-```typescript
-deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise;
-```
-
-## Parameters
-
-| Parameter | Type | Description |
-| --- | --- | --- |
-| type | string | |
-| id | string | |
-| namespaces | string[] | |
-| options | SavedObjectsDeleteFromNamespacesOptions | |
-
-Returns:
-
-`Promise`
-
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md
index 00e6ed3aeddfcb4..191b125ef3f74bf 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md
@@ -15,17 +15,16 @@ export declare class SavedObjectsRepository
| Method | Modifiers | Description |
| --- | --- | --- |
-| [addToNamespaces(type, id, namespaces, options)](./kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md) | | Adds one or more namespaces to a given multi-namespace saved object. This method and \[deleteFromNamespaces\][SavedObjectsRepository.deleteFromNamespaces()](./kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md) are the only ways to change which Spaces a multi-namespace saved object is shared to. |
| [bulkCreate(objects, options)](./kibana-plugin-core-server.savedobjectsrepository.bulkcreate.md) | | Creates multiple documents at once |
| [bulkGet(objects, options)](./kibana-plugin-core-server.savedobjectsrepository.bulkget.md) | | Returns an array of objects by id |
| [bulkUpdate(objects, options)](./kibana-plugin-core-server.savedobjectsrepository.bulkupdate.md) | | Updates multiple objects in bulk |
| [checkConflicts(objects, options)](./kibana-plugin-core-server.savedobjectsrepository.checkconflicts.md) | | Check what conflicts will result when creating a given array of saved objects. This includes "unresolvable conflicts", which are multi-namespace objects that exist in a different namespace; such conflicts cannot be resolved/overwritten. |
| [closePointInTime(id, options)](./kibana-plugin-core-server.savedobjectsrepository.closepointintime.md) | | Closes a Point In Time (PIT) by ID. This simply proxies the request to ES via the Elasticsearch client, and is included in the Saved Objects Client as a convenience for consumers who are using openPointInTimeForType.Only use this API if you have an advanced use case that's not solved by the [SavedObjectsRepository.createPointInTimeFinder()](./kibana-plugin-core-server.savedobjectsrepository.createpointintimefinder.md) method. |
+| [collectMultiNamespaceReferences(objects, options)](./kibana-plugin-core-server.savedobjectsrepository.collectmultinamespacereferences.md) | | Gets all references and transitive references of the given objects. Ignores any object and/or reference that is not a multi-namespace type. |
| [create(type, attributes, options)](./kibana-plugin-core-server.savedobjectsrepository.create.md) | | Persists an object |
| [createPointInTimeFinder(findOptions, dependencies)](./kibana-plugin-core-server.savedobjectsrepository.createpointintimefinder.md) | | Returns a [ISavedObjectsPointInTimeFinder](./kibana-plugin-core-server.isavedobjectspointintimefinder.md) to help page through large sets of saved objects. We strongly recommend using this API for any find queries that might return more than 1000 saved objects, however this API is only intended for use in server-side "batch" processing of objects where you are collecting all objects in memory or streaming them back to the client.Do NOT use this API in a route handler to facilitate paging through saved objects on the client-side unless you are streaming all of the results back to the client at once. Because the returned generator is stateful, you cannot rely on subsequent http requests retrieving new pages from the same Kibana server in multi-instance deployments.This generator wraps calls to [SavedObjectsRepository.find()](./kibana-plugin-core-server.savedobjectsrepository.find.md) and iterates over multiple pages of results using _pit and search_after. This will open a new Point-In-Time (PIT), and continue paging until a set of results is received that's smaller than the designated perPage.Once you have retrieved all of the results you need, it is recommended to call close() to clean up the PIT and prevent Elasticsearch from consuming resources unnecessarily. This is only required if you are done iterating and have not yet paged through all of the results: the PIT will automatically be closed for you once you reach the last page of results, or if the underlying call to find fails for any reason. |
| [delete(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.delete.md) | | Deletes an object |
| [deleteByNamespace(namespace, options)](./kibana-plugin-core-server.savedobjectsrepository.deletebynamespace.md) | | Deletes all objects from the provided namespace. |
-| [deleteFromNamespaces(type, id, namespaces, options)](./kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md) | | Removes one or more namespaces from a given multi-namespace saved object. If no namespaces remain, the saved object is deleted entirely. This method and \[addToNamespaces\][SavedObjectsRepository.addToNamespaces()](./kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md) are the only ways to change which Spaces a multi-namespace saved object is shared to. |
| [find(options)](./kibana-plugin-core-server.savedobjectsrepository.find.md) | | |
| [get(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.get.md) | | Gets a single object |
| [incrementCounter(type, id, counterFields, options)](./kibana-plugin-core-server.savedobjectsrepository.incrementcounter.md) | | Increments all the specified counter fields (by one by default). Creates the document if one doesn't exist for the given id. |
@@ -33,4 +32,5 @@ export declare class SavedObjectsRepository
| [removeReferencesTo(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.removereferencesto.md) | | Updates all objects containing a reference to the given {type, id} tuple to remove the said reference. |
| [resolve(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.resolve.md) | | Resolves a single object, using any legacy URL alias if it exists |
| [update(type, id, attributes, options)](./kibana-plugin-core-server.savedobjectsrepository.update.md) | | Updates an object |
+| [updateObjectsSpaces(objects, spacesToAdd, spacesToRemove, options)](./kibana-plugin-core-server.savedobjectsrepository.updateobjectsspaces.md) | | Updates one or more objects to add and/or remove them from specified spaces. |
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.updateobjectsspaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.updateobjectsspaces.md
new file mode 100644
index 000000000000000..6914c1b46b8296a
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.updateobjectsspaces.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsRepository](./kibana-plugin-core-server.savedobjectsrepository.md) > [updateObjectsSpaces](./kibana-plugin-core-server.savedobjectsrepository.updateobjectsspaces.md)
+
+## SavedObjectsRepository.updateObjectsSpaces() method
+
+Updates one or more objects to add and/or remove them from specified spaces.
+
+Signature:
+
+```typescript
+updateObjectsSpaces(objects: SavedObjectsUpdateObjectsSpacesObject[], spacesToAdd: string[], spacesToRemove: string[], options?: SavedObjectsUpdateObjectsSpacesOptions): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| objects | SavedObjectsUpdateObjectsSpacesObject[] | |
+| spacesToAdd | string[] | |
+| spacesToRemove | string[] | |
+| options | SavedObjectsUpdateObjectsSpacesOptions | |
+
+Returns:
+
+`Promise`
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.rawtosavedobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.rawtosavedobject.md
index 3fc386f26314173..d71db9caf6a3b82 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.rawtosavedobject.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.rawtosavedobject.md
@@ -9,7 +9,7 @@ Converts a document from the format that is stored in elasticsearch to the saved
Signature:
```typescript
-rawToSavedObject(doc: SavedObjectsRawDoc, options?: SavedObjectsRawDocParseOptions): SavedObjectSanitizedDoc;
+rawToSavedObject(doc: SavedObjectsRawDoc, options?: SavedObjectsRawDocParseOptions): SavedObjectSanitizedDoc;
```
## Parameters
@@ -21,5 +21,5 @@ rawToSavedObject(doc: SavedObjectsRawDoc, options?: SavedObjectsRawDocParseOptio
Returns:
-`SavedObjectSanitizedDoc`
+`SavedObjectSanitizedDoc`
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.id.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.id.md
new file mode 100644
index 000000000000000..dac110ac4f47545
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.id.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesObject](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.md) > [id](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.id.md)
+
+## SavedObjectsUpdateObjectsSpacesObject.id property
+
+The type of the object to update
+
+Signature:
+
+```typescript
+id: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.md
new file mode 100644
index 000000000000000..847e40a8896b43b
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesObject](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.md)
+
+## SavedObjectsUpdateObjectsSpacesObject interface
+
+An object that should have its spaces updated.
+
+Signature:
+
+```typescript
+export interface SavedObjectsUpdateObjectsSpacesObject
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [id](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.id.md) | string | The type of the object to update |
+| [type](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.type.md) | string | The ID of the object to update |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.type.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.type.md
new file mode 100644
index 000000000000000..2e54d1636c5e9b8
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.type.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesObject](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.md) > [type](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesobject.type.md)
+
+## SavedObjectsUpdateObjectsSpacesObject.type property
+
+The ID of the object to update
+
+Signature:
+
+```typescript
+type: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.md
new file mode 100644
index 000000000000000..49ee013c5d2da96
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesOptions](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.md)
+
+## SavedObjectsUpdateObjectsSpacesOptions interface
+
+Options for the update operation.
+
+Signature:
+
+```typescript
+export interface SavedObjectsUpdateObjectsSpacesOptions extends SavedObjectsBaseOptions
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [refresh](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.refresh.md) | MutatingOperationRefreshSetting | The Elasticsearch Refresh setting for this operation |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.refresh.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.refresh.md
new file mode 100644
index 000000000000000..3d210f6ac51c766
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.refresh.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesOptions](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.md) > [refresh](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesoptions.refresh.md)
+
+## SavedObjectsUpdateObjectsSpacesOptions.refresh property
+
+The Elasticsearch Refresh setting for this operation
+
+Signature:
+
+```typescript
+refresh?: MutatingOperationRefreshSetting;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.md
new file mode 100644
index 000000000000000..bf53277887bda32
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesResponse](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.md)
+
+## SavedObjectsUpdateObjectsSpacesResponse interface
+
+The response when objects' spaces are updated.
+
+Signature:
+
+```typescript
+export interface SavedObjectsUpdateObjectsSpacesResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [objects](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.objects.md) | SavedObjectsUpdateObjectsSpacesResponseObject[] | |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.objects.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.objects.md
new file mode 100644
index 000000000000000..13328e2aed094dc
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.objects.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesResponse](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.md) > [objects](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponse.objects.md)
+
+## SavedObjectsUpdateObjectsSpacesResponse.objects property
+
+Signature:
+
+```typescript
+objects: SavedObjectsUpdateObjectsSpacesResponseObject[];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.error.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.error.md
new file mode 100644
index 000000000000000..7d7ac4ada884ddc
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.error.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesResponseObject](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.md) > [error](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.error.md)
+
+## SavedObjectsUpdateObjectsSpacesResponseObject.error property
+
+Included if there was an error updating this object's spaces
+
+Signature:
+
+```typescript
+error?: SavedObjectError;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.id.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.id.md
new file mode 100644
index 000000000000000..28a81ee5dfd6a6f
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.id.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesResponseObject](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.md) > [id](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.id.md)
+
+## SavedObjectsUpdateObjectsSpacesResponseObject.id property
+
+The ID of the referenced object
+
+Signature:
+
+```typescript
+id: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.md
new file mode 100644
index 000000000000000..03802278ee5a3f2
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesResponseObject](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.md)
+
+## SavedObjectsUpdateObjectsSpacesResponseObject interface
+
+Details about a specific object's update result.
+
+Signature:
+
+```typescript
+export interface SavedObjectsUpdateObjectsSpacesResponseObject
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [error](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.error.md) | SavedObjectError | Included if there was an error updating this object's spaces |
+| [id](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.id.md) | string | The ID of the referenced object |
+| [spaces](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.spaces.md) | string[] | The space(s) that the referenced object exists in |
+| [type](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.type.md) | string | The type of the referenced object |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.spaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.spaces.md
new file mode 100644
index 000000000000000..52b1ca187925c43
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.spaces.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesResponseObject](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.md) > [spaces](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.spaces.md)
+
+## SavedObjectsUpdateObjectsSpacesResponseObject.spaces property
+
+The space(s) that the referenced object exists in
+
+Signature:
+
+```typescript
+spaces: string[];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.type.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.type.md
new file mode 100644
index 000000000000000..da0bbb10885073e
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.type.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUpdateObjectsSpacesResponseObject](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.md) > [type](./kibana-plugin-core-server.savedobjectsupdateobjectsspacesresponseobject.type.md)
+
+## SavedObjectsUpdateObjectsSpacesResponseObject.type property
+
+The type of the referenced object
+
+Signature:
+
+```typescript
+type: string;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldtype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldtype.md
index 2b3d3df1ec8d0eb..4e3dea5549b566c 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldtype.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldtype.md
@@ -4,6 +4,11 @@
## IFieldType interface
+> Warning: This API is now obsolete.
+>
+> Use IndexPatternField or FieldSpec instead
+>
+
Signature:
```typescript
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md
index 3a78395b4275488..bf7f88ab3703951 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md
@@ -4,7 +4,10 @@
## IIndexPattern interface
-IIndexPattern allows for an IndexPattern OR an index pattern saved object too ambiguous, should be avoided
+> Warning: This API is now obsolete.
+>
+> IIndexPattern allows for an IndexPattern OR an index pattern saved object Use IndexPattern or IndexPatternSpec instead
+>
Signature:
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
index 58a225a3a4bc3f3..7f5a042e0ab8187 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
@@ -67,7 +67,7 @@
| [IEsSearchRequest](./kibana-plugin-plugins-data-public.iessearchrequest.md) | |
| [IFieldSubType](./kibana-plugin-plugins-data-public.ifieldsubtype.md) | |
| [IFieldType](./kibana-plugin-plugins-data-public.ifieldtype.md) | |
-| [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) | IIndexPattern allows for an IndexPattern OR an index pattern saved object too ambiguous, should be avoided |
+| [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) | |
| [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) | |
| [IKibanaSearchRequest](./kibana-plugin-plugins-data-public.ikibanasearchrequest.md) | |
| [IKibanaSearchResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.md) | |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md
index 259009c1c5668da..3c99ae4c86c639a 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md
@@ -21,7 +21,7 @@ search: {
})[];
InvalidEsCalendarIntervalError: typeof InvalidEsCalendarIntervalError;
InvalidEsIntervalFormatError: typeof InvalidEsIntervalFormatError;
- Ipv4Address: typeof Ipv4Address;
+ IpAddress: typeof IpAddress;
isDateHistogramBucketAggConfig: typeof isDateHistogramBucketAggConfig;
isNumberType: (agg: import("../common").AggConfig) => boolean;
isStringType: (agg: import("../common").AggConfig) => boolean;
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldtype.md
index 48836a1b620b8de..5ac48d26a85d6d3 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldtype.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldtype.md
@@ -4,6 +4,11 @@
## IFieldType interface
+> Warning: This API is now obsolete.
+>
+> Use IndexPatternField or FieldSpec instead
+>
+
Signature:
```typescript
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.start.md
index 118b0104fbee640..7559695a0a33165 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.start.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsserviceprovider.start.md
@@ -8,7 +8,7 @@
```typescript
start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps): {
- indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: ElasticsearchClient) => Promise;
+ indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: ElasticsearchClient) => Promise;
};
```
@@ -22,6 +22,6 @@ start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps):
Returns:
`{
- indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: ElasticsearchClient) => Promise;
+ indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: ElasticsearchClient) => Promise;
}`
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md
index f4404521561d24b..dd1f3806c140816 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md
@@ -12,7 +12,7 @@ start(core: CoreStart): {
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise;
};
indexPatterns: {
- indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise;
+ indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise;
};
search: ISearchStart>;
};
@@ -31,7 +31,7 @@ start(core: CoreStart): {
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise;
};
indexPatterns: {
- indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise;
+ indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise;
};
search: ISearchStart>;
}`
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md
index 930f7710f9a0092..7072f25489db298 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md
@@ -21,7 +21,7 @@ search: {
})[];
InvalidEsCalendarIntervalError: typeof InvalidEsCalendarIntervalError;
InvalidEsIntervalFormatError: typeof InvalidEsIntervalFormatError;
- Ipv4Address: typeof Ipv4Address;
+ IpAddress: typeof IpAddress;
isNumberType: (agg: import("../common").AggConfig) => boolean;
isStringType: (agg: import("../common").AggConfig) => boolean;
isType: (...types: string[]) => (agg: import("../common").AggConfig) => boolean;
diff --git a/docs/discover/images/add-field-to-pattern.png b/docs/discover/images/add-field-to-pattern.png
new file mode 100644
index 000000000000000..84dfcb0745c6915
Binary files /dev/null and b/docs/discover/images/add-field-to-pattern.png differ
diff --git a/docs/discover/images/hello-field.png b/docs/discover/images/hello-field.png
new file mode 100644
index 000000000000000..07d97e054d7ecbe
Binary files /dev/null and b/docs/discover/images/hello-field.png differ
diff --git a/docs/management/action-types.asciidoc b/docs/management/action-types.asciidoc
index 6cdb1dbfa712e51..ec5677bd04a6e25 100644
--- a/docs/management/action-types.asciidoc
+++ b/docs/management/action-types.asciidoc
@@ -111,6 +111,13 @@ image::images/connector-select-type.png[Connector select type]
=== Importing and exporting connectors
To import and export rules, use the <>.
+After a successful import, the proper banner is displayed:
+[role="screenshot"]
+image::images/coonectors-import-banner.png[Connectors import banner, width=50%]
+
+If a connector is missing user sensitive information because of the import, a **Fix** button appears in the list view.
+[role="screenshot"]
+image::images/connectors-with-missing-secrets.png[Connectors with missing secrets]
[float]
[[create-connectors]]
diff --git a/docs/management/field-formatters/color-formatter.asciidoc b/docs/management/field-formatters/color-formatter.asciidoc
index d9ba5e9be116573..488fb3715379970 100644
--- a/docs/management/field-formatters/color-formatter.asciidoc
+++ b/docs/management/field-formatters/color-formatter.asciidoc
@@ -1,10 +1,5 @@
-The `Color` field formatter enables you to specify colors with specific ranges of values for a numeric field.
+The *Color* field formatter enables you to specify colors with ranges of values for a number field.
-When you select the `Color` field formatter, Kibana displays the *Range*, *Font Color*, *Background Color*, and
-*Example* fields.
-
-Click the *Add Color* button to add a range of values to associate with a particular color. You can click in the *Font
-Color* and *Background Color* fields to display a color picker. You can also enter a specific hex code value in the
-field. The effect of your current color choices are displayed in the *Example* field.
+When you select the *Color* formatter, click *Add Color*, then specify the *Range*, *Text color*, and *Background color*.
image::images/colorformatter.png[]
diff --git a/docs/management/field-formatters/duration-formatter.asciidoc b/docs/management/field-formatters/duration-formatter.asciidoc
index 36a73f61f622747..873a4ac94c2911a 100644
--- a/docs/management/field-formatters/duration-formatter.asciidoc
+++ b/docs/management/field-formatters/duration-formatter.asciidoc
@@ -1,4 +1,4 @@
-The `Duration` field formatter can display the numeric value of a field in the following increments:
+The *Duration* field formatter displays the numeric value of a field in the following increments:
* Picoseconds
* Nanoseconds
@@ -12,4 +12,4 @@ The `Duration` field formatter can display the numeric value of a field in the f
* Months
* Years
-You can specify these increments with up to 20 decimal places for both input and output formats.
+You can specify these increments with up to 20 decimal places for input and output formats.
diff --git a/docs/management/field-formatters/string-formatter.asciidoc b/docs/management/field-formatters/string-formatter.asciidoc
index ed3aa45873284ef..f32eee7dc8396b8 100644
--- a/docs/management/field-formatters/string-formatter.asciidoc
+++ b/docs/management/field-formatters/string-formatter.asciidoc
@@ -1,11 +1,20 @@
-The `String` field formatter can apply the following transformations to the field's contents:
+The *String* field formatter enables you to apply transforms to the field.
+
+Supported transformations include:
* Convert to lowercase
+
* Convert to uppercase
+
* Convert to title case
-* Apply the short dots transformation, which replaces the content before a `.` character with the first character of
-that content, as in the following example:
+
+* Apply the short dots transformation, which replaces the content before the `.` character with the first character of
+the content. For example:
[horizontal]
*Original*:: *Becomes*
`com.organizations.project.ClassName`:: `c.o.p.ClassName`
+
+* Base64 decode
+
+* URL param decode
diff --git a/docs/management/field-formatters/url-formatter.asciidoc b/docs/management/field-formatters/url-formatter.asciidoc
index 41d4f75603dc698..8b0e43c9f249687 100644
--- a/docs/management/field-formatters/url-formatter.asciidoc
+++ b/docs/management/field-formatters/url-formatter.asciidoc
@@ -1,33 +1,32 @@
-The `Url` field formatter can take on the following types:
+You can specify the following types to the `Url` field formatter:
-* The *Link* type turn the contents of the field into an URL.
-* The *Image* type can be used to specify an image directory where a specified image is located.
-* The *Audio* type can be used to specify an audio directory where a specified audio file is located.
+* *Link* — Converts the contents of the field into an URL. You can specify the width and height of the image, while keeping the aspect ratio.
+When the image is smaller than the specified paramters, the image is unable to upscale.
+* *Image* — Specifies the image directory.
+* *Audio* — Specify the audio directory.
-For an *Image* type you can specify width and height attributes. These will be used to set the max width / max height of the image, while keeping the aspect ratio. Image will not be upscaled if it's smaller than the provided size parameters.
-
-You can customize either type of URL field formats with templates. A _URL template_ enables you to add specific values
-to a partial URL. Use the string `{{value}}` to add the contents of the field to a fixed URL.
+To customize URL field formats, use templates. An *URL template* enables you to add values
+to a partial URL. To add the contents of the field to a fixed URL, use the `{{value}}` string.
For example, when:
* A field contains a user ID
-* That field uses the `Url` field formatter
+* A field uses the `Url` field formatter
* The URI template is `http://company.net/profiles?user_id={{value}}`
The resulting URL replaces `{{value}}` with the user ID from the field.
The `{{value}}` template string URL-encodes the contents of the field. When a field encoded into a URL contains
-non-ASCII characters, these characters are replaced with a `%` character and the appropriate hexadecimal code. For
+non-ASCII characters, the characters are replaced with a `%` character and the appropriate hexadecimal code. For
example, field contents `users/admin` result in the URL template adding `users%2Fadmin`.
-When the formatter type is set to *Image*, the `{{value}}` template string specifies the name of an image at the
+When the formatter type is *Image*, the `{{value}}` template string specifies the name of an image at the
specified URI.
-When the formatter type is set to *Audio*, the `{{value}}` template string specifies the name of an audio file at the specified URI.
+When the formatter type is *Audio*, the `{{value}}` template string specifies the name of an audio file at the specified URI.
-In order to pass unescaped values directly to the URL, use the `{{rawValue}}` string.
+To pass unescaped values directly to the URL, use the `{{rawValue}}` string.
-A _Label Template_ enables you to specify a text string that displays instead of the raw URL. You can use the
+A *Label template* enables you to specify a text string that appears instead of the raw URL. You can use the
`{{value}}` template string normally in label templates. You can also use the `{{url}}` template string to display
the formatted URL.
diff --git a/docs/management/images/colorformatter.png b/docs/management/images/colorformatter.png
index df5dc34dd31e51a..3c2cfad62d76a0f 100644
Binary files a/docs/management/images/colorformatter.png and b/docs/management/images/colorformatter.png differ
diff --git a/docs/management/images/connectors-with-missing-secrets.png b/docs/management/images/connectors-with-missing-secrets.png
new file mode 100644
index 000000000000000..ffc902d4a476874
Binary files /dev/null and b/docs/management/images/connectors-with-missing-secrets.png differ
diff --git a/docs/management/images/coonectors-import-banner.png b/docs/management/images/coonectors-import-banner.png
new file mode 100644
index 000000000000000..55a6e91d28c8df1
Binary files /dev/null and b/docs/management/images/coonectors-import-banner.png differ
diff --git a/docs/management/manage-index-patterns.asciidoc b/docs/management/manage-index-patterns.asciidoc
new file mode 100644
index 000000000000000..94870733174ad49
--- /dev/null
+++ b/docs/management/manage-index-patterns.asciidoc
@@ -0,0 +1,264 @@
+[[managing-index-patterns]]
+== Manage index pattern data fields
+
+To customize the data fields in your index pattern, you can add runtime fields to the existing documents, add scrited fields to compute data on the fly, and change how {kib} displays the data fields.
+
+[float]
+[[runtime-fields]]
+=== Explore your data with runtime fields
+
+Runtime fields are fields that you add to documents after you've ingested, and are evaluated at query time. With runtime fields, you allow for a smaller index and faster ingest time so that you can use less resources and reduce your operating costs. You can use runtime fields anywhere index patterns are used.
+
+When you use runtime fields, you can:
+
+* Define fields for a specific use without modifying the underlying schema.
+
+* Override the returned values from index fields.
+
+* Start working on your data without first understanding the structure.
+
+* Add fields to existing documents without reindexing your data.
+
+* Explore runtime field data in *Discover*.
+
+* Create visualizations with runtime field data using *Lens*, *Maps*, and *TSVB*.
+
+WARNING: Runtime fields can impact {kib} performance. When you run a query, {es} uses the fields you index first to shorten the response time.
+Index the fields that you commonly search for and filter on, such as `timestamp`, then use runtime fields to limit the number of fields {es} uses to calculate values.
+
+For more information, refer to {ref}/runtime.html[Runtime fields].
+
+[float]
+[[create-runtime-fields]]
+==== Create runtime fields
+
+Create runtime fields in your index patterns, or create runtime fields in *Discover* and *Lens*.
+
+. Open the main menu, then click *Stack Management > Index Patterns*.
+
+. Select the index pattern you want to add the runtime field to, then click *Add field*.
+
+. Enter a *Name* for the runtime field, then select the field *Type*.
+
+. Select *Set value*, then define the field value by emitting a single value using the {ref}/modules-scripting-painless.html[Painless scripting language].
++
+The script must match the field *Type*, or the script fails.
+
+. Click *Create field*.
+//+
+//For information on how to create runtime fields in *Discover*, refer to <>.
++
+For information on how to create runtime fields in *Lens*, refer to <>.
+
+[float]
+[[runtime-field-examples]]
+==== Runtime field examples
+
+Try the runtime field examples on your own using the *Sample web logs* data index pattern.
+
+[float]
+[[simple-hello-world-example]]
+==== Return a keyword value
+
+To return `Hello World!` value:
+
+[source,text]
+----
+emit("Hello World!");
+----
+
+[float]
+[[perform-a-calculation-on-a-single-field]]
+===== Perform a calculation on a single field
+
+Calculate kilobytes from bytes:
+
+[source,text]
+----
+emit(doc['bytes'].value / 1024)
+----
+
+[float]
+[[return-substring]]
+===== Return a substring
+
+Return the string that appears after the last slash in the URL:
+
+[source,text]
+----
+def path = doc["url.keyword"].value;
+if (path != null) {
+ int lastSlashIndex = path.lastIndexOf('/');
+ if (lastSlashIndex > 0) {
+ emit(path.substring(lastSlashIndex+1));
+ return;
+ }
+}
+emit("");
+----
+
+[float]
+[[replace-nulls-with-blanks]]
+===== Replace nulls with blanks
+
+Replace null values with none values:
+
+[source,text]
+----
+def source = doc['referer'].value;
+if (source != null) {
+ emit(source);
+ return;
+}
+else {
+ emit("None");
+}
+----
+
+Specify operating system condition:
+
+[source,text]
+----
+def source = doc['machine.os.keyword'].value;
+if (source != "") {
+ emit(source);
+}
+else {
+ emit("None");
+}
+----
+
+[float]
+[[manage-runtime-fields]]
+==== Manage runtime fields
+
+Edit the settings for runtime fields, or remove runtime fields from index patterns.
+
+. Open the main menu, then click *Stack Management > Index Patterns*.
+
+. Select the index pattern that contains the runtime field you want to manage, then open the runtime field edit options or delete the runtime field.
+
+[float]
+[[scripted-fields]]
+=== Add scripted fields to index patterns
+
+deprecated::[7.13,Use {ref}/runtime.html[runtime fields] instead of scripted fields. Runtime fields support Painless scripts and provide greater flexibility.]
+
+Scripted fields compute data on the fly from the data in your {es} indices. The data is shown on
+the Discover tab as part of the document data, and you can use scripted fields in your visualizations. You query scripted fields with the <>, and can filter them using the filter bar. The scripted field values are computed at query time, so they aren't indexed and cannot be searched using the {kib} default
+query language.
+
+WARNING: Computing data on the fly with scripted fields can be very resource intensive and can have a direct impact on
+{kib} performance. Keep in mind that there's no built-in validation of a scripted field. If your scripts are
+buggy, you'll get exceptions whenever you try to view the dynamically generated data.
+
+When you define a scripted field in {kib}, you have a choice of the {ref}/modules-scripting-expression.html[Lucene expressions] or the
+{ref}/modules-scripting-painless.html[Painless] scripting language.
+
+You can reference any single value numeric field in your expressions, for example:
+
+----
+doc['field_name'].value
+----
+
+For more information on scripted fields and additional examples, refer to
+https://www.elastic.co/blog/using-painless-kibana-scripted-fields[Using Painless in {kib} scripted fields]
+
+[float]
+[[create-scripted-field]]
+==== Create scripted fields
+
+Create and add scripted fields to your index patterns.
+
+. Open the main menu, then click *Stack Management > Index Patterns*.
+
+. Select the index pattern you want to add a scripted field to.
+
+. Select the *Scripted fields* tab, then click *Add scripted field*.
+
+. Enter a *Name* for the scripted field, then enter the *Script* you want to use to compute a value on the fly from your index data.
+
+. Click *Create field*.
+
+For more information about scripted fields in {es}, refer to {ref}/modules-scripting.html[Scripting].
+
+[float]
+[[update-scripted-field]]
+==== Manage scripted fields
+
+. Open the main menu, then click *Stack Management > Index Patterns*.
+
+. Select the index pattern that contains the scripted field you want to manage.
+
+. Select the *Scripted fields* tab, then open the scripted field edit options or delete the scripted field.
+
+WARNING: Built-in validation is unsupported for scripted fields. When your scripts contain errors, you receive
+exceptions when you view the dynamically generated data.
+
+[float]
+[[managing-fields]]
+=== Format data fields
+
+{kib} uses the same field types as {es}, however, some {es} field types are unsupported in {kib}.
+To customize how {kib} displays data fields, use the formatting options.
+
+. Open the main menu, then click *Stack Management > Index Patterns*.
+
+. Click the index pattern that contains the field you want to change.
+
+. Find the field, then open the edit options (image:management/index-patterns/images/edit_icon.png[Data field edit icon]).
+
+. Select *Set custom label*, then enter a *Custom label* for the field.
+
+. Select *Set format*, then enter the *Format* for the field.
+
+[float]
+[[string-field-formatters]]
+==== String field formatters
+
+String fields support *String* and *Url* formatters.
+
+include::field-formatters/string-formatter.asciidoc[]
+
+include::field-formatters/url-formatter.asciidoc[]
+
+[float]
+[[field-formatters-date]]
+==== Date field formatters
+
+Date fields support *Date*, *String*, and *Url* formatters.
+
+The *Date* formatter enables you to choose the display format of date stamps using the https://momentjs.com/[moment.js]
+standard format definitions.
+
+include::field-formatters/string-formatter.asciidoc[]
+
+include::field-formatters/url-formatter.asciidoc[]
+
+[float]
+[[field-formatters-geopoint]]
+==== Geographic point field formatters
+
+Geographic point fields support the *String* formatter.
+
+include::field-formatters/string-formatter.asciidoc[]
+
+[float]
+[[field-formatters-numeric]]
+==== Number field formatters
+
+Numeric fields support *Bytes*, *Color*, *Duration*, *Histogram*, *Number*, *Percentage*, *String*, and *Url* formatters.
+
+The *Bytes*, *Number*, and *Percentage* formatters enable you to choose the display formats of numbers in the field using
+the <> syntax that {kib} maintains.
+
+The *Histogram* formatter is used only for the {ref}/histogram.html[histogram field type]. When you use the *Histogram* formatter,
+you can apply the *Bytes*, *Number*, or *Percentage* format to aggregated data.
+
+include::field-formatters/url-formatter.asciidoc[]
+
+include::field-formatters/string-formatter.asciidoc[]
+
+include::field-formatters/duration-formatter.asciidoc[]
+
+include::field-formatters/color-formatter.asciidoc[]
\ No newline at end of file
diff --git a/docs/management/managing-fields.asciidoc b/docs/management/managing-fields.asciidoc
deleted file mode 100644
index 505f6853c79060a..000000000000000
--- a/docs/management/managing-fields.asciidoc
+++ /dev/null
@@ -1,134 +0,0 @@
-[[managing-fields]]
-== Field management
-
-Whenever possible,
-{kib} uses the same field type for display as {es}. However, a few field types
-{es} supports are not available in {kib}. Use field formatters to customize how your
-fields are displayed in Kibana, regardless of how they are stored in {es}.
-
-Kibana provides these field formatters:
-
-* <>
-* <>
-* <>
-* <>
-
-To format a field:
-
-. Open the main menu, and click *Stack Management > Index Patterns*.
-. Click the index pattern that contains the field you want to format.
-. Find the field you want to format and click the edit icon (image:management/index-patterns/images/edit_icon.png[]).
-. Enter a custom label for the field, if needed.
-. Select a format and fill in the details.
-+
-[role="screenshot"]
-image:management/index-patterns/images/edit-field-format.png["Edit field format"]
-
-
-
-[[field-formatters-string]]
-=== String field formatters
-
-String fields support the `String` and `Url` formatters.
-
-include::field-formatters/string-formatter.asciidoc[]
-
-include::field-formatters/url-formatter.asciidoc[]
-
-[[field-formatters-date]]
-=== Date field formatters
-
-Date fields support the `Date`, `Url`, and `String` formatters.
-
-The `Date` formatter enables you to choose the display format of date stamps using the https://momentjs.com/[moment.js]
-standard format definitions.
-
-include::field-formatters/string-formatter.asciidoc[]
-
-include::field-formatters/url-formatter.asciidoc[]
-
-[[field-formatters-geopoint]]
-=== Geographic point field formatters
-
-Geographic point fields support the `String` formatter.
-
-include::field-formatters/string-formatter.asciidoc[]
-
-[[field-formatters-numeric]]
-=== Numeric field formatters
-
-Numeric fields support the `Url`, `Bytes`, `Duration`, `Number`, `Percentage`, `Histogram`, `String`, and `Color` formatters.
-
-The `Bytes`, `Number`, and `Percentage` formatters enable you to choose the display formats of numbers in this field using
-the <> syntax that {kib} maintains.
-
-The `Histogram` formatter is only used for the {ref}/histogram.html[histogram field type]. When using the `Histogram` formatter,
-you can apply the `Number`, `Bytes`, or `Percentage` format to the aggregated data.
-
-`Number`, and `Percentage` formatters enable you to choose the display formats of numbers in this field using
-the <> syntax that {kib} maintains.
-
-include::field-formatters/url-formatter.asciidoc[]
-
-include::field-formatters/string-formatter.asciidoc[]
-
-include::field-formatters/duration-formatter.asciidoc[]
-
-include::field-formatters/color-formatter.asciidoc[]
-
-[[scripted-fields]]
-=== Scripted fields
-deprecated::[7.13,Use {ref}/runtime.html[runtime fields] instead of scripted fields. Runtime fields support Painless scripts and provide greater flexibility.]
-
-Scripted fields compute data on the fly from the data in your {es} indices. The data is shown on
-the Discover tab as part of the document data, and you can use scripted fields in your visualizations. You query scripted fields with the <>, and can filter them using the filter bar. The scripted field values are computed at query time, so they aren't indexed and cannot be searched using the {kib} default
-query language.
-
-WARNING: Computing data on the fly with scripted fields can be very resource intensive and can have a direct impact on
-{kib} performance. Keep in mind that there's no built-in validation of a scripted field. If your scripts are
-buggy, you'll get exceptions whenever you try to view the dynamically generated data.
-
-When you define a scripted field in {kib}, you have a choice of the {ref}/modules-scripting-expression.html[Lucene expressions] or the
-{ref}/modules-scripting-painless.html[Painless] scripting language.
-
-You can reference any single value numeric field in your expressions, for example:
-
-----
-doc['field_name'].value
-----
-
-For more information on scripted fields and additional examples, refer to
-https://www.elastic.co/blog/using-painless-kibana-scripted-fields[Using Painless in {kib} scripted fields]
-
-[float]
-[[create-scripted-field]]
-=== Create a scripted field
-
-. Open the main menu, then click *Stack Management > Index Patterns*.
-. Select the index pattern you want to add a scripted field to.
-. Go to the *Scripted fields* tab for the index pattern, then click *Add scripted field*.
-. Enter a name for the scripted field.
-. Enter the expression that you want to use to compute a value on the fly from your index data.
-. Click *Create field*.
-
-For more information about scripted fields in {es}, see
-{ref}/modules-scripting.html[Scripting].
-
-[float]
-[[update-scripted-field]]
-=== Update a scripted field
-
-. Click the *Scripted fields* tab for the index pattern.
-. Click the *Edit* button for the scripted field you want to change.
-. Make your changes, then click *Save field*.
-
-WARNING: Built-in validation is unsupported for scripted fields. If your scripts are buggy, you'll get
-exceptions whenever you try to view the dynamically generated data.
-
-[float]
-[[delete-scripted-field]]
-=== Delete a scripted field
-
-. Click the *Scripted fields* tab for the index pattern.
-. Click *Delete* for the scripted field you want to remove.
-. Click *Delete* on the confirmation window.
diff --git a/docs/maps/trouble-shooting.asciidoc b/docs/maps/trouble-shooting.asciidoc
index 0e1ed0b9e1bec5f..a58e8ac8902b8b0 100644
--- a/docs/maps/trouble-shooting.asciidoc
+++ b/docs/maps/trouble-shooting.asciidoc
@@ -26,7 +26,7 @@ image::maps/images/inspector.png[]
* Verify your geospatial data is correctly mapped as {ref}/geo-point.html[geo_point] or {ref}/geo-shape.html[geo_shape].
** Run `GET myIndexPatternTitle/_field_caps?fields=myGeoFieldName` in <>, replacing `myIndexPatternTitle` and `myGeoFieldName` with your index pattern title and geospatial field name.
** Ensure response specifies `type` as `geo_point` or `geo_shape`.
-* Verify your geospatial data is correctly mapped in your <>.
+* Verify your geospatial data is correctly mapped in your <>.
** Open your index pattern in <>.
** Ensure your geospatial field type is `geo_point` or `geo_shape`.
** Ensure your geospatial field is searchable and aggregatable.
diff --git a/docs/maps/vector-tooltips.asciidoc b/docs/maps/vector-tooltips.asciidoc
index b0498c9088e4e68..2dda35aa28f768f 100644
--- a/docs/maps/vector-tooltips.asciidoc
+++ b/docs/maps/vector-tooltips.asciidoc
@@ -18,7 +18,7 @@ image::maps/images/multifeature_tooltip.png[]
==== Format tooltips
You can format the attributes in a tooltip by adding <> to your
-Kibana index pattern. You can use field formatters to round numbers, provide units,
+index pattern. You can use field formatters to round numbers, provide units,
and even display images in your tooltip.
[float]
diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc
index 4aedb0f516b20bf..a14bda2bf5a9867 100644
--- a/docs/redirects.asciidoc
+++ b/docs/redirects.asciidoc
@@ -279,26 +279,32 @@ This content has moved. Refer to <>.
[role="exclude",id="ingest-node-pipelines"]
== Ingest Node Pipelines
-This content has moved. See {ref}/ingest.html[Ingest pipelines].
+This content has moved. Refer to {ref}/ingest.html[Ingest pipelines].
[role="exclude",id="create-panels-with-timelion"]
== Timelion
-This content has moved. refer to <>.
+This content has moved. Refer to <>.
[role="exclude",id="space-rbac-tutorial"]
== Tutorial: Use role-based access control to customize Kibana spaces
-This content has moved. refer to <>.
+This content has moved. Refer to <>.
[role="exclude",id="search"]
== Search your data
-This content has moved. refer to <>.
+This content has moved. Refer to <>.
[role="exclude",id="discover-document-context"]
== View surrounding documents
-This content has moved. refer to <>.
+This content has moved. Refer to <>.
+
+[role="exclude",id="field-formatters-string"]
+== String field formatters
+
+This content has moved. Refer to <>.
+
diff --git a/docs/user/alerting/images/rule-details-alerts-inactive.png b/docs/user/alerting/images/rule-details-alerts-inactive.png
index f84910ae0dcdc4b..fc82cf465ebb2da 100644
Binary files a/docs/user/alerting/images/rule-details-alerts-inactive.png and b/docs/user/alerting/images/rule-details-alerts-inactive.png differ
diff --git a/docs/user/alerting/images/rules-imported-banner.png b/docs/user/alerting/images/rules-imported-banner.png
new file mode 100644
index 000000000000000..54dd5205a488d0f
Binary files /dev/null and b/docs/user/alerting/images/rules-imported-banner.png differ
diff --git a/docs/user/alerting/rule-management.asciidoc b/docs/user/alerting/rule-management.asciidoc
index b15c46254b770e3..e47858f58cd1a6c 100644
--- a/docs/user/alerting/rule-management.asciidoc
+++ b/docs/user/alerting/rule-management.asciidoc
@@ -62,6 +62,9 @@ image:images/bulk-mute-disable.png[The Manage rules button lets you mute/unmute,
=== Importing and exporting rules
To import and export rules, use the <>.
+After the succesful import the proper banner will be displayed:
+[role="screenshot"]
+image::images/rules-imported-banner.png[Rules import banner, width=50%]
[float]
=== Required permissions
diff --git a/docs/user/dashboard/images/lens_advanced_2_1.png b/docs/user/dashboard/images/lens_advanced_2_1.png
index 5090f0d3b2841f4..dab32369d71a1d9 100644
Binary files a/docs/user/dashboard/images/lens_advanced_2_1.png and b/docs/user/dashboard/images/lens_advanced_2_1.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_2_1_1.png b/docs/user/dashboard/images/lens_advanced_2_1_1.png
deleted file mode 100644
index f4d9ca488782e61..000000000000000
Binary files a/docs/user/dashboard/images/lens_advanced_2_1_1.png and /dev/null differ
diff --git a/docs/user/dashboard/images/lens_advanced_3_1.gif b/docs/user/dashboard/images/lens_advanced_2_2.gif
similarity index 100%
rename from docs/user/dashboard/images/lens_advanced_3_1.gif
rename to docs/user/dashboard/images/lens_advanced_2_2.gif
diff --git a/docs/user/dashboard/images/lens_advanced_2_2.png b/docs/user/dashboard/images/lens_advanced_2_2.png
index 820bc3bd4dfa9e3..1d88bcd238ca302 100644
Binary files a/docs/user/dashboard/images/lens_advanced_2_2.png and b/docs/user/dashboard/images/lens_advanced_2_2.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_2_2_1.png b/docs/user/dashboard/images/lens_advanced_2_2_1.png
index 3044f1070367d04..c3fb697666b4624 100644
Binary files a/docs/user/dashboard/images/lens_advanced_2_2_1.png and b/docs/user/dashboard/images/lens_advanced_2_2_1.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_3_3.png b/docs/user/dashboard/images/lens_advanced_2_3.png
similarity index 100%
rename from docs/user/dashboard/images/lens_advanced_3_3.png
rename to docs/user/dashboard/images/lens_advanced_2_3.png
diff --git a/docs/user/dashboard/images/lens_advanced_3_1.png b/docs/user/dashboard/images/lens_advanced_3_1.png
new file mode 100644
index 000000000000000..1473b203924a3a5
Binary files /dev/null and b/docs/user/dashboard/images/lens_advanced_3_1.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_3_1_1.png b/docs/user/dashboard/images/lens_advanced_3_1_1.png
deleted file mode 100644
index c3fb697666b4624..000000000000000
Binary files a/docs/user/dashboard/images/lens_advanced_3_1_1.png and /dev/null differ
diff --git a/docs/user/dashboard/images/lens_advanced_3_2.png b/docs/user/dashboard/images/lens_advanced_3_2.png
index 20da2ed706dfd2a..15f2f0228a0fc60 100644
Binary files a/docs/user/dashboard/images/lens_advanced_3_2.png and b/docs/user/dashboard/images/lens_advanced_3_2.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_4_1.png b/docs/user/dashboard/images/lens_advanced_4_1.png
index 43c8db213d482cd..50d1affa268dd49 100644
Binary files a/docs/user/dashboard/images/lens_advanced_4_1.png and b/docs/user/dashboard/images/lens_advanced_4_1.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_4_2.png b/docs/user/dashboard/images/lens_advanced_4_2.png
deleted file mode 100644
index 4b3e98910e7b794..000000000000000
Binary files a/docs/user/dashboard/images/lens_advanced_4_2.png and /dev/null differ
diff --git a/docs/user/dashboard/images/lens_advanced_5_1.png b/docs/user/dashboard/images/lens_advanced_5_1.png
new file mode 100644
index 000000000000000..5090f0d3b2841f4
Binary files /dev/null and b/docs/user/dashboard/images/lens_advanced_5_1.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_5_2.png b/docs/user/dashboard/images/lens_advanced_5_2.png
new file mode 100644
index 000000000000000..820bc3bd4dfa9e3
Binary files /dev/null and b/docs/user/dashboard/images/lens_advanced_5_2.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_5_2_1.png b/docs/user/dashboard/images/lens_advanced_5_2_1.png
new file mode 100644
index 000000000000000..3044f1070367d04
Binary files /dev/null and b/docs/user/dashboard/images/lens_advanced_5_2_1.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_6_1.png b/docs/user/dashboard/images/lens_advanced_6_1.png
new file mode 100644
index 000000000000000..5d5cefa472a13f2
Binary files /dev/null and b/docs/user/dashboard/images/lens_advanced_6_1.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_7_1.png b/docs/user/dashboard/images/lens_advanced_7_1.png
new file mode 100644
index 000000000000000..3d66d5d7e4579ea
Binary files /dev/null and b/docs/user/dashboard/images/lens_advanced_7_1.png differ
diff --git a/docs/user/dashboard/images/lens_advanced_result.png b/docs/user/dashboard/images/lens_advanced_result.png
index 19963d87c8e1c5d..8cf087f936abd40 100644
Binary files a/docs/user/dashboard/images/lens_advanced_result.png and b/docs/user/dashboard/images/lens_advanced_result.png differ
diff --git a/docs/user/dashboard/images/manage-runtime-field.gif b/docs/user/dashboard/images/manage-runtime-field.gif
new file mode 100644
index 000000000000000..c6ecf0caf818012
Binary files /dev/null and b/docs/user/dashboard/images/manage-runtime-field.gif differ
diff --git a/docs/user/dashboard/images/runtime-field-menu.png b/docs/user/dashboard/images/runtime-field-menu.png
new file mode 100644
index 000000000000000..891de38bb68333c
Binary files /dev/null and b/docs/user/dashboard/images/runtime-field-menu.png differ
diff --git a/docs/user/dashboard/lens-advanced.asciidoc b/docs/user/dashboard/lens-advanced.asciidoc
index 6b090f6017f5d7e..e49db0c0d026dc3 100644
--- a/docs/user/dashboard/lens-advanced.asciidoc
+++ b/docs/user/dashboard/lens-advanced.asciidoc
@@ -1,11 +1,11 @@
[[create-a-dashboard-of-panels-with-ecommerce-data]]
-== Tutorial: Create a dashboard of panels with ecommerce sales data
+== Time series analysis with Lens
-You collected sales data from your store, and you want to visualize and analyze the data on a dashboard.
+The tutorial uses sample data from the perspective of a shop owner looking
+at sales trends, but this type of dashboard works on any type of data.
To create dashboard panels of the data, open the *Lens* visualization builder, then
create the visualization panels that best display the data.
-
-When you've completed the tutorial, you'll have a dashboard that provides you with a complete overview of your ecommerce sales data.
+Before using this tutorial, you should be familiar with the <>.
[role="screenshot"]
image::images/lens_advanced_result.png[Dashboard view]
@@ -14,36 +14,52 @@ image::images/lens_advanced_result.png[Dashboard view]
[[add-the-data-and-create-the-dashboard-advanced]]
=== Add the data and create the dashboard
-To create visualizations of the data from your store, add the data set, then create the dashboard.
+If you are working with your own data, you should already have an <>.
+To install the sample sales data:
. From the {kib} *Home* page, click *Try our sample data*.
. From *Sample eCommerce orders*, click *Add data*.
+Then create a new dashboard:
+
. Open the main menu, then click *Dashboard*.
. On the *Dashboards* page, click *Create dashboard*.
+. Set the <> to *Last 30 days*.
+
[float]
[[open-and-set-up-lens-advanced]]
=== Open and set up Lens
-Open the *Lens* editor, then make sure the correct fields appear.
+*Lens* is designed to help you quickly build visualizations for your dashboard, as shown in <>, while providing support for advanced usage as well.
-. From the dashboard, click *Create panel*.
+Open the *Lens* editor, then make sure the correct fields appear.
-. On the *New visualization* window, click *Lens*.
-+
-[role="screenshot"]
-image::images/lens_end_to_end_1_1.png[New visualization popover]
+. From the dashboard, click *Create visualization*.
-. Make sure the *kibana_sample_data_ecommerce_* index appears.
+. Make sure the *kibana_sample_data_ecommerce* index appears.
[discrete]
-[[view-the-number-of-transactions-per-day]]
-=== View the number of transactions per hour
+[[custom-time-interval]]
+=== View a date histogram with a custom time interval
+
+It is common to use the automatic date histogram interval, but sometimes you want a larger or smaller
+interval. *Lens* only lets you choose the minimum time interval, not the exact time interval, for
+performance reasons. The performance limit is controlled by the <>
+advanced setting and the overall time range. To see hourly sales over a 30 day time period, choose
+one of these options:
-To determine the number of orders made every hour, create a bar chart, then add the chart to the dashboard.
+* View less than 30 days at a time, then use the time picker to select each day separately.
+
+* Increase `histogram:maxBars` from 100 to at least 720, which the number of hours in 30 days.
+This affects all visualizations and can reduce performance.
+
+* If approximation is okay, use the *Normalize unit* option. This can convert *Average sales per 12 hours*
+into *Average sales per 12 hours (per hour)* by dividing the number of hours.
+
+For the sample data, approximation is okay. To use the *Normalize unit* option:
. Set the <> to *Last 30 days*.
@@ -75,183 +91,258 @@ image::images/lens_advanced_1_2.png[Orders per day]
. Click *Save and return*.
[discrete]
-[[view-the-cumulative-number-of-products-sold-over-time]]
-=== View the cumulative number of products sold on weekends
+[[add-a-data-layer-advanced]]
+=== Monitor multiple series within a date histogram
-To determine the number of orders made only on Saturday and Sunday, create an area chart, then add it to the dashboard.
+It is often required to monitor multiple series within a time interval. These series can be have similar configurations with few changes between one and another.
+*Lens* copies a function when you drag and drop it to the *Drop a field or click to add*
+field within the same group, or when you drag and drop to the *Duplicate* field on a different group.
+You can also drag and drop using your keyboard. For more information, refer to <>.
-. Open *Lens*.
+To quickly create many copies of a percentile metric that shows distribution of price over time:
-. From the *Chart Type* dropdown, select *Area*.
+. From the *Chart Type* dropdown, select *Line*.
+
[role="screenshot"]
-image::images/lens_advanced_2_1_1.png[Chart type menu with Area selected]
+image::images/lens_advanced_2_1.png[Chart type menu with Line selected]
-. Configure the cumulative sum of the store orders.
-
-.. From the *Available fields* list, drag and drop *Records* to the visualization builder.
+. From the *Available fields* list, drag and drop *products.price* to the visualization builder.
-.. From the editor, click *Count of Records*.
+. Create the 95th percentile.
-.. From *Select a function*, click *Cumulative sum*.
+.. In the editor, click *Median of products.price*.
-.. In the *Display name* field, enter `Cumulative orders during weekend days`, then click *Close*.
+.. From *Select a function*, click *Percentile*.
-. Filter the results to display the data for only Saturday and Sunday.
+.. In the *Display name* field, enter `95th`, then click *Close*.
-.. From the editor, click the *Drop a field or click to add* field for *Break down by*.
+. To create the 90th percentile, duplicate the `95th` percentile.
-.. From *Select a function*, click *Filters*.
+.. Drag and drop *95th* to *Drop a field or click to add*.
-.. Click *All records*.
+.. Click *95th [1]*, then enter `90` in the *Percentile* field.
-.. In the *KQL* field, enter `day_of_week : "Saturday" or day_of_week : "Sunday"`, then press Return.
-+
-The <> displays all documents where `day_of_week` matches `Saturday` or `Sunday`.
+.. In the *Display name* field enter `90th`, then click *Close*.
+
[role="screenshot"]
-image::images/lens_advanced_2_1.png[Filter aggregation to filter weekend days]
+image::images/lens_advanced_2_2.gif[Easily duplicate the items with drag and drop]
-. To hide the legend, open the *Legend* menu, then click *Hide*.
+.. Repeat the duplication steps to create the `50th` and `10th` percentile, naming them accordingly.
+
+. To change the left axis label, open the *Left Axis* menu, then enter `Percentiles for product prices` in the *Axis name* field.
+
[role="screenshot"]
-image::images/lens_advanced_2_2_1.png[Legend menu]
+image::images/lens_advanced_2_2_1.png[Left Axis menu]
+
-You have an area chart that shows you how many orders your store received during the weekend.
+You have a line chart that shows you the price distribution of products sold over time.
+
[role="screenshot"]
-image::images/lens_advanced_2_2.png[Line chart with cumulative sum of orders made on the weekend]
+image::images/lens_advanced_2_3.png[Percentiles for product prices chart]
-. Click *Save and return*.
+. Add the filter for the redirect codes.
[discrete]
-[[add-a-data-layer-advanced]]
-=== Create multiple key percentiles of product prices
-
-To view the price distribution of products sold over time, create a percentile chart, then add it to the dashboard.
-
-. Open *Lens*.
-
-. From the *Chart Type* dropdown, select *Line*.
-
-. From the *Available fields* list, drag and drop the data fields to the *Drop a field or click to add* fields in the editor.
-
-* Drag and drop *products.price* to the *Vertical axis* field.
+[[add-a-data-layer]]
+==== Multiple chart types or index patterns in one visualization
-* Drag and drop *order_date* to the *Horizontal axis* field.
+You can add multiple metrics to a single chart type, but if you want to overlay
+multiple chart types or index patterns, use a second layer. When building layered charts,
+it is important to match the data on the horizontal axis so that it uses the same
+scale. To add a line chart layer on top of an existing chart:
-. Create the 95th percentile.
+To compare product prices with customers traffic:
-.. In the editor, click *Median of products.price*.
+. From the *Available fields* list, drag and drop *products.price* to the visualization builder.
-.. From *Select a function*, click *Percentile*.
+.. In the *KQL* field, enter `response.keyword>=500 AND response.keyword<600`.
-.. In the *Display name* field, enter `95th`, then click *Close*.
+.. From *Select a function*, click *Average*.
-. To create the 90th percentile, duplicate the `95th` percentile.
+.. In the *Display name* field, enter `Average of prices`, then click *Close*.
-.. Drag and drop *95th* to *Drop a field or click to add*.
+. From the *Chart Type* dropdown, select *Area*.
-.. Click *95th [1]*, then enter `90` in the *Percentile* field.
+. Create a new layer to overlay with custom traffic.
-.. In the *Display name* field enter `90th`, then click *Close*.
+. In the editor, click *+*.
+
[role="screenshot"]
-image::images/lens_advanced_3_1.gif[Easily duplicate the items with drag and drop]
-
-. Create the 50th percentile.
-
-.. Drag and drop *90th* to *Drop a field or click to add*.
+image::images/lens_advanced_3_1.png[Add new layer button]
-.. Click *90th [1]*, then enter `50` in the *Percentile* field.
+. From the *Available fields* list, drag and drop *customer_id* to the *Vertical Axis* of the newly created layer.
-.. In the *Display name* field enter `50th`, then click *Close*.
+.. In the editor, click *Unique count of customer_id*.
-. Create the 10th percentile.
+.. In the *Display name* field, enter `Unique customers`, then click *Close*.
-.. Drag and drop *50th* to *Drop a field or click to add*.
+. In the *Series color* field, enter *#D36086*, then click *Close*.
-.. Click *50th [1]*, then enter `10` in the *Percentile* field.
+. For *Axis side*, click *Right*, then click *Close*.
-.. In the *Display name* field enter `10th`, then click *Close*.
+. From the *Available fields* list, drag and drop *order_date* to the *Horizontal Axis* of the newly created layer.
-. To change the left axis label, open the *Left Axis* menu, then enter `Percentiles for product prices` in the *Axis name* field.
+. From the new layer editor, click the *Chart type* dropdown, then click the line chart.
+
[role="screenshot"]
-image::images/lens_advanced_3_1_1.png[Left Axis menu]
-+
-You have a line chart that shows you the price distribution of products sold over time.
-+
-[role="screenshot"]
-image::images/lens_advanced_3_3.png[Percentiles for product prices chart]
+image::images/lens_advanced_3_2.png[Change layer type]
+
+The visualization is done, but the legend uses a lot of space. Change the legend position to the top of the chart.
+
+. From the *Legend* dropdown, select the top position.
. Click *Save and return*.
[discrete]
-[[add-the-response-code-filters-advanced]]
-=== View the moving average of inventory prices
+[[percentage-stacked-area]]
+=== Compare the change in percentage over time
-To view and analyze the prices of shoes, accessories, and clothing in the store inventory, create a line chart.
+By default, *Lens* shows *date histograms* using a stacked chart visualization, which helps understand how distinct sets of documents perform over time. Sometimes it is useful to understand how the distributions of these sets change over time.
+Combine *filters* and *date histogram* functions to see the change over time in specific
+sets of documents. To view this as a percentage, use a *stacked percentage* bar or area chart.
-. Open *Lens*.
+To see sales change of product by type over time:
-. From the *Chart Type* dropdown, select *Line*.
+. From the *Available fields* list, drag and drop *Records* to the visualization builder.
-. From the *Available fields* list, drag and drop *products.price* to the visualization builder.
+. Click *Bar vertical stacked*, then select *Area percentage*.
-. In the editor, click the *Drop a field or click to add* field for *Break down by*.
+For each category type that you want to break down, create a filter.
+
+. In the editor, click the *Drop a field or click to add* field for *Break down by*.
. From *Select a function*, click *Filters*.
-. Add a filter for shoes.
+. Add the filter for the clothing category.
.. Click *All records*.
-.. In the *KQL* field, enter `category.keyword : *Shoes*`.
+.. In the *KQL* field, enter `category.keyword : *Clothing`.
+
+.. In the *Label* field, enter `Clothing`, then press Return.
+
+. Add the filter for the shoes category.
+
+.. Click *Add a filter*.
+
+.. In the *KQL* field, enter `category.keyword : *Shoes`.
.. In the *Label* field, enter `Shoes`, then press Return.
-. Add a filter for accessories.
+. Add the filter for the accessories category.
.. Click *Add a filter*.
-.. In the *KQL* field, enter `category.keyword : *Accessories*`.
+.. In the *KQL* field, enter `category.keyword : *Accessories`.
.. In the *Label* field, enter `Accessories`, then press Return.
-. Add a filter for clothing.
+Change the legend position to the top of the chart.
-.. Click *Add a filter*.
+. From the *Legend* dropdown, select the top position.
-.. In the *KQL* field, enter `category.keyword : *Clothing*`.
++
+[role="screenshot"]
+image::images/lens_advanced_4_1.png[Prices share by category]
-.. In the *Label* field, enter `Clothing`, then press Return.
+ Click *Save and return*.
+
+[discrete]
+[[view-the-cumulative-number-of-products-sold-on-weekends]]
+=== View the cumulative number of products sold on weekends
+
+To determine the number of orders made only on Saturday and Sunday, create an area chart, then add it to the dashboard.
+
+. Open *Lens*.
+
+. From the *Chart Type* dropdown, select *Area*.
+
+. Configure the cumulative sum of the store orders.
+
+.. From the *Available fields* list, drag and drop *Records* to the visualization builder.
+
+.. From the editor, click *Count of Records*.
+
+.. From *Select a function*, click *Cumulative sum*.
+
+.. In the *Display name* field, enter `Cumulative orders during weekend days`, then click *Close*.
+
+. Filter the results to display the data for only Saturday and Sunday.
+
+.. From the editor, click the *Drop a field or click to add* field for *Break down by*.
+
+.. From *Select a function*, click *Filters*.
+
+.. Click *All records*.
+
+.. In the *KQL* field, enter `day_of_week : "Saturday" or day_of_week : "Sunday"`, then press Return.
++
+The <> displays all documents where `day_of_week` matches `Saturday` or `Sunday`.
++
+[role="screenshot"]
+image::images/lens_advanced_5_1.png[Filter aggregation to filter weekend days]
+
+. To hide the legend, open the *Legend* menu, then click *Hide*.
++
+[role="screenshot"]
+image::images/lens_advanced_5_2_1.png[Legend menu]
++
+You have an area chart that shows you how many orders your store received during the weekend.
-. Click *Close*
+. Click *Bar vertical stacked*, then select *Area*.
+
[role="screenshot"]
-image::images/lens_advanced_4_1.png[Median prices chart for different categories]
+image::images/lens_advanced_5_2.png[Line chart with cumulative sum of orders made on the weekend]
+
+. Click *Save and return*.
[discrete]
-[[add-the-moving-average]]
-==== Add the moving average
+[[view-customers-over-time-by-continents]]
+=== View table of customers by category over time
+
+Tables are an alternative type of visualization for time series, useful when you want to read the actual values.
+You can build a date histogram table, and group the customer count metric by category, like the continent registered in their profile.
+
+In *Lens* you can split the metric in a table leveraging the *Columns* field, where each data value from the aggregation is used as column of the table and the relative metric value is shown.
+
+To build a date histogram table:
+
+. Open *Lens*.
+
+. From the *Chart Type* dropdown, select *Table*.
-To focus on the general trends rather than on the peaks in the data, add the moving average, then add the visualization to the dashboard.
+.. From the *Available fields* list, drag and drop *customer_id* to the *Metrics* field of the editor.
-. In the editor, click the *Median of products.price*.
+.. From the editor, click *Unique count of customer_id*.
-. From *Select a function*, click *Moving average*.
+.. In the *Display name* field, enter `Customers`, then click *Close*.
-. In the *Window size* field, enter `7`, then click *Close*.
+.. From the *Available fields* list, drag and drop *order_date* to the *Rows* field of the editor.
+
+.. From the editor *Rows*, click the *order_date* field just dropped.
+
+. Select *Customize time interval*.
+
+. Change the *Minimum interval* to `1 days`, then click *Close*.
+
+.. In the *Display name* field, enter `Sale`, then click *Close*.
+
+To split the customers count by continent:
+
+. From the *Available fields* list, drag and drop *geoip.continent_name* to the *Columns* field of the editor.
+
[role="screenshot"]
-image::images/lens_advanced_4_2.png[Moving average prices chart for different categories]
+image::images/lens_advanced_6_1.png[Table with daily customers by continent configuration]
. Click *Save and return*.
[discrete]
=== Save the dashboard
+By default the dashboard attempts to match the palette across panels, but in this case there's no need for that, so it can be disabled.
+
+[role="screenshot"]
+image::images/lens_advanced_7_1.png[Disable palette sync in dashboard]
+
Now that you have a complete overview of your ecommerce sales data, save the dashboard.
. In the toolbar, click *Save*.
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_1.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_1.png
deleted file mode 100644
index 1c752972791af6f..000000000000000
Binary files a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_1.png and /dev/null differ
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_2.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_2.png
index de719c852dfdef2..ea58057433a19eb 100644
Binary files a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_2.png and b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_2.png differ
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_2_1.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_2_1.png
index 9408cd736b27eea..fa404d50b4dd1f5 100644
Binary files a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_2_1.png and b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_2_1.png differ
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_3.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_3.png
index 4302c95eb8a2512..b069acae43163ed 100644
Binary files a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_3.png and b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_1_3.png differ
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_2_1.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_2_1.png
deleted file mode 100644
index c08a865e52137f4..000000000000000
Binary files a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_2_1.png and /dev/null differ
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_2_1_1.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_2_1_1.png
index 48712ec5ef224e4..e996b58520d410d 100644
Binary files a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_2_1_1.png and b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_2_1_1.png differ
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_2_1_2.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_2_1_2.png
new file mode 100644
index 000000000000000..a524dd1e456f6f0
Binary files /dev/null and b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_2_1_2.png differ
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_4_3.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_4_3.png
index b70910fdd2500c0..2cd75d2797a981a 100644
Binary files a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_4_3.png and b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_4_3.png differ
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_6_2.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_6_2.png
deleted file mode 100644
index e95b3d9a97b8d4f..000000000000000
Binary files a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_6_2.png and /dev/null differ
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_7_1.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_7_1.png
deleted file mode 100644
index e63f7e77f1e9f62..000000000000000
Binary files a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_7_1.png and /dev/null differ
diff --git a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_dashboard.png b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_dashboard.png
index 9bce65401f4c966..9e9a5ed3d758eb9 100644
Binary files a/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_dashboard.png and b/docs/user/dashboard/lens-end-to-end/images/lens_end_to_end_dashboard.png differ
diff --git a/docs/user/dashboard/lens.asciidoc b/docs/user/dashboard/lens.asciidoc
index 58476bcae87dffc..94c9db1462760f9 100644
--- a/docs/user/dashboard/lens.asciidoc
+++ b/docs/user/dashboard/lens.asciidoc
@@ -75,6 +75,31 @@ Drag and drop the fields on to the visualization builder, then
[role="screenshot"]
image::images/lens_value_labels_xychart_toggle.png[Lens Bar chart value labels menu]
+[float]
+[[add-fields-in-lens]]
+===== Add fields
+
+Add and define fields to the index pattern that you want to visualize using the {ref}/modules-scripting-painless.html[Painless scripting language].
+The fields that you are add are saved to the index pattern and appear in all visualizations, saved searches, and saved objects that use the index pattern.
+
+. Click *...*, then select *Add field to index pattern*.
++
+[role="screenshot"]
+image:images/runtime-field-menu.png[Dropdown menu located next to index pattern field with items for adding and managing fields, width=50%]
+
+. Enter a *Name* for the field, then select the field *Type*.
+
+. Select *Set value*, then define the field value by emitting a single value using the {ref}/modules-scripting-painless.html[Painless scripting language].
+
+. Click *Save*.
++
+To manage the field, click the field, then click *Edit index pattern field* or *Remove index pattern field*.
++
+[role="screenshot"]
+image:images/manage-runtime-field.gif[Field menu to edit or remove field from index pattern, width=50%]
+
+For more information about adding fields to index patterns and Painless scripting language examples, refer to <>.
+
[float]
[[drag-and-drop-keyboard-navigation]]
===== Create visualization panels with keyboard navigation
diff --git a/docs/user/dashboard/timelion.asciidoc b/docs/user/dashboard/timelion.asciidoc
index ff71cd7b383bdc7..12d0169c13f661b 100644
--- a/docs/user/dashboard/timelion.asciidoc
+++ b/docs/user/dashboard/timelion.asciidoc
@@ -4,7 +4,7 @@
Instead of using a visual editor to create charts, 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 8.0 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]."]
+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]."]
[float]
==== Timelion expressions
@@ -554,4 +554,4 @@ Save and add the panel to the dashboard.
. Click *Save and return*.
-For more information about *Timelion* conditions, refer to https://www.elastic.co/blog/timeseries-if-then-else-with-timelion[I have but one .condition()].
\ No newline at end of file
+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 ff1c16c14d46715..5c4ce8e365e865f 100644
--- a/docs/user/dashboard/tsvb.asciidoc
+++ b/docs/user/dashboard/tsvb.asciidoc
@@ -2,7 +2,8 @@
=== TSVB
*TSVB* enables you to visualize the data from multiple data series, supports <>, multiple visualization types, custom functions, and some math. To use *TSVB*, your data must have a date field.
+most {es} metric aggregations>>, multiple visualization types, custom functions, and some math.
+To create *TSVB* visualization panels, your data must have a time field.
[role="screenshot"]
image::visualize/images/tsvb-screenshot.png[TSVB overview]
@@ -17,15 +18,19 @@ Open *TSVB*, then make sure the required settings are configured.
. On the *New visualization* window, click *TSVB*.
-. In *TSVB*, click *Panel options*, then make sure the following settings are configured:
+. In *TSVB*, click *Panel options*, then specify the required *Data* settings.
-* *Index pattern*
-* *Time field*
-* *Interval*
+.. From the *Index pattern* dropdown, select the index pattern you want to visualize.
++
+To visualize an {es} index, open the *Index pattern select mode* menu, deselect *Use only {kib} index patterns*, then enter the {es} index.
+
+.. From the *Time field* dropdown, select the field you want to visualize, then enter the field *Interval*.
-. Select a *Drop last bucket* option. It is dropped by default because the time filter intersects the time range of the last bucket, but can be enabled to see the partial data.
+.. Select a *Drop last bucket* option.
++
+By default, *TSVB* drops the last bucket because the time filter intersects the time range of the last bucket. To view the partial data, select *No*.
-. In the *Panel filter* field, specify any <> to select specific documents.
+.. In the *Panel filter* field, enter <> to view specific documents.
[float]
[[configure-the-data-series]]
diff --git a/docs/user/dashboard/tutorial-create-a-dashboard-of-lens-panels.asciidoc b/docs/user/dashboard/tutorial-create-a-dashboard-of-lens-panels.asciidoc
index 22483b28018488c..1375aa8d5934e73 100644
--- a/docs/user/dashboard/tutorial-create-a-dashboard-of-lens-panels.asciidoc
+++ b/docs/user/dashboard/tutorial-create-a-dashboard-of-lens-panels.asciidoc
@@ -1,10 +1,10 @@
[[create-a-dashboard-of-panels-with-web-server-data]]
-== Tutorial: Create a dashboard of panels with web server data
+== Build your first dashboard
-You collected data from your web server, and you want to visualize and analyze the data on a dashboard. To create dashboard panels of the data, open the *Lens* visualization builder, then
-create the visualization panels that best display the data.
-
-When you've completed the tutorial, you'll have a dashboard that provides you with a complete overview of your web server data.
+Learn the most common ways to build a dashboard from your own data.
+The tutorial will use sample data from the perspective of an analyst looking
+at website logs, but this type of dashboard works on any type of data.
+Before using this tutorial, you should be familiar with the <>.
[role="screenshot"]
image::images/lens_end_to_end_dashboard.png[Final dashboard vis]
@@ -13,46 +13,78 @@ image::images/lens_end_to_end_dashboard.png[Final dashboard vis]
[[add-the-data-and-create-the-dashboard]]
=== Add the data and create the dashboard
-To create visualizations of the data from the web server, add the data set, then create the dashboard.
+Install the sample web logs data that you'll use to create your dashboard.
-. From the {kib} *Home* page, click *Try our sample data*.
+. On the {kib} *Home* page, click *Try our sample data*.
. From *Sample web logs*, click *Add data*.
-. Open the main menu, click *Dashboard*.
+Then create a new dashboard:
+
+. Open the main menu, then click *Dashboard*.
. Click *Create dashboard*.
+. Set the <> to *Last 90 days*.
+
[float]
[[open-and-set-up-lens]]
-=== Open and set up Lens
+=== Open Lens and get familiar with the data
-With *Lens*, you identify the data fields you want to visualize, drag and drop the fields, then watch as
-*Lens* uses heuristics to apply the fields and create a visualization for you.
+. On the dashboard, click *Create visualization*.
-. From the dashboard, click *Create panel*.
-
-. On the *New visualization* window, click *Lens*.
+. Make sure the *kibana_sample_data_logs* index appears. You might need to select
+a different index pattern from the dropdown:
+
[role="screenshot"]
-image::images/lens_end_to_end_1_1.png[New visualization popover]
+image::images/lens_end_to_end_1_2.png[Lens index pattern selector, width=50%]
+
+This tutorial uses `Records`, timestamp`, `bytes`, `clientip`, and `referer.keyword`.
+To see the most frequent values of a field, click the field name to view a summary.
+
+The main elements of *Lens* are named:
-. Make sure the *kibana_sample_data_logs* index appears.
+Workspace panel:: Displays your visualization. You can drag and drop onto this area.
+Dimensions:: Each dimension is a function with extra options. Dimensions are grouped
+for each visualization type, for example the *Vertical axis* is a group that allows
+multiple dimensions. Each dimension starts empty with the label *Drop a field or click to add*.
+Functions:: There are two main types of functions: *buckets* and *metrics*, which are
+equivalent to what {es} provides.
[discrete]
[[view-the-number-of-website-visitors]]
-=== View the number of website visitors
+=== Create your first visualization
-To determine how many users have visited your website within the last 90 days, create a metric visualization, then add it to the dashboard.
+Every time you build a visualization in *Lens*, you need to:
-. Set the <> to *Last 90 days*.
+* *Choose your visualization.* Do you know the type of visualization you'd like to use?
+If you do, select the type before dragging any fields. If you don't, you can change the
+visualization type after configuring your functions.
+
+* *Choose your field.* Do you know the dimension group you want to use the field in? If you do,
+drag and drop the field from the field list to your chosen dimension and Lens will pick a function for you.
+If you don't, drag and drop the field onto the workspace panel. Skip this step if you are
+using the *Filters* function.
+
+* *Edit and delete.* To change the function or styling options, click the dimension to open
+the configuration panel. To delete a specific dimension, close the configuration panel and click
+the delete button. To reset the entire visualization, click *Reset layer*.
+
+To put this into practice, pick a field you want to analyze, such as `clientip`. If you want
+to analyze only this field, you can use *Metric* to show a big number.
+The only number function that you can use with `clientip` is *Unique count*.
+*Unique count*, also known as cardinality, approximates the number of unique values
+of the `clientip` field.
-. From the *Chart Type* dropdown, select *Metric*.
+. To select the visualization type, click *Bar vertical stacked* to open the chart type dropdown, then select *Metric*.
+
[role="screenshot"]
-image::images/lens_end_to_end_1_2_1.png[Chart Type dropdown with Metric selected]
+image::images/lens_end_to_end_1_2_1.png[Chart Type dropdown with Metric selected, width=50%]
-. From the *Available fields* list, drag and drop *clientip* to the visualization builder.
+. From the *Available fields* list, drag and drop `clientip` to the workspace panel.
+Lens selects *Unique count* because it is the only numeric function
+that works for IP addresses. You can also drag and drop `clientip` onto
+the empty dimension for the same result.
+
[role="screenshot"]
image::images/lens_end_to_end_1_3.png[Changed type and dropped clientip field]
@@ -68,294 +100,220 @@ image::images/lens_end_to_end_1_4.png[Flyout config open]
. Click *Save and return*.
-[discrete]
-[[view-the-distribution-of-visitors-by-operating-system]]
-=== View the distribution of visitors by operating system
-
-To determine the operating systems you should continue to support, and the importance of mobile traffic from iOS devices,
-create a donut chart that displays the top operating systems that your visitors used to access your website within the last 90 days.
+. Customize the newly added panel:
-. Open *Lens*, then set the <> to *Last 90 days*.
+.. Drag the bottom corner of the panel until the metric takes up one quarter of the screen
+width. The row for the metric will have 4 items on it later.
-. From the *Chart Type* dropdown, select *Donut*.
+.. The metric visualization has its own label, so you do not need to add a panel title.
-. From the *Available fields* list, drag and drop the data fields to the *Drop a field or click to add* fields in the editor.
+. Click *Save* on the dashboard menu
-.. Drag and drop *clientip* to the *Size by* field.
+. In the *Title* field, enter `Logs dashboard`.
-.. Drag and drop *machine.os.keyword* to the *Slice by* field.
-+
-[role="screenshot"]
-image::images/lens_end_to_end_2_1_1.png[Donut chart with clientip and machine.os.keyword fields]
-
-. Change the color palette.
-
-.. In the editor, click *Top values of machine.os.keyword*.
-
-.. From the *Color palette* dropdown, select *Compatibility*.
-
-.. Click *Close*.
-+
-[role="screenshot"]
-image::images/lens_end_to_end_2_1.png[Donut chart with open config panel]
+. Select *Store time with dashboard* box, then click *Save*.
-. Click *Save and return*.
+. After the dashboard refreshes, click *Edit* again.
[discrete]
[[mixed-multiaxis]]
-=== View the average of bytes transfer per day
+=== View a metric over time
-To prevent potential server failures, and optimize the cost of website maintenance, create an area chart that displays the average of bytes transfer. To compare
-the data to the number of visitors to your website, add a line chart layer.
+*Lens* has two shortcuts that simplify viewing metrics over time.
+If you drag and drop a numeric field to the workspace panel, *Lens* adds the default
+time field from the index pattern. If the *Date histogram* function is being used,
+quickly replace the time field by dragging and dropping on the workspace panel.
-. Open *Lens*.
+To visualize the `bytes` field over time without choosing a visualization type or function:
-. From the *Available fields* list, drag and drop *bytes* to the visualization builder.
+. From the *Available fields* list, drag and drop `bytes` onto the workspace panel to have *Lens* automatically
+create a chart. *Lens* creates a bar chart with two dimensions, *timestamp* and *Median of bytes*.
+
+. *Lens* automatically chooses a date interval. To zoom in on the data you want to view,
+click and drag your cursor across the bars.
-. To zoom in on the data you want to view, click and drag your cursor across the bars.
-+
[role="screenshot"]
image::images/lens_end_to_end_3_1_1.gif[Zoom in on the data]
-. Change the *timestamp* interval.
-
-.. In the editor, click *timestamp*.
+To emphasize the change in *Median of bytes* over time, use a line chart.
+To change the visualization type, use one of the following ways:
-.. Select *Customize time interval*.
+* From the *Suggestions*, click the line chart.
+* Click *Bar vertical stacked*, then select *Line*.
+* Click the chart type icon above *Horizontal axis*, then click the line icon.
-.. Change the *Minimum interval* to `1 days`, then click *Close*.
-+
-[role="screenshot"]
-image::images/lens_end_to_end_3_1.png[Customize time interval]
+Most users use the automatic time interval. You can increase and decrease
+the minimum interval that *Lens* uses, but you cannot decrease the interval
+below the {kib} advanced settings. To set the minimum time interval:
-. From the *Chart Type* dropdown, select *Area*.
+. In the editor, click *timestamp*.
-[discrete]
-[[add-a-data-layer]]
-==== Add the line chart layer
+. Click *How it works* to learn about the *Lens* minimum interval
-To compare the average of bytes transfer to the number of users that visit your website, add a line chart layer.
+. Select *Customize time interval*.
-. In the editor, click *+*.
+. Increase the *Minimum interval* to `1 days`, then click *Close*.
+
[role="screenshot"]
-image::images/lens_end_to_end_3_2.png[Add new layer button]
+image::images/lens_end_to_end_3_1.png[Customize time interval]
-. From the new layer editor, click the *Chart type* dropdown, then click the line chart.
+To save space on the dashboard, so to save space, hide the vertical and horizontal
+axis labels.
+
+. Open the *Left axis* menu, then deselect *Show*.
+
[role="screenshot"]
-image::images/lens_end_to_end_3_3.png[Change layer type]
-+
-The chart type for the visualization changes to *Mixed XY*.
-
-. From the *Available fields* list, drag and drop the data fields to the *Drop a field or click to add* fields in the editor.
-
-.. Drag and drop *timestamp* to the *Horizontal axis* field.
-
-.. Drag and drop *clientip* to the *Vertical axis* field.
-
-. Change the *timestamp* interval.
+image::images/lens_end_to_end_4_3.png[Turn off axis name]
-.. In the editor, click *timestamp* in the line chart layer.
+. Open the *Bottom axis* menu, then deselect *Show*.
-.. Select *Customize time interval*.
+. Click *Save and return*
-.. Change the *Minimum interval* to `1 days`, then click *Close*.
+. On the dashboard, move the panel so that it is in the same row as the *Metric* visualization panel. The two should
+take up half the screen width.
-. Change the *Unique count of clientip* label and color.
+. Add a panel title to explain the panel, which is necessary because you removed the axis labels.
-.. In the editor, click *Unique count of clientip*.
+.. Open the panel menu and choose *Edit panel title*.
-.. In the *Display name* field, enter `Unique visitors` in the line chart layer.
+.. In the *Title* field, enter `Median of bytes`, then click *Save*.
-.. In the *Series color* field, enter *#CA8EAE*, then click *Close*.
+. In the toolbar, click *Save*.
[discrete]
-[[configure-the-multiaxis-chart]]
-==== Configure the y-axes
-
-There is a significant difference between the *timestamp per day* and *Unique visitors* data, which makes the *Unique visitors* data difficult to read. To improve the readability,
-display the *Unique visitors* data along a second y-axis, then change the formatting. When functions contain multiple formats, separate axes are created by default.
+[[view-the-distribution-of-visitors-by-operating-system]]
+=== View the top values of a field
-. In the editor, click *Unique visitors* in the line chart layer.
+The *Top values* function ranks the unique values of a field by another dimension.
+The values are the most frequent when ranked by a *Count* dimension.
+The values are the largest when ranked by a *Sum* dimension.
-. For *Axis side*, click *Right*, then click *Close*.
+When you drag and drop a text or IP address field onto the workspace panel,
+*Lens* adds a *Top values* function ranked by *Count of records* to show the most frequent values.
-[float]
-[[change-the-visualization-type]]
-==== Change the visualization type
+For this tutorial, you have picked a field and function, but not a visualization type.
+You want to see the most frequent values of `request.keyword` on your website, ranked by the unique visitors.
+This means that you want to use *Top values of request.keyword* ranked by *Unique count of clientip*, instead of
+being ranked by *Count of records*.
-. In the editor, click *Average of bytes* in the area chart layer.
+. From the *Available fields* list, drag and drop `clientip` onto the *Vertical axis*.
+*Lens* chooses the function for you when you drop onto a dimension, which is *Unique count* here.
+Do not drop the field into the main workspace because `clientip` will be added to the wrong axis.
-. From the *Value format* dropdown, select *Bytes (1024)*, then click *Close*.
+. Drag and drop `request.keyword` to the main workspace. *Lens* adds *Top values of request.keyword*
+to the *Horizontal axis*.
+
[role="screenshot"]
-image::images/lens_end_to_end_3_4.png[Multiaxis chart]
-
-[discrete]
-[[lens-legend-position]]
-==== Change the legend position
+image::images/lens_end_to_end_2_1_1.png[Vertical bar chart with top values of request.keyword by most unique visitors]
-The visualization is done, but the legend uses a lot of space. Change the legend position to the top of the chart.
+This chart is hard to read because the `request.keyword` field contains long text. You could try
+using one of the *Suggestions*, but the suggestions also have issues with long text. Instead, switch
+to the *Table* visualization.
-. From the *Legend* dropdown, select the top position.
+Click *Bar vertical stacked*, then select *Table*.
+
[role="screenshot"]
-image::images/lens_end_to_end_3_5.png[legend position]
-
-. Click *Save and return*.
-
-[discrete]
-[[percentage-stacked-area]]
-=== View the health of your website
+image::images/lens_end_to_end_2_1_2.png[Table with top values of request.keyword by most unique visitors]
-To detect unusual traffic, bad website links, and server errors, create a percentage stacked area chart that displays the associated response codes.
+Next, customize the table.
-. Open *Lens*.
+. Click the *Top values of request.keyword* dimension.
-. From the *Available fields* list, drag and drop the data fields to the *Drop a field or click to add* fields in the editor.
+.. Increase the *Number of values*. The maximum allowed value is 1000.
-.. Drag and drop *Records* to the *Vertical axis* field.
+.. In the *Display name* field, enter `Page URL`, then click *Close*.
-.. Drag and drop *@timestamp* to the *Horizontal axis* field.
+. Click *Save and return*.
-. From the *Chart Type* dropdown, select *Percentage bar*.
+. Move the table panel so that it has its own row, but do not change the size.
-. To remove the vertical axis label, click *Left axis*, then deselect *Show*.
-+
-[role="screenshot"]
-image::images/lens_end_to_end_4_3.png[Turn off axis name]
+NOTE: You do not need a panel title because the table columns are clearly labeled.
[discrete]
-[[add-the-response-code-filters]]
-==== Add the response code filters
-
-For each response code that you want to display, create a filter.
-
-. In the editor, click the *Drop a field or click to add* field for *Break down by*.
-
-. From *Select a function*, click *Filters*.
-
-. Add the filter for the successful response codes.
+[[custom-ranges]]
+=== Compare a subset of documents to all documents
-.. Click *All records*.
+To compare a field on subset of documents to all documents, you need to select two or more sets of documents that add up to 100%.
+For this example, we are comparing documents where the `bytes` field is under 10 Kb to documents where `bytes` is over 10 Kb,
+which are two sets that do not overlap.
-.. In the *KQL* field, enter `response.keyword>=200 AND response.keyword<300`.
+Use *Intervals* to select documents based on the number range of a field. Use *Filters* when your criteria
+is not numeric, or when your query needs multiple clauses.
-.. In the *Label* field, enter `2XX`, then press Return.
-+
-[role="screenshot"]
-image::images/lens_end_to_end_4_1.png[First filter in filters aggregation]
+Use a proportion chart to display the values as a percentage of the sum of all values. Lens has 5 types of proportion charts:
+pie, donut, treemap, percentage bar and percentage area.
-. Add the filter for the redirect codes.
+To determine if your users transfer more `bytes` from small files versus large files,
+configure dimensions with *Intervals* and *Sum*, then switch to a pie chart to display as a percentage:
-.. Click *Add a filter*.
+. From the *Available fields* list, drag and drop `bytes` to *Vertical axis* in the editor.
-.. In the *KQL* field, enter `response.keyword>=300 AND response.keyword<400`.
+. Click *Median of bytes*, select *Sum*, then click *Close*.
-.. In the *Label* field, enter `3XX`, then press Return.
+. From the *Available fields* list, drag and drop `bytes` to *Break down by* in the editor, then specify the file size ranges.
-. Add the filter for the client error codes.
+.. In the editor, click *bytes*.
-.. Click *Add a filter*.
+.. Click *Create custom ranges*, enter the following, then press Return:
-.. In the *KQL* field, enter `response.keyword>=400 AND response.keyword<500`.
+* *Ranges* — `0` -> `10240`
-.. In the *Label* field, enter `4XX`, then press Return.
+* *Label* — `Below 10KB`
-. Add the filter for the server error codes.
+.. Click *Add range*, enter the following, then press Return:
-.. Click *Add a filter*.
+* *Ranges* — `10240` -> `+∞`
-.. In the *KQL* field, enter `response.keyword>=500 AND response.keyword<600`.
+* *Label* — `Above 10KB`
++
+[role="screenshot"]
+image::images/lens_end_to_end_6_1.png[Custom ranges configuration]
-.. In the *Label* field, enter `5XX`, then press Return.
+.. From the *Value format* dropdown, select *Bytes (1024)*, then click *Close*.
-. To change the color palette, select *Status* from the *Color palette* dropdown, then click *Close*.
+. From the *Chart Type* dropdown, select *Pie*.
. Click *Save and return*.
[discrete]
[[histogram]]
-=== View the traffic for your website by the hour
-
-To find the best time to shut down your website for maintenance, create a histogram that displays the traffic for your website by the hour.
-
-. Open *Lens*.
-
-. From the *Available fields* list, drag and drop *bytes* to *Vertical axis* in the editor, then configure the options.
+=== View a the distribution of a number field
-.. Click *Average of bytes*.
+Knowing the distribution of a number helps to find patterns. For example, you could
+look at the website traffic per hour to find the best time to do routine maintenance.
+Use *Intervals* to see an evenly spaced distribution of a number field.
-.. From *Select a function*, click *Sum*.
+. From the *Available fields* list, drag and drop `bytes` to *Vertical axis* in the editor.
-.. In the *Display name* field, enter `Transferred bytes`.
+. Click *Median of bytes*, then select *Sum*.
-.. From the *Value format* dropdown, select `Bytes (1024)`, then click *Close*.
+. In the *Display name* field, enter `Transferred bytes`.
-. From the *Available fields* list, drag and drop *hour_of_day* to *Horizontal axis* in the editor, then configure the options.
+. From the *Value format* dropdown, select `Bytes (1024)`, then click *Close*.
-.. Click *hour_of_day*.
+. From the *Available fields* list, drag and drop *hour_of_day* to *Horizontal axis* in the editor.
-.. Click and slide the *Intervals granularity* slider until the horizontal axis displays hourly intervals.
+. Click *hour_of_day*, and then slide the *Intervals granularity* slider until the horizontal axis displays hourly intervals.
+
[role="screenshot"]
image::images/lens_end_to_end_5_2.png[Create custom ranges]
. Click *Save and return*.
-[discrete]
-[[custom-ranges]]
-=== View the percent of small versus large transferred files
-
-To determine if your users transfer more small files versus large files, create a pie chart that displays the percentage of each size.
-
-. Open *Lens*.
-
-. From the *Available fields* list, drag and drop *bytes* to *Vertical axis* in the editor, then configure the options.
-
-.. Click *Average of bytes*.
-
-.. From *Select a function*, click *Sum*, then click *Close*.
-
-. From the *Available fields* list, drag and drop *bytes* to *Break down by* in the editor, then specify the file size ranges.
-
-.. Click *bytes*.
-
-.. Click *Create custom ranges*, enter the following, then press Return:
-
-* *Ranges* — `0` -> `10240`
-
-* *Label* — `Below 10KB`
-
-.. Click *Add range*, enter the following, then press Return:
-
-* *Ranges* — `10240` -> `+∞`
-
-* *Label* — `Above 10KB`
-+
-[role="screenshot"]
-image::images/lens_end_to_end_6_1.png[Custom ranges configuration]
-
-.. From the *Value format* dropdown, select *Bytes (1024)*, then click *Close*.
+. Decrease the panel size, then drag and drop it to the first row next to the `Median of bytes` panel. There
+should be four panels in a row.
-. From the *Chart Type* dropdown, select *Pie*.
-+
-[role="screenshot"]
-image::images/lens_end_to_end_6_2.png[Files size distribution]
-
-. Click *Save and return*.
+. You do not need a panel title because the axis labels are self-explanatory.
[discrete]
[[treemap]]
-=== View the top sources of website traffic
-
-To determine how users find out about your website and where your users are located, create a treemap that displays the percentage of users that
-enter your website from specific social media websites, and the top countries where users are located.
+=== Create a multi-level chart
-. Open *Lens*.
+*Lens* lets you use multiple functions in the data table and proportion charts. For example,
+to create a chart that breaks down the traffic sources and user geography, use *Filters* and
+*Top values*.
-. From the *Chart Type* dropdown, select *Treemap*.
+. Click *Bar vertical stacked*, then select *Treemap*.
. From the *Available fields* list, drag and drop *Records* to the *Size by* field in the editor.
@@ -377,21 +335,15 @@ enter your website from specific social media websites, and the top countries wh
.. Click *Add a filter*, enter the following, then press Return:
-* *KQL* — `NOT referer : *twitter* OR NOT referer: *facebook.com*`
+* *KQL* — `NOT referer : *twitter.com* OR NOT referer: *facebook.com*`
* *Label* — `Other`
.. Click *Close*.
-[discrete]
-[[add-the-countries]]
-==== Add the geographic data
-
-To determine the top countries where users are located, add the geographic data.
+Add the next break down by geography:
-Compare the top sources of website traffic data to the top three countries.
-
-. From the *Available fields* list, drag and drop *geo.src* to the visualization builder.
+. From the *Available fields* list, drag and drop *geo.src* to the main workspace.
. To change the *Group by* order, click and drag *Top values of geo.src* so that it appears first in the editor.
+
@@ -409,6 +361,12 @@ image::images/lens_end_to_end_7_3.png[Group other values as Other]
. Click *Save and return*.
+. Arrange the panel so that it is in the same row as the table.
+
+.. Click the gear icon and choose *Edit panel title*.
+
+.. Enter "Page views by location and referer" as the panel title, then click *Save*.
+
[discrete]
=== Save the dashboard
@@ -417,3 +375,5 @@ Now that you have a complete overview of your web server data, save the dashboar
. In the toolbar, click *Save*.
. On the *Save dashboard* window, enter `Web server data`, then click *Save*.
+
+. If this was not the first time you saved the dashboard, click *Switch to view mode*
diff --git a/docs/user/discover.asciidoc b/docs/user/discover.asciidoc
index 6e3a7f697073d7c..ea413747a2aadf7 100644
--- a/docs/user/discover.asciidoc
+++ b/docs/user/discover.asciidoc
@@ -6,7 +6,7 @@
**_Gain insight to your data._**
*Discover* enables you to quickly search and filter your data, get information
-about structure of the fields, and visualize your data with *Lens* and *Maps*.
+about the structure of the fields, and visualize your data with *Lens* and *Maps*.
You can customize and save your searches and place them on a dashboard.
++++
@@ -110,6 +110,43 @@ image:images/document-table.png[Document table with fields for manufacturer, geo
. To rearrange the table columns, hover the mouse over a
column header, and then use the move and sort controls.
+[float]
+[[add-field-in-discover]]
+=== Add a field
+
+What happens if you forgot to define an important value as a separate field? Or, what if you
+want to combine two fields and treat them as one?
+You can add a field to your index pattern from inside of **Discover**,
+and then use that field for analysis and visualizations,
+the same way you do with other fields.
+
+. Click the ellipsis icon (...), and then click *Add field to index pattern*.
++
+[role="screenshot"]
+image:images/add-field-to-pattern.png[Dropdown menu located next to index pattern field with item for adding a field to an index pattern, width=50%]
+
+. In the *Create field* form, enter `hello` for the name.
+
+. Turn on *Set value*.
+
+. Use the Painless scripting language to define the field:
++
+```ts
+emit("Hello World!");
+```
+
+. Click *Save*.
+
+. In the fields list, search for the *hello* field, and then click it.
++
+You'll see the top values for the field. The pop-up also includes actions for filtering,
+editing, and deleting the field.
++
+[role="screenshot"]
+image:images/hello-field.png[Top values for the hello field, width=50%]
+
+For more information on adding fields and Painless scripting language examples, refer to <>.
+
[float]
[[search-in-discover]]
@@ -186,7 +223,8 @@ You can bookmark this document and share the link.
=== Save your search for later use
Save your search so you can repeat it later, generate a CSV report, or use it in visualizations, dashboards, and Canvas workpads.
-Saving a search saves the query and the filters.
+Saving a search saves the query text, filters,
+and current view of *Discover*—the columns selected in the document table, the sort order, and the index pattern.
. In the toolbar, click **Save**.
diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc
index 83e18734f65d433..397ab1717183b82 100644
--- a/docs/user/management.asciidoc
+++ b/docs/user/management.asciidoc
@@ -131,8 +131,8 @@ Kerberos, PKI, OIDC, and SAML.
[cols="50, 50"]
|===
-a| <>
-|Create and manage the index patterns that retrieve your data from {es}.
+a| <>
+|Manage the data fields in the index patterns that retrieve your data from {es}.
| <>
| Copy, edit, delete, import, and export your saved objects.
@@ -186,10 +186,10 @@ include::{kib-repo-dir}/management/managing-beats.asciidoc[]
include::{kib-repo-dir}/management/action-types.asciidoc[]
-include::{kib-repo-dir}/management/managing-fields.asciidoc[]
-
include::{kib-repo-dir}/management/managing-licenses.asciidoc[]
+include::{kib-repo-dir}/management/manage-index-patterns.asciidoc[]
+
include::{kib-repo-dir}/management/numeral.asciidoc[]
include::{kib-repo-dir}/management/rollups/create_and_manage_rollups.asciidoc[]
diff --git a/docs/user/reporting/reporting-troubleshooting.asciidoc b/docs/user/reporting/reporting-troubleshooting.asciidoc
index c43e9210dd7c800..4305b39653f8dcd 100644
--- a/docs/user/reporting/reporting-troubleshooting.asciidoc
+++ b/docs/user/reporting/reporting-troubleshooting.asciidoc
@@ -16,6 +16,7 @@ Having trouble? Here are solutions to common problems you might encounter while
* <>
* <>
* <>
+* <>
[float]
[[reporting-diagnostics]]
@@ -163,3 +164,12 @@ In this case, try increasing the memory for the {kib} instance to 2GB.
=== ARM systems
Chromium is not compatible with ARM RHEL/CentOS.
+
+[float]
+[[reporting-troubleshooting-maps-ems]]
+=== Unable to connect to Elastic Maps Service
+
+https://www.elastic.co/elastic-maps-service[{ems} ({ems-init})] is a service that hosts
+tile layers and vector shapes of administrative boundaries.
+If a report contains a map with a missing basemap layer or administrative boundary, the {kib} server does not have access to {ems-init}.
+See <> for information on how to connect your {kib} server to {ems-init}.
diff --git a/docs/user/security/authentication/index.asciidoc b/docs/user/security/authentication/index.asciidoc
index 805ae924a599e56..54142a6fe39e341 100644
--- a/docs/user/security/authentication/index.asciidoc
+++ b/docs/user/security/authentication/index.asciidoc
@@ -7,6 +7,7 @@
{kib} supports the following authentication mechanisms:
+- <>
- <>
- <>
- <>
@@ -16,7 +17,12 @@
- <>
- <>
-Enable multiple authentication mechanisms at the same time specifying a prioritized list of the authentication _providers_ (typically of various types) in the configuration. Providers are consulted in ascending order. Make sure each configured provider has a unique name (e.g. `basic1` or `saml1` in the configuration example) and `order` setting. In the event that two or more providers have the same name or `order`, {kib} will fail to start.
+For an introduction to {kib}'s security features, including the login process, refer to <>.
+
+[[multiple-authentication-providers]]
+==== Multiple authentication providers
+
+Enable multiple authentication mechanisms at the same time by specifying a prioritized list of the authentication _providers_ (typically of various types) in the configuration. Providers are consulted in ascending order. Make sure each configured provider has a unique name (e.g. `basic1` or `saml1` in the configuration example) and `order` setting. In the event that two or more providers have the same name or `order`, {kib} will fail to start.
When two or more providers are configured, you can choose the provider you want to use on the Login Selector UI. The order the providers appear is determined by the `order` setting. The appearance of the specific provider entry can be customized with the `description`, `hint`, and `icon` settings.
@@ -24,7 +30,7 @@ TIP: To provide login instructions to users, use the `xpack.security.loginHelp`
If you don't want a specific provider to show up at the Login Selector UI (e.g. to only support third-party initiated login) you can hide it with `showInSelector` setting set to `false`. However, in this case, the provider is presented in the provider chain and may be consulted during authentication based on its `order`. To disable the provider, use the `enabled` setting.
-TIP: The Login Selector UI can also be disabled or enabled with `xpack.security.authc.selector.enabled` setting.
+TIP: The Login Selector UI can also be disabled or enabled with `xpack.security.authc.selector.enabled` setting.
Here is how your `kibana.yml` and Login Selector UI can look like if you deal with multiple authentication providers:
@@ -292,9 +298,9 @@ xpack.security.authc.providers:
order: 1
-----------------------------------------------
-IMPORTANT: {kib} uses SPNEGO, which wraps the Kerberos protocol for use with HTTP, extending it to web applications.
+IMPORTANT: {kib} uses SPNEGO, which wraps the Kerberos protocol for use with HTTP, extending it to web applications.
At the end of the Kerberos handshake, {kib} forwards the service ticket to {es}, then {es} unpacks the service ticket and responds with an access and refresh token, which are used for subsequent authentication.
-On every {es} node that {kib} connects to, the keytab file should always contain the HTTP service principal for the {kib} host.
+On every {es} node that {kib} connects to, the keytab file should always contain the HTTP service principal for the {kib} host.
The HTTP service principal name must have the `HTTP/kibana.domain.local@KIBANA.DOMAIN.LOCAL` format.
@@ -386,7 +392,7 @@ xpack.security.authc.providers:
[[anonymous-access-and-embedding]]
===== Anonymous access and embedding
-One of the most popular use cases for anonymous access is when you embed {kib} into other applications and don't want to force your users to log in to view it.
+One of the most popular use cases for anonymous access is when you embed {kib} into other applications and don't want to force your users to log in to view it.
If you configured {kib} to use anonymous access as the sole authentication mechanism, you don't need to do anything special while embedding {kib}.
If you have multiple authentication providers enabled, and you want to automatically log in anonymous users when embedding dashboards and visualizations:
diff --git a/examples/screenshot_mode_example/.i18nrc.json b/examples/screenshot_mode_example/.i18nrc.json
new file mode 100644
index 000000000000000..cce0f6b34fea299
--- /dev/null
+++ b/examples/screenshot_mode_example/.i18nrc.json
@@ -0,0 +1,7 @@
+{
+ "prefix": "screenshotModeExample",
+ "paths": {
+ "screenshotModeExample": "."
+ },
+ "translations": ["translations/ja-JP.json"]
+}
diff --git a/examples/screenshot_mode_example/README.md b/examples/screenshot_mode_example/README.md
new file mode 100755
index 000000000000000..ebae7480ca5fe0e
--- /dev/null
+++ b/examples/screenshot_mode_example/README.md
@@ -0,0 +1,9 @@
+# screenshotModeExample
+
+A Kibana plugin
+
+---
+
+## Development
+
+See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment.
diff --git a/examples/screenshot_mode_example/common/index.ts b/examples/screenshot_mode_example/common/index.ts
new file mode 100644
index 000000000000000..c6b22bdb077852e
--- /dev/null
+++ b/examples/screenshot_mode_example/common/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 const PLUGIN_NAME = 'Screenshot mode example app';
+
+export const BASE_API_ROUTE = '/api/screenshot_mode_example';
diff --git a/examples/screenshot_mode_example/kibana.json b/examples/screenshot_mode_example/kibana.json
new file mode 100644
index 000000000000000..4cb8c1a1393fbdf
--- /dev/null
+++ b/examples/screenshot_mode_example/kibana.json
@@ -0,0 +1,9 @@
+{
+ "id": "screenshotModeExample",
+ "version": "1.0.0",
+ "kibanaVersion": "kibana",
+ "server": true,
+ "ui": true,
+ "requiredPlugins": ["navigation", "screenshotMode", "usageCollection"],
+ "optionalPlugins": []
+}
diff --git a/examples/screenshot_mode_example/public/application.tsx b/examples/screenshot_mode_example/public/application.tsx
new file mode 100644
index 000000000000000..670468c77bd5f51
--- /dev/null
+++ b/examples/screenshot_mode_example/public/application.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 ReactDOM from 'react-dom';
+import { AppMountParameters, CoreStart } from '../../../src/core/public';
+import { AppPluginSetupDependencies, AppPluginStartDependencies } from './types';
+import { ScreenshotModeExampleApp } from './components/app';
+
+export const renderApp = (
+ { notifications, http }: CoreStart,
+ { screenshotMode }: AppPluginSetupDependencies,
+ { navigation }: AppPluginStartDependencies,
+ { appBasePath, element }: AppMountParameters
+) => {
+ ReactDOM.render(
+ ,
+ element
+ );
+
+ return () => ReactDOM.unmountComponentAtNode(element);
+};
diff --git a/examples/screenshot_mode_example/public/components/app.tsx b/examples/screenshot_mode_example/public/components/app.tsx
new file mode 100644
index 000000000000000..c50eaf5b525683d
--- /dev/null
+++ b/examples/screenshot_mode_example/public/components/app.tsx
@@ -0,0 +1,122 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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, { useEffect } from 'react';
+import { BrowserRouter as Router } from 'react-router-dom';
+import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
+
+import {
+ EuiPage,
+ EuiPageBody,
+ EuiPageContent,
+ EuiPageContentBody,
+ EuiPageContentHeader,
+ EuiPageHeader,
+ EuiTitle,
+ EuiText,
+} from '@elastic/eui';
+
+import { CoreStart } from '../../../../src/core/public';
+import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public';
+import {
+ ScreenshotModePluginSetup,
+ KBN_SCREENSHOT_MODE_HEADER,
+} from '../../../../src/plugins/screenshot_mode/public';
+
+import { PLUGIN_NAME, BASE_API_ROUTE } from '../../common';
+
+interface ScreenshotModeExampleAppDeps {
+ basename: string;
+ notifications: CoreStart['notifications'];
+ http: CoreStart['http'];
+ navigation: NavigationPublicPluginStart;
+ screenshotMode: ScreenshotModePluginSetup;
+}
+
+export const ScreenshotModeExampleApp = ({
+ basename,
+ notifications,
+ http,
+ navigation,
+ screenshotMode,
+}: ScreenshotModeExampleAppDeps) => {
+ const isScreenshotMode = screenshotMode.isScreenshotMode();
+
+ useEffect(() => {
+ // fire and forget
+ http.get(`${BASE_API_ROUTE}/check_is_screenshot`, {
+ headers: isScreenshotMode ? { [KBN_SCREENSHOT_MODE_HEADER]: 'true' } : undefined,
+ });
+ notifications.toasts.addInfo({
+ title: 'Welcome to the screenshot example app!',
+ text: isScreenshotMode
+ ? 'In screenshot mode we want this to remain visible'
+ : 'In normal mode this toast will disappear eventually',
+ toastLifeTimeMs: isScreenshotMode ? 360000 : 3000,
+ });
+ }, [isScreenshotMode, notifications, http]);
+ return (
+
+
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {isScreenshotMode ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {isScreenshotMode ? (
+
We detected screenshot mode. The chrome navbar should be hidden.
+ ) : (
+
+ This is how the app looks in normal mode. The chrome navbar should be
+ visible.
+
+ )}
+
+
+
+
+
+ >
+
+
+ );
+};
diff --git a/examples/screenshot_mode_example/public/index.scss b/examples/screenshot_mode_example/public/index.scss
new file mode 100644
index 000000000000000..e69de29bb2d1d64
diff --git a/examples/screenshot_mode_example/public/index.ts b/examples/screenshot_mode_example/public/index.ts
new file mode 100644
index 000000000000000..07768cbb1fdb717
--- /dev/null
+++ b/examples/screenshot_mode_example/public/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 './index.scss';
+
+import { ScreenshotModeExamplePlugin } from './plugin';
+
+// This exports static code and TypeScript types,
+// as well as, Kibana Platform `plugin()` initializer.
+export function plugin() {
+ return new ScreenshotModeExamplePlugin();
+}
diff --git a/examples/screenshot_mode_example/public/plugin.ts b/examples/screenshot_mode_example/public/plugin.ts
new file mode 100644
index 000000000000000..91bcc2410b5fc54
--- /dev/null
+++ b/examples/screenshot_mode_example/public/plugin.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { AppMountParameters, CoreSetup, CoreStart, Plugin } from '../../../src/core/public';
+import { AppPluginSetupDependencies, AppPluginStartDependencies } from './types';
+import { MetricsTracking } from './services';
+import { PLUGIN_NAME } from '../common';
+
+export class ScreenshotModeExamplePlugin implements Plugin {
+ uiTracking = new MetricsTracking();
+
+ public setup(core: CoreSetup, depsSetup: AppPluginSetupDependencies): void {
+ const { screenshotMode, usageCollection } = depsSetup;
+ const isScreenshotMode = screenshotMode.isScreenshotMode();
+
+ this.uiTracking.setup({
+ disableTracking: isScreenshotMode, // In screenshot mode there will be no user interactions to track
+ usageCollection,
+ });
+
+ // Register an application into the side navigation menu
+ core.application.register({
+ id: 'screenshotModeExample',
+ title: PLUGIN_NAME,
+ async mount(params: AppMountParameters) {
+ // Load application bundle
+ const { renderApp } = await import('./application');
+ // Get start services as specified in kibana.json
+ const [coreStart, depsStart] = await core.getStartServices();
+
+ // For screenshots we don't need to have the top bar visible
+ coreStart.chrome.setIsVisible(!isScreenshotMode);
+
+ // Render the application
+ return renderApp(coreStart, depsSetup, depsStart as AppPluginStartDependencies, params);
+ },
+ });
+ }
+
+ public start(core: CoreStart): void {}
+
+ public stop() {}
+}
diff --git a/examples/screenshot_mode_example/public/services/index.ts b/examples/screenshot_mode_example/public/services/index.ts
new file mode 100644
index 000000000000000..5725e52e65097bd
--- /dev/null
+++ b/examples/screenshot_mode_example/public/services/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { MetricsTracking } from './metrics_tracking';
diff --git a/examples/screenshot_mode_example/public/services/metrics_tracking.ts b/examples/screenshot_mode_example/public/services/metrics_tracking.ts
new file mode 100644
index 000000000000000..e40b6bbf09e44fe
--- /dev/null
+++ b/examples/screenshot_mode_example/public/services/metrics_tracking.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { UiCounterMetricType, METRIC_TYPE } from '@kbn/analytics';
+import { PLUGIN_NAME } from '../../common';
+import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public';
+
+export class MetricsTracking {
+ private trackingDisabled = false;
+ private usageCollection?: UsageCollectionSetup;
+
+ private track(eventName: string, type: UiCounterMetricType) {
+ if (this.trackingDisabled) return;
+
+ this.usageCollection?.reportUiCounter(PLUGIN_NAME, type, eventName);
+ }
+
+ public setup({
+ disableTracking,
+ usageCollection,
+ }: {
+ disableTracking?: boolean;
+ usageCollection: UsageCollectionSetup;
+ }) {
+ this.usageCollection = usageCollection;
+ if (disableTracking) this.trackingDisabled = true;
+ }
+
+ public trackInit() {
+ this.track('init', METRIC_TYPE.LOADED);
+ }
+}
diff --git a/examples/screenshot_mode_example/public/types.ts b/examples/screenshot_mode_example/public/types.ts
new file mode 100644
index 000000000000000..88812a4a507c91d
--- /dev/null
+++ b/examples/screenshot_mode_example/public/types.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 { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public';
+import { ScreenshotModePluginSetup } from '../../../src/plugins/screenshot_mode/public';
+import { UsageCollectionSetup } from '../../../src/plugins/usage_collection/public';
+
+export interface AppPluginSetupDependencies {
+ usageCollection: UsageCollectionSetup;
+ screenshotMode: ScreenshotModePluginSetup;
+}
+
+export interface AppPluginStartDependencies {
+ navigation: NavigationPublicPluginStart;
+}
diff --git a/examples/screenshot_mode_example/server/index.ts b/examples/screenshot_mode_example/server/index.ts
new file mode 100644
index 000000000000000..af23ea893a75536
--- /dev/null
+++ b/examples/screenshot_mode_example/server/index.ts
@@ -0,0 +1,12 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { PluginInitializerContext } from 'kibana/server';
+import { ScreenshotModeExamplePlugin } from './plugin';
+
+export const plugin = (ctx: PluginInitializerContext) => new ScreenshotModeExamplePlugin(ctx);
diff --git a/examples/screenshot_mode_example/server/plugin.ts b/examples/screenshot_mode_example/server/plugin.ts
new file mode 100644
index 000000000000000..5738f4a583a1a35
--- /dev/null
+++ b/examples/screenshot_mode_example/server/plugin.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 { Plugin, PluginInitializerContext, CoreSetup, Logger } from 'kibana/server';
+import { ScreenshotModePluginSetup } from '../../../src/plugins/screenshot_mode/server';
+import { RouteDependencies } from './types';
+import { registerRoutes } from './routes';
+
+export class ScreenshotModeExamplePlugin implements Plugin {
+ log: Logger;
+ constructor(ctx: PluginInitializerContext) {
+ this.log = ctx.logger.get();
+ }
+ setup(core: CoreSetup, { screenshotMode }: { screenshotMode: ScreenshotModePluginSetup }): void {
+ const deps: RouteDependencies = {
+ screenshotMode,
+ router: core.http.createRouter(),
+ log: this.log,
+ };
+
+ registerRoutes(deps);
+ }
+
+ start() {}
+ stop() {}
+}
diff --git a/examples/screenshot_mode_example/server/routes.ts b/examples/screenshot_mode_example/server/routes.ts
new file mode 100644
index 000000000000000..adf4c2e2b6fc5b3
--- /dev/null
+++ b/examples/screenshot_mode_example/server/routes.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { RouteDependencies } from './types';
+import { BASE_API_ROUTE } from '../common';
+
+export const registerRoutes = ({ router, log, screenshotMode }: RouteDependencies) => {
+ router.get(
+ { path: `${BASE_API_ROUTE}/check_is_screenshot`, validate: false },
+ async (ctx, req, res) => {
+ log.info(`Reading screenshot mode from a request: ${screenshotMode.isScreenshotMode(req)}`);
+ log.info(`Reading is screenshot mode from ctx: ${ctx.screenshotMode.isScreenshot}`);
+ return res.ok();
+ }
+ );
+};
diff --git a/examples/screenshot_mode_example/server/types.ts b/examples/screenshot_mode_example/server/types.ts
new file mode 100644
index 000000000000000..9d8d5888c3ab10c
--- /dev/null
+++ b/examples/screenshot_mode_example/server/types.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { IRouter, Logger } from 'kibana/server';
+import { ScreenshotModeRequestHandlerContext } from '../../../src/plugins/screenshot_mode/server';
+import { ScreenshotModePluginSetup } from '../../../src/plugins/screenshot_mode/server';
+
+export type ScreenshotModeExampleRouter = IRouter;
+
+export interface RouteDependencies {
+ screenshotMode: ScreenshotModePluginSetup;
+ router: ScreenshotModeExampleRouter;
+ log: Logger;
+}
diff --git a/examples/screenshot_mode_example/tsconfig.json b/examples/screenshot_mode_example/tsconfig.json
new file mode 100644
index 000000000000000..dfb436e7377ac11
--- /dev/null
+++ b/examples/screenshot_mode_example/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "./target",
+ "skipLibCheck": true
+ },
+ "include": [
+ "index.ts",
+ "public/**/*.ts",
+ "public/**/*.tsx",
+ "common/**/*.ts",
+ "server/**/*.ts",
+ "../../typings/**/*"
+ ],
+ "exclude": [],
+ "references": [{ "path": "../../src/core/tsconfig.json" }]
+}
diff --git a/examples/search_examples/common/index.ts b/examples/search_examples/common/index.ts
index cc47c0f57597397..c61de9d3c6dee2e 100644
--- a/examples/search_examples/common/index.ts
+++ b/examples/search_examples/common/index.ts
@@ -6,17 +6,7 @@
* Side Public License, v 1.
*/
-import { IEsSearchResponse, IEsSearchRequest } from '../../../src/plugins/data/common';
-
export const PLUGIN_ID = 'searchExamples';
export const PLUGIN_NAME = 'Search Examples';
-export interface IMyStrategyRequest extends IEsSearchRequest {
- get_cool: boolean;
-}
-export interface IMyStrategyResponse extends IEsSearchResponse {
- cool: string;
- executed_at: number;
-}
-
export const SERVER_SEARCH_ROUTE_PATH = '/api/examples/search';
diff --git a/examples/search_examples/common/types.ts b/examples/search_examples/common/types.ts
new file mode 100644
index 000000000000000..8bb38ea0b2d0d38
--- /dev/null
+++ b/examples/search_examples/common/types.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 {
+ IEsSearchRequest,
+ IEsSearchResponse,
+ IKibanaSearchRequest,
+ IKibanaSearchResponse,
+} from '../../../src/plugins/data/common';
+
+export interface IMyStrategyRequest extends IEsSearchRequest {
+ get_cool: boolean;
+}
+export interface IMyStrategyResponse extends IEsSearchResponse {
+ cool: string;
+ executed_at: number;
+}
+
+export type FibonacciRequest = IKibanaSearchRequest<{ n: number }>;
+
+export type FibonacciResponse = IKibanaSearchResponse<{ values: number[] }>;
diff --git a/examples/search_examples/public/search/app.tsx b/examples/search_examples/public/search/app.tsx
index c9ede2ff2b45fb7..06f9426b4965cbd 100644
--- a/examples/search_examples/public/search/app.tsx
+++ b/examples/search_examples/public/search/app.tsx
@@ -26,27 +26,27 @@ import {
EuiCode,
EuiComboBox,
EuiFormLabel,
+ EuiFieldNumber,
+ EuiProgress,
EuiTabbedContent,
+ EuiTabbedContentTab,
} from '@elastic/eui';
import { CoreStart } from '../../../../src/core/public';
import { mountReactNode } from '../../../../src/core/public/utils';
import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public';
-import {
- PLUGIN_ID,
- PLUGIN_NAME,
- IMyStrategyResponse,
- SERVER_SEARCH_ROUTE_PATH,
-} from '../../common';
+import { PLUGIN_ID, PLUGIN_NAME, SERVER_SEARCH_ROUTE_PATH } from '../../common';
import {
DataPublicPluginStart,
+ IKibanaSearchResponse,
IndexPattern,
IndexPatternField,
isCompleteResponse,
isErrorResponse,
} from '../../../../src/plugins/data/public';
+import { IMyStrategyResponse } from '../../common/types';
interface SearchExamplesAppDeps {
notifications: CoreStart['notifications'];
@@ -88,7 +88,10 @@ export const SearchExamplesApp = ({
}: SearchExamplesAppDeps) => {
const { IndexPatternSelect } = data.ui;
const [getCool, setGetCool] = useState(false);
+ const [fibonacciN, setFibonacciN] = useState(10);
const [timeTook, setTimeTook] = useState();
+ const [total, setTotal] = useState(100);
+ const [loaded, setLoaded] = useState(0);
const [indexPattern, setIndexPattern] = useState();
const [fields, setFields] = useState();
const [selectedFields, setSelectedFields] = useState([]);
@@ -99,7 +102,15 @@ export const SearchExamplesApp = ({
IndexPatternField | null | undefined
>();
const [request, setRequest] = useState>({});
- const [response, setResponse] = useState>({});
+ const [rawResponse, setRawResponse] = useState>({});
+ const [selectedTab, setSelectedTab] = useState(0);
+
+ function setResponse(response: IKibanaSearchResponse) {
+ setRawResponse(response.rawResponse);
+ setLoaded(response.loaded!);
+ setTotal(response.total!);
+ setTimeTook(response.rawResponse.took);
+ }
// Fetch the default index pattern using the `data.indexPatterns` service, as the component is mounted.
useEffect(() => {
@@ -152,8 +163,7 @@ export const SearchExamplesApp = ({
.subscribe({
next: (res) => {
if (isCompleteResponse(res)) {
- setResponse(res.rawResponse);
- setTimeTook(res.rawResponse.took);
+ setResponse(res);
const avgResult: number | undefined = res.rawResponse.aggregations
? // @ts-expect-error @elastic/elasticsearch no way to declare a type for aggregation in the search response
res.rawResponse.aggregations[1].value
@@ -234,7 +244,7 @@ export const SearchExamplesApp = ({
setRequest(searchSource.getSearchRequestBody());
const { rawResponse: res } = await searchSource.fetch$().toPromise();
- setResponse(res);
+ setRawResponse(res);
const message = Searched {res.hits.total} documents.;
notifications.toasts.addSuccess(
@@ -247,7 +257,7 @@ export const SearchExamplesApp = ({
}
);
} catch (e) {
- setResponse(e.body);
+ setRawResponse(e.body);
notifications.toasts.addWarning(`An error has occurred: ${e.message}`);
}
};
@@ -260,6 +270,41 @@ export const SearchExamplesApp = ({
doAsyncSearch('myStrategy');
};
+ const onPartialResultsClickHandler = () => {
+ setSelectedTab(1);
+ const req = {
+ params: {
+ n: fibonacciN,
+ },
+ };
+
+ // Submit the search request using the `data.search` service.
+ setRequest(req.params);
+ const searchSubscription$ = data.search
+ .search(req, {
+ strategy: 'fibonacciStrategy',
+ })
+ .subscribe({
+ next: (res) => {
+ setResponse(res);
+ if (isCompleteResponse(res)) {
+ notifications.toasts.addSuccess({
+ title: 'Query result',
+ text: 'Query finished',
+ });
+ searchSubscription$.unsubscribe();
+ } else if (isErrorResponse(res)) {
+ // TODO: Make response error status clearer
+ notifications.toasts.addWarning('An error has occurred');
+ searchSubscription$.unsubscribe();
+ }
+ },
+ error: () => {
+ notifications.toasts.addDanger('Failed to run search');
+ },
+ });
+ };
+
const onClientSideSessionCacheClickHandler = () => {
doAsyncSearch('myStrategy', data.search.session.getSessionId());
};
@@ -284,7 +329,7 @@ export const SearchExamplesApp = ({
doSearchSourceSearch(withOtherBucket);
};
- const reqTabs = [
+ const reqTabs: EuiTabbedContentTab[] = [
{
id: 'request',
name: Request,
@@ -318,6 +363,7 @@ export const SearchExamplesApp = ({
values={{ time: timeTook ?? 'Unknown' }}
/>
+
- {JSON.stringify(response, null, 2)}
+ {JSON.stringify(rawResponse, null, 2)}
>
),
@@ -484,6 +530,37 @@ export const SearchExamplesApp = ({
+
+
Handling partial results
+
+
+ The observable returned from data.search provides partial results
+ when the response is not yet complete. These can be handled to update a chart or
+ simply a progress bar:
+
+
+ <EuiProgress value={response.loaded} max={response.total}
+ />
+
+ Below is an example showing a custom search strategy that emits partial Fibonacci
+ sequences up to the length provided, updates the response with each partial result,
+ and updates a progress bar (see the Response tab).
+ setFibonacciN(parseInt(event.target.value, 10))}
+ />
+
+ Request Fibonacci sequence
+
+
+
Writing a custom search strategy
@@ -567,8 +644,13 @@ export const SearchExamplesApp = ({
+
-
+ setSelectedTab(reqTabs.indexOf(tab))}
+ />
diff --git a/examples/search_examples/server/fibonacci_strategy.ts b/examples/search_examples/server/fibonacci_strategy.ts
new file mode 100644
index 000000000000000..a37438aba7055c4
--- /dev/null
+++ b/examples/search_examples/server/fibonacci_strategy.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 uuid from 'uuid';
+import { ISearchStrategy } from '../../../src/plugins/data/server';
+import { FibonacciRequest, FibonacciResponse } from '../common/types';
+
+export const fibonacciStrategyProvider = (): ISearchStrategy<
+ FibonacciRequest,
+ FibonacciResponse
+> => {
+ const responseMap = new Map();
+ return ({
+ search: (request: FibonacciRequest) => {
+ const id = request.id ?? uuid();
+ const [sequence, total, started] = responseMap.get(id) ?? [
+ [],
+ request.params?.n ?? 0,
+ Date.now(),
+ ];
+ if (sequence.length < 2) {
+ if (total > 0) sequence.push(sequence.length);
+ } else {
+ const [a, b] = sequence.slice(-2);
+ sequence.push(a + b);
+ }
+ const loaded = sequence.length;
+ responseMap.set(id, [sequence, total, started]);
+ if (loaded >= total) {
+ responseMap.delete(id);
+ }
+
+ const isRunning = loaded < total;
+ const isPartial = isRunning;
+ const took = Date.now() - started;
+ const values = sequence.slice(0, loaded);
+
+ // Usually we'd do something like "of()" but for some reason it breaks in tests with the error
+ // "You provided an invalid object where a stream was expected." which is why we have to cast
+ // down below as well
+ return [{ id, loaded, total, isRunning, isPartial, rawResponse: { took, values } }];
+ },
+ cancel: async (id: string) => {
+ responseMap.delete(id);
+ },
+ } as unknown) as ISearchStrategy;
+};
diff --git a/examples/search_examples/server/my_strategy.ts b/examples/search_examples/server/my_strategy.ts
index 0a647889600912e..db8cd903f23d69f 100644
--- a/examples/search_examples/server/my_strategy.ts
+++ b/examples/search_examples/server/my_strategy.ts
@@ -8,7 +8,7 @@
import { map } from 'rxjs/operators';
import { ISearchStrategy, PluginStart } from '../../../src/plugins/data/server';
-import { IMyStrategyResponse, IMyStrategyRequest } from '../common';
+import { IMyStrategyRequest, IMyStrategyResponse } from '../common/types';
export const mySearchStrategyProvider = (
data: PluginStart
diff --git a/examples/search_examples/server/plugin.ts b/examples/search_examples/server/plugin.ts
index 84f082d890bb0f6..984d3201220eb2e 100644
--- a/examples/search_examples/server/plugin.ts
+++ b/examples/search_examples/server/plugin.ts
@@ -24,6 +24,7 @@ import {
} from './types';
import { mySearchStrategyProvider } from './my_strategy';
import { registerRoutes } from './routes';
+import { fibonacciStrategyProvider } from './fibonacci_strategy';
export class SearchExamplesPlugin
implements
@@ -48,7 +49,9 @@ export class SearchExamplesPlugin
core.getStartServices().then(([_, depsStart]) => {
const myStrategy = mySearchStrategyProvider(depsStart.data);
+ const fibonacciStrategy = fibonacciStrategyProvider();
deps.data.search.registerSearchStrategy('myStrategy', myStrategy);
+ deps.data.search.registerSearchStrategy('fibonacciStrategy', fibonacciStrategy);
registerRoutes(router);
});
diff --git a/package.json b/package.json
index 211cbed02d4c74a..7966f33f262492f 100644
--- a/package.json
+++ b/package.json
@@ -83,6 +83,7 @@
"**/load-grunt-config/lodash": "^4.17.21",
"**/minimist": "^1.2.5",
"**/node-jose/node-forge": "^0.10.0",
+ "**/pdfkit/crypto-js": "4.0.0",
"**/prismjs": "1.23.0",
"**/react-syntax-highlighter": "^15.3.1",
"**/react-syntax-highlighter/**/highlight.js": "^10.4.1",
@@ -92,7 +93,7 @@
"**/underscore": "^1.13.1"
},
"engines": {
- "node": "14.16.1",
+ "node": "14.17.0",
"yarn": "^1.21.1"
},
"dependencies": {
@@ -107,12 +108,13 @@
"@elastic/good": "^9.0.1-kibana3",
"@elastic/maki": "6.3.0",
"@elastic/node-crypto": "1.2.1",
- "@elastic/numeral": "^2.5.0",
+ "@elastic/numeral": "^2.5.1",
"@elastic/react-search-ui": "^1.5.1",
"@elastic/request-crypto": "1.1.4",
"@elastic/safer-lodash-set": "link:bazel-bin/packages/elastic-safer-lodash-set/npm_module",
"@elastic/search-ui-app-search-connector": "^1.5.0",
"@elastic/ui-ace": "0.2.3",
+ "@hapi/accept": "^5.0.2",
"@hapi/boom": "^9.1.1",
"@hapi/cookie": "^11.0.2",
"@hapi/good-squeeze": "6.0.0",
@@ -129,16 +131,20 @@
"@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/i18n": "link:packages/kbn-i18n",
+ "@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-utils": "link:bazel-bin/packages/kbn-securitysolution-utils/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/server-http-tools": "link:packages/kbn-server-http-tools",
+ "@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",
"@kbn/server-route-repository": "link:packages/kbn-server-route-repository",
"@kbn/std": "link:bazel-bin/packages/kbn-std/npm_module",
"@kbn/tinymath": "link:bazel-bin/packages/kbn-tinymath/npm_module",
@@ -167,7 +173,6 @@
"JSONStream": "1.3.5",
"abort-controller": "^3.0.0",
"abortcontroller-polyfill": "^1.4.0",
- "accept": "3.0.2",
"ajv": "^6.12.4",
"angular": "^1.8.0",
"angular-aria": "^1.8.0",
@@ -264,7 +269,8 @@
"json-stringify-safe": "5.0.1",
"jsonwebtoken": "^8.5.1",
"jsts": "^1.6.2",
- "kea": "^2.3.0",
+ "@kbn/rule-data-utils": "link:packages/kbn-rule-data-utils",
+ "kea": "^2.4.2",
"leaflet": "1.5.1",
"leaflet-draw": "0.4.14",
"leaflet-responsive-popup": "0.6.4",
@@ -298,7 +304,6 @@
"object-hash": "^1.3.1",
"object-path-immutable": "^3.1.1",
"opn": "^5.5.0",
- "oppsy": "^2.0.0",
"p-limit": "^3.0.1",
"p-map": "^4.0.0",
"p-retry": "^4.2.0",
@@ -314,7 +319,7 @@
"proxy-from-env": "1.0.0",
"proxyquire": "1.8.0",
"puid": "1.0.7",
- "puppeteer": "npm:@elastic/puppeteer@5.4.1-patch.1",
+ "puppeteer": "^8.0.0",
"query-string": "^6.13.2",
"raw-loader": "^3.1.0",
"rbush": "^3.0.1",
@@ -431,7 +436,7 @@
"@babel/traverse": "^7.12.12",
"@babel/types": "^7.12.12",
"@bazel/ibazel": "^0.15.10",
- "@bazel/typescript": "^3.4.2",
+ "@bazel/typescript": "^3.5.0",
"@cypress/snapshot": "^2.1.7",
"@cypress/webpack-preprocessor": "^5.6.0",
"@elastic/apm-rum": "^5.6.1",
@@ -446,18 +451,18 @@
"@kbn/babel-preset": "link:bazel-bin/packages/kbn-babel-preset/npm_module",
"@kbn/cli-dev-mode": "link:packages/kbn-cli-dev-mode",
"@kbn/dev-utils": "link:bazel-bin/packages/kbn-dev-utils/npm_module",
- "@kbn/docs-utils": "link:packages/kbn-docs-utils",
+ "@kbn/docs-utils": "link:bazel-bin/packages/kbn-docs-utils/npm_module",
"@kbn/es": "link:bazel-bin/packages/kbn-es/npm_module",
"@kbn/es-archiver": "link:packages/kbn-es-archiver",
"@kbn/eslint-import-resolver-kibana": "link:bazel-bin/packages/kbn-eslint-import-resolver-kibana/npm_module",
"@kbn/eslint-plugin-eslint": "link:bazel-bin/packages/kbn-eslint-plugin-eslint/npm_module",
"@kbn/expect": "link:bazel-bin/packages/kbn-expect/npm_module",
"@kbn/optimizer": "link:packages/kbn-optimizer",
- "@kbn/plugin-generator": "link:packages/kbn-plugin-generator",
+ "@kbn/plugin-generator": "link:bazel-bin/packages/kbn-plugin-generator/npm_module",
"@kbn/plugin-helpers": "link:packages/kbn-plugin-helpers",
"@kbn/pm": "link:packages/kbn-pm",
"@kbn/storybook": "link:packages/kbn-storybook",
- "@kbn/telemetry-tools": "link:packages/kbn-telemetry-tools",
+ "@kbn/telemetry-tools": "link:bazel-bin/packages/kbn-telemetry-tools/npm_module",
"@kbn/test": "link:packages/kbn-test",
"@kbn/test-subj-selector": "link:packages/kbn-test-subj-selector",
"@loaders.gl/polyfills": "^2.3.5",
@@ -484,7 +489,6 @@
"@testing-library/react": "^11.2.6",
"@testing-library/react-hooks": "^5.1.1",
"@testing-library/user-event": "^13.1.1",
- "@types/accept": "3.1.1",
"@types/angular": "^1.6.56",
"@types/angular-mocks": "^1.7.0",
"@types/archiver": "^5.1.0",
@@ -586,7 +590,6 @@
"@types/pretty-ms": "^5.0.0",
"@types/prop-types": "^15.7.3",
"@types/proper-lockfile": "^3.0.1",
- "@types/puppeteer": "^5.4.1",
"@types/rbush": "^3.0.0",
"@types/reach__router": "^1.2.6",
"@types/react": "^16.9.36",
diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel
index 8b483f54344e2b5..76250d8a1e86487 100644
--- a/packages/BUILD.bazel
+++ b/packages/BUILD.bazel
@@ -16,16 +16,25 @@ filegroup(
"//packages/kbn-config-schema:build",
"//packages/kbn-crypto:build",
"//packages/kbn-dev-utils:build",
+ "//packages/kbn-docs-utils:build",
"//packages/kbn-es:build",
"//packages/kbn-eslint-import-resolver-kibana:build",
"//packages/kbn-eslint-plugin-eslint:build",
"//packages/kbn-expect:build",
+ "//packages/kbn-i18n:build",
"//packages/kbn-legacy-logging:build",
"//packages/kbn-logging:build",
+ "//packages/kbn-plugin-generator:build",
"//packages/kbn-securitysolution-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-utils:build",
+ "//packages/kbn-securitysolution-es-utils:build",
+ "//packages/kbn-server-http-tools:build",
"//packages/kbn-std:build",
+ "//packages/kbn-telemetry-tools:build",
"//packages/kbn-tinymath:build",
"//packages/kbn-utility-types:build",
"//packages/kbn-utils:build",
diff --git a/packages/kbn-cli-dev-mode/package.json b/packages/kbn-cli-dev-mode/package.json
index 0401e6a82e11a7f..dd491de55c075dc 100644
--- a/packages/kbn-cli-dev-mode/package.json
+++ b/packages/kbn-cli-dev-mode/package.json
@@ -14,7 +14,6 @@
"devOnly": true
},
"dependencies": {
- "@kbn/server-http-tools": "link:../kbn-server-http-tools",
"@kbn/optimizer": "link:../kbn-optimizer"
}
}
\ No newline at end of file
diff --git a/packages/kbn-config/src/deprecation/apply_deprecations.test.ts b/packages/kbn-config/src/deprecation/apply_deprecations.test.ts
index f2c0a4391634321..47746967bbe5f1f 100644
--- a/packages/kbn-config/src/deprecation/apply_deprecations.test.ts
+++ b/packages/kbn-config/src/deprecation/apply_deprecations.test.ts
@@ -36,10 +36,9 @@ describe('applyDeprecations', () => {
const addDeprecation = jest.fn();
const createAddDeprecation = jest.fn().mockReturnValue(addDeprecation);
const initialConfig = { foo: 'bar', deprecated: 'deprecated' };
- const alteredConfig = { foo: 'bar' };
- const handlerA = jest.fn().mockReturnValue(alteredConfig);
- const handlerB = jest.fn().mockImplementation((conf) => conf);
+ const handlerA = jest.fn().mockReturnValue({ unset: [{ path: 'deprecated' }] });
+ const handlerB = jest.fn().mockReturnValue(undefined);
applyDeprecations(
initialConfig,
@@ -47,8 +46,6 @@ describe('applyDeprecations', () => {
createAddDeprecation
);
- expect(handlerA).toHaveBeenCalledWith(initialConfig, 'pathA', addDeprecation);
- expect(handlerB).toHaveBeenCalledWith(alteredConfig, 'pathB', addDeprecation);
expect(createAddDeprecation).toBeCalledTimes(2);
expect(createAddDeprecation).toHaveBeenNthCalledWith(1, 'pathA');
expect(createAddDeprecation).toHaveBeenNthCalledWith(2, 'pathB');
@@ -60,8 +57,15 @@ describe('applyDeprecations', () => {
const initialConfig = { foo: 'bar', deprecated: 'deprecated' };
const alteredConfig = { foo: 'bar' };
- const handlerA = jest.fn().mockReturnValue(alteredConfig);
- const handlerB = jest.fn().mockImplementation((conf) => conf);
+ const configs: Array<{ fn: string; config: Record }> = [];
+ const handlerA = jest.fn().mockImplementation((config) => {
+ // the first argument is mutated between calls, we store a copy of it
+ configs.push({ fn: 'handlerA', config: { ...config } });
+ return { unset: [{ path: 'deprecated' }] };
+ });
+ const handlerB = jest.fn().mockImplementation((config) => {
+ configs.push({ fn: 'handlerB', config: { ...config } });
+ });
applyDeprecations(
initialConfig,
@@ -69,8 +73,10 @@ describe('applyDeprecations', () => {
createAddDeprecation
);
- expect(handlerA).toHaveBeenCalledWith(initialConfig, 'pathA', addDeprecation);
- expect(handlerB).toHaveBeenCalledWith(alteredConfig, 'pathB', addDeprecation);
+ expect(configs).toEqual([
+ { fn: 'handlerA', config: initialConfig },
+ { fn: 'handlerB', config: alteredConfig },
+ ]);
});
it('returns the migrated config', () => {
@@ -94,4 +100,40 @@ describe('applyDeprecations', () => {
expect(initialConfig).toEqual({ foo: 'bar', deprecated: 'deprecated' });
expect(migrated).toEqual({ foo: 'bar' });
});
+
+ it('ignores a command for unknown path', () => {
+ const addDeprecation = jest.fn();
+ const createAddDeprecation = jest.fn().mockReturnValue(addDeprecation);
+ const initialConfig = { foo: 'bar', deprecated: 'deprecated' };
+
+ const handler = jest.fn().mockImplementation((config) => {
+ return { unset: [{ path: 'unknown' }] };
+ });
+
+ const migrated = applyDeprecations(
+ initialConfig,
+ [wrapHandler(handler, 'pathA')],
+ createAddDeprecation
+ );
+
+ expect(migrated).toEqual(initialConfig);
+ });
+
+ it('ignores an unknown command', () => {
+ const addDeprecation = jest.fn();
+ const createAddDeprecation = jest.fn().mockReturnValue(addDeprecation);
+ const initialConfig = { foo: 'bar', deprecated: 'deprecated' };
+
+ const handler = jest.fn().mockImplementation((config) => {
+ return { rewrite: [{ path: 'foo' }] };
+ });
+
+ const migrated = applyDeprecations(
+ initialConfig,
+ [wrapHandler(handler, 'pathA')],
+ createAddDeprecation
+ );
+
+ expect(migrated).toEqual(initialConfig);
+ });
});
diff --git a/packages/kbn-config/src/deprecation/apply_deprecations.ts b/packages/kbn-config/src/deprecation/apply_deprecations.ts
index 6aced541dc30d45..092a5ced28371d7 100644
--- a/packages/kbn-config/src/deprecation/apply_deprecations.ts
+++ b/packages/kbn-config/src/deprecation/apply_deprecations.ts
@@ -6,7 +6,8 @@
* Side Public License, v 1.
*/
-import { cloneDeep } from 'lodash';
+import { cloneDeep, unset } from 'lodash';
+import { set } from '@elastic/safer-lodash-set';
import { ConfigDeprecationWithContext, AddConfigDeprecation } from './types';
const noopAddDeprecationFactory: () => AddConfigDeprecation = () => () => undefined;
@@ -22,9 +23,21 @@ export const applyDeprecations = (
deprecations: ConfigDeprecationWithContext[],
createAddDeprecation: (pluginId: string) => AddConfigDeprecation = noopAddDeprecationFactory
) => {
- let processed = cloneDeep(config);
+ const result = cloneDeep(config);
deprecations.forEach(({ deprecation, path }) => {
- processed = deprecation(processed, path, createAddDeprecation(path));
+ const commands = deprecation(result, path, createAddDeprecation(path));
+ if (commands) {
+ if (commands.set) {
+ commands.set.forEach(function ({ path: commandPath, value }) {
+ set(result, commandPath, value);
+ });
+ }
+ if (commands.unset) {
+ commands.unset.forEach(function ({ path: commandPath }) {
+ unset(result, commandPath);
+ });
+ }
+ }
});
- return processed;
+ return result;
};
diff --git a/packages/kbn-config/src/deprecation/deprecation_factory.test.ts b/packages/kbn-config/src/deprecation/deprecation_factory.test.ts
index 11a49ed79d17012..563d4017f5ed950 100644
--- a/packages/kbn-config/src/deprecation/deprecation_factory.test.ts
+++ b/packages/kbn-config/src/deprecation/deprecation_factory.test.ts
@@ -29,15 +29,15 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = rename('deprecated', 'renamed')(rawConfig, 'myplugin', addDeprecation);
- expect(processed).toEqual({
- myplugin: {
- renamed: 'toberenamed',
- valid: 'valid',
- },
- someOtherPlugin: {
- property: 'value',
- },
+ const commands = rename('deprecated', 'renamed')(rawConfig, 'myplugin', addDeprecation);
+ expect(commands).toEqual({
+ set: [
+ {
+ path: 'myplugin.renamed',
+ value: 'toberenamed',
+ },
+ ],
+ unset: [{ path: 'myplugin.deprecated' }],
});
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
@@ -64,16 +64,8 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = rename('deprecated', 'new')(rawConfig, 'myplugin', addDeprecation);
- expect(processed).toEqual({
- myplugin: {
- new: 'new',
- valid: 'valid',
- },
- someOtherPlugin: {
- property: 'value',
- },
- });
+ const commands = rename('deprecated', 'new')(rawConfig, 'myplugin', addDeprecation);
+ expect(commands).toBeUndefined();
expect(addDeprecation).toHaveBeenCalledTimes(0);
});
it('handles nested keys', () => {
@@ -88,22 +80,19 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = rename('oldsection.deprecated', 'newsection.renamed')(
+ const commands = rename('oldsection.deprecated', 'newsection.renamed')(
rawConfig,
'myplugin',
addDeprecation
);
- expect(processed).toEqual({
- myplugin: {
- oldsection: {},
- newsection: {
- renamed: 'toberenamed',
+ expect(commands).toEqual({
+ set: [
+ {
+ path: 'myplugin.newsection.renamed',
+ value: 'toberenamed',
},
- valid: 'valid',
- },
- someOtherPlugin: {
- property: 'value',
- },
+ ],
+ unset: [{ path: 'myplugin.oldsection.deprecated' }],
});
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
@@ -127,11 +116,9 @@ describe('DeprecationFactory', () => {
renamed: 'renamed',
},
};
- const processed = rename('deprecated', 'renamed')(rawConfig, 'myplugin', addDeprecation);
- expect(processed).toEqual({
- myplugin: {
- renamed: 'renamed',
- },
+ const commands = rename('deprecated', 'renamed')(rawConfig, 'myplugin', addDeprecation);
+ expect(commands).toEqual({
+ unset: [{ path: 'myplugin.deprecated' }],
});
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
@@ -162,19 +149,19 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = renameFromRoot('myplugin.deprecated', 'myplugin.renamed')(
+ const commands = renameFromRoot('myplugin.deprecated', 'myplugin.renamed')(
rawConfig,
'does-not-matter',
addDeprecation
);
- expect(processed).toEqual({
- myplugin: {
- renamed: 'toberenamed',
- valid: 'valid',
- },
- someOtherPlugin: {
- property: 'value',
- },
+ expect(commands).toEqual({
+ set: [
+ {
+ path: 'myplugin.renamed',
+ value: 'toberenamed',
+ },
+ ],
+ unset: [{ path: 'myplugin.deprecated' }],
});
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
@@ -202,19 +189,19 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = renameFromRoot('oldplugin.deprecated', 'newplugin.renamed')(
+ const commands = renameFromRoot('oldplugin.deprecated', 'newplugin.renamed')(
rawConfig,
'does-not-matter',
addDeprecation
);
- expect(processed).toEqual({
- oldplugin: {
- valid: 'valid',
- },
- newplugin: {
- renamed: 'toberenamed',
- property: 'value',
- },
+ expect(commands).toEqual({
+ set: [
+ {
+ path: 'newplugin.renamed',
+ value: 'toberenamed',
+ },
+ ],
+ unset: [{ path: 'oldplugin.deprecated' }],
});
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
@@ -242,20 +229,12 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = renameFromRoot('myplugin.deprecated', 'myplugin.new')(
+ const commands = renameFromRoot('myplugin.deprecated', 'myplugin.new')(
rawConfig,
'does-not-matter',
addDeprecation
);
- expect(processed).toEqual({
- myplugin: {
- new: 'new',
- valid: 'valid',
- },
- someOtherPlugin: {
- property: 'value',
- },
- });
+ expect(commands).toBeUndefined();
expect(addDeprecation).toBeCalledTimes(0);
});
@@ -266,15 +245,13 @@ describe('DeprecationFactory', () => {
renamed: 'renamed',
},
};
- const processed = renameFromRoot('myplugin.deprecated', 'myplugin.renamed')(
+ const commands = renameFromRoot('myplugin.deprecated', 'myplugin.renamed')(
rawConfig,
'does-not-matter',
addDeprecation
);
- expect(processed).toEqual({
- myplugin: {
- renamed: 'renamed',
- },
+ expect(commands).toEqual({
+ unset: [{ path: 'myplugin.deprecated' }],
});
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
@@ -306,14 +283,9 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = unused('deprecated')(rawConfig, 'myplugin', addDeprecation);
- expect(processed).toEqual({
- myplugin: {
- valid: 'valid',
- },
- someOtherPlugin: {
- property: 'value',
- },
+ const commands = unused('deprecated')(rawConfig, 'myplugin', addDeprecation);
+ expect(commands).toEqual({
+ unset: [{ path: 'myplugin.deprecated' }],
});
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
@@ -343,17 +315,10 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = unused('section.deprecated')(rawConfig, 'myplugin', addDeprecation);
- expect(processed).toEqual({
- myplugin: {
- valid: 'valid',
- section: {},
- },
- someOtherPlugin: {
- property: 'value',
- },
+ const commands = unused('section.deprecated')(rawConfig, 'myplugin', addDeprecation);
+ expect(commands).toEqual({
+ unset: [{ path: 'myplugin.section.deprecated' }],
});
-
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
@@ -379,15 +344,8 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = unused('deprecated')(rawConfig, 'myplugin', addDeprecation);
- expect(processed).toEqual({
- myplugin: {
- valid: 'valid',
- },
- someOtherPlugin: {
- property: 'value',
- },
- });
+ const commands = unused('deprecated')(rawConfig, 'myplugin', addDeprecation);
+ expect(commands).toBeUndefined();
expect(addDeprecation).toBeCalledTimes(0);
});
});
@@ -403,20 +361,14 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = unusedFromRoot('myplugin.deprecated')(
+ const commands = unusedFromRoot('myplugin.deprecated')(
rawConfig,
'does-not-matter',
addDeprecation
);
- expect(processed).toEqual({
- myplugin: {
- valid: 'valid',
- },
- someOtherPlugin: {
- property: 'value',
- },
+ expect(commands).toEqual({
+ unset: [{ path: 'myplugin.deprecated' }],
});
-
expect(addDeprecation.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
@@ -442,19 +394,12 @@ describe('DeprecationFactory', () => {
property: 'value',
},
};
- const processed = unusedFromRoot('myplugin.deprecated')(
+ const commands = unusedFromRoot('myplugin.deprecated')(
rawConfig,
'does-not-matter',
addDeprecation
);
- expect(processed).toEqual({
- myplugin: {
- valid: 'valid',
- },
- someOtherPlugin: {
- property: 'value',
- },
- });
+ expect(commands).toBeUndefined();
expect(addDeprecation).toBeCalledTimes(0);
});
});
diff --git a/packages/kbn-config/src/deprecation/deprecation_factory.ts b/packages/kbn-config/src/deprecation/deprecation_factory.ts
index 140846d86ae0b4b..76bcc1958d0de42 100644
--- a/packages/kbn-config/src/deprecation/deprecation_factory.ts
+++ b/packages/kbn-config/src/deprecation/deprecation_factory.ts
@@ -7,13 +7,12 @@
*/
import { get } from 'lodash';
-import { set } from '@elastic/safer-lodash-set';
-import { unset } from '@kbn/std';
import {
ConfigDeprecation,
AddConfigDeprecation,
ConfigDeprecationFactory,
DeprecatedConfigDetails,
+ ConfigDeprecationCommand,
} from './types';
const _rename = (
@@ -23,20 +22,16 @@ const _rename = (
oldKey: string,
newKey: string,
details?: Partial
-) => {
+): void | ConfigDeprecationCommand => {
const fullOldPath = getPath(rootPath, oldKey);
const oldValue = get(config, fullOldPath);
if (oldValue === undefined) {
- return config;
+ return;
}
- unset(config, fullOldPath);
-
const fullNewPath = getPath(rootPath, newKey);
const newValue = get(config, fullNewPath);
if (newValue === undefined) {
- set(config, fullNewPath, oldValue);
-
addDeprecation({
message: `"${fullOldPath}" is deprecated and has been replaced by "${fullNewPath}"`,
correctiveActions: {
@@ -46,6 +41,10 @@ const _rename = (
},
...details,
});
+ return {
+ set: [{ path: fullNewPath, value: oldValue }],
+ unset: [{ path: fullOldPath }],
+ };
} else {
addDeprecation({
message: `"${fullOldPath}" is deprecated and has been replaced by "${fullNewPath}". However both key are present, ignoring "${fullOldPath}"`,
@@ -59,7 +58,9 @@ const _rename = (
});
}
- return config;
+ return {
+ unset: [{ path: fullOldPath }],
+ };
};
const _unused = (
@@ -68,12 +69,11 @@ const _unused = (
addDeprecation: AddConfigDeprecation,
unusedKey: string,
details?: Partial
-) => {
+): void | ConfigDeprecationCommand => {
const fullPath = getPath(rootPath, unusedKey);
if (get(config, fullPath) === undefined) {
- return config;
+ return;
}
- unset(config, fullPath);
addDeprecation({
message: `${fullPath} is deprecated and is no longer used`,
correctiveActions: {
@@ -83,7 +83,9 @@ const _unused = (
},
...details,
});
- return config;
+ return {
+ unset: [{ path: fullPath }],
+ };
};
const rename = (
diff --git a/packages/kbn-config/src/deprecation/index.ts b/packages/kbn-config/src/deprecation/index.ts
index 3286acca9e584a5..48576e6d830befa 100644
--- a/packages/kbn-config/src/deprecation/index.ts
+++ b/packages/kbn-config/src/deprecation/index.ts
@@ -8,6 +8,7 @@
export type {
ConfigDeprecation,
+ ConfigDeprecationCommand,
ConfigDeprecationWithContext,
ConfigDeprecationFactory,
AddConfigDeprecation,
diff --git a/packages/kbn-config/src/deprecation/types.ts b/packages/kbn-config/src/deprecation/types.ts
index 3b1d004d7ec761b..6944f45c1e1d227 100644
--- a/packages/kbn-config/src/deprecation/types.ts
+++ b/packages/kbn-config/src/deprecation/types.ts
@@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
-
+import type { RecursiveReadonly } from '@kbn/utility-types';
/**
* Config deprecation hook used when invoking a {@link ConfigDeprecation}
*
@@ -41,14 +41,29 @@ export interface DeprecatedConfigDetails {
* @remarks
* This should only be manually implemented if {@link ConfigDeprecationFactory} does not provide the proper helpers for a specific
* deprecation need.
+ * @param config must not be mutated, return {@link ConfigDeprecationCommand} to change config shape.
*
- * @public
+ * @example
+ * ```typescript
+ * const provider: ConfigDeprecation = (config, path) => ({ unset: [{ key: 'path.to.key' }] })
+ * ```
+ * @internal
*/
export type ConfigDeprecation = (
- config: Record,
+ config: RecursiveReadonly>,
fromPath: string,
addDeprecation: AddConfigDeprecation
-) => Record;
+) => void | ConfigDeprecationCommand;
+
+/**
+ * Outcome of deprecation operation. Allows mutating config values in a declarative way.
+ *
+ * @public
+ */
+export interface ConfigDeprecationCommand {
+ set?: Array<{ path: string; value: any }>;
+ unset?: Array<{ path: string }>;
+}
/**
* A provider that should returns a list of {@link ConfigDeprecation}.
@@ -60,7 +75,7 @@ export type ConfigDeprecation = (
* const provider: ConfigDeprecationProvider = ({ rename, unused }) => [
* rename('oldKey', 'newKey'),
* unused('deprecatedKey'),
- * myCustomDeprecation,
+ * (config, path) => ({ unset: [{ key: 'path.to.key' }] })
* ]
* ```
*
diff --git a/packages/kbn-config/src/index.ts b/packages/kbn-config/src/index.ts
index a9ea8265a37682f..cf875d3daa4a21c 100644
--- a/packages/kbn-config/src/index.ts
+++ b/packages/kbn-config/src/index.ts
@@ -12,6 +12,7 @@ export type {
ConfigDeprecationProvider,
ConfigDeprecationWithContext,
ConfigDeprecation,
+ ConfigDeprecationCommand,
} from './deprecation';
export { applyDeprecations, configDeprecationFactory } from './deprecation';
diff --git a/packages/kbn-crypto/BUILD.bazel b/packages/kbn-crypto/BUILD.bazel
index 8f55f0e0f06a794..14e292c056db695 100644
--- a/packages/kbn-crypto/BUILD.bazel
+++ b/packages/kbn-crypto/BUILD.bazel
@@ -2,7 +2,7 @@
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-cypto"
+PKG_BASE_NAME = "kbn-crypto"
PKG_REQUIRE_NAME = "@kbn/crypto"
SOURCE_FILES = glob(
diff --git a/packages/kbn-docs-utils/BUILD.bazel b/packages/kbn-docs-utils/BUILD.bazel
new file mode 100644
index 000000000000000..e72d83851f5d2ee
--- /dev/null
+++ b/packages/kbn-docs-utils/BUILD.bazel
@@ -0,0 +1,88 @@
+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-docs-utils"
+PKG_REQUIRE_NAME = "@kbn/docs-utils"
+
+SOURCE_FILES = glob(
+ [
+ "src/**/*.ts",
+ ],
+ exclude = [
+ "**/*.test.*",
+ "**/__fixtures__/**",
+ "**/snapshots/**",
+ ],
+)
+
+SRCS = SOURCE_FILES
+
+filegroup(
+ name = "srcs",
+ srcs = SRCS,
+)
+
+NPM_MODULE_EXTRA_FILES = [
+ "package.json",
+]
+
+SRC_DEPS = [
+ "//packages/kbn-config",
+ "//packages/kbn-dev-utils",
+ "//packages/kbn-utils",
+ "@npm//dedent",
+ "@npm//ts-morph",
+]
+
+TYPES_DEPS = [
+ "@npm//@types/dedent",
+ "@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",
+ 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 = [":tsc"] + DEPS,
+ 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-docs-utils/package.json b/packages/kbn-docs-utils/package.json
index 27d38d2d8ed4fc4..b2a52b2d1f78e7f 100644
--- a/packages/kbn-docs-utils/package.json
+++ b/packages/kbn-docs-utils/package.json
@@ -7,9 +7,5 @@
"types": "target/index.d.ts",
"kibana": {
"devOnly": true
- },
- "scripts": {
- "kbn:bootstrap": "../../node_modules/.bin/tsc",
- "kbn:watch": "../../node_modules/.bin/tsc --watch"
}
}
\ No newline at end of file
diff --git a/packages/kbn-docs-utils/tsconfig.json b/packages/kbn-docs-utils/tsconfig.json
index 6f4a6fa2af8a550..9868c8b3d2bb4ac 100644
--- a/packages/kbn-docs-utils/tsconfig.json
+++ b/packages/kbn-docs-utils/tsconfig.json
@@ -1,11 +1,12 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "incremental": false,
+ "incremental": true,
"outDir": "./target",
"target": "ES2019",
"declaration": true,
"declarationMap": true,
+ "rootDir": "src",
"sourceMap": true,
"sourceRoot": "../../../../packages/kbn-docs-utils/src",
"types": [
diff --git a/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts b/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts
index fbef255cd9ee5e0..51d4f28d20f2e0b 100644
--- a/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts
+++ b/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts
@@ -165,6 +165,7 @@ export async function createDefaultSpace({
{
index,
id: 'space:default',
+ refresh: 'wait_for',
body: {
type: 'space',
updated_at: new Date().toISOString(),
diff --git a/packages/kbn-es/README.md b/packages/kbn-es/README.md
index 4d4c2aa94db07fd..80850c9e6a09cc0 100644
--- a/packages/kbn-es/README.md
+++ b/packages/kbn-es/README.md
@@ -7,6 +7,8 @@ If running elasticsearch from source, elasticsearch needs to be cloned to a sibl
To run, go to the Kibana root and run `node scripts/es --help` to get the latest command line options.
+The script attempts to preserve the existing interfaces used by Elasticsearch CLI. This includes passing through options with the `-E` argument and the `ES_JAVA_OPTS` environment variable for Java options.
+
### Examples
Run a snapshot install with a trial license
diff --git a/packages/kbn-es/src/cluster.js b/packages/kbn-es/src/cluster.js
index c55e5d3513c44e4..ad9ecb059031cd4 100644
--- a/packages/kbn-es/src/cluster.js
+++ b/packages/kbn-es/src/cluster.js
@@ -236,6 +236,7 @@ exports.Cluster = class Cluster {
* @param {String} installPath
* @param {Object} options
* @property {string|Array} options.esArgs
+ * @property {string} options.esJavaOpts
* @return {undefined}
*/
_exec(installPath, options = {}) {
@@ -268,14 +269,17 @@ exports.Cluster = class Cluster {
this._log.debug('%s %s', ES_BIN, args.join(' '));
- options.esEnvVars = options.esEnvVars || {};
+ let esJavaOpts = `${options.esJavaOpts || ''} ${process.env.ES_JAVA_OPTS || ''}`;
// ES now automatically sets heap size to 50% of the machine's available memory
// so we need to set it to a smaller size for local dev and CI
// especially because we currently run many instances of ES on the same machine during CI
- options.esEnvVars.ES_JAVA_OPTS =
- (options.esEnvVars.ES_JAVA_OPTS ? `${options.esEnvVars.ES_JAVA_OPTS} ` : '') +
- '-Xms1g -Xmx1g';
+ // inital and max must be the same, so we only need to check the max
+ if (!esJavaOpts.includes('Xmx')) {
+ esJavaOpts += ' -Xms1g -Xmx1g';
+ }
+
+ this._log.debug('ES_JAVA_OPTS: %s', esJavaOpts.trim());
this._process = execa(ES_BIN, args, {
cwd: installPath,
@@ -283,7 +287,7 @@ exports.Cluster = class Cluster {
...(installPath ? { ES_TMPDIR: path.resolve(installPath, 'ES_TMPDIR') } : {}),
...process.env,
JAVA_HOME: '', // By default, we want to always unset JAVA_HOME so that the bundled JDK will be used
- ...(options.esEnvVars || {}),
+ ES_JAVA_OPTS: esJavaOpts.trim(),
},
stdio: ['ignore', 'pipe', 'pipe'],
});
diff --git a/packages/kbn-es/src/integration_tests/cluster.test.js b/packages/kbn-es/src/integration_tests/cluster.test.js
index 6b4025840283f59..34220b08d212096 100644
--- a/packages/kbn-es/src/integration_tests/cluster.test.js
+++ b/packages/kbn-es/src/integration_tests/cluster.test.js
@@ -71,11 +71,17 @@ function mockEsBin({ exitCode, start }) {
);
}
+const initialEnv = { ...process.env };
+
beforeEach(() => {
jest.resetAllMocks();
extractConfigFiles.mockImplementation((config) => config);
});
+afterEach(() => {
+ process.env = { ...initialEnv };
+});
+
describe('#installSource()', () => {
it('awaits installSource() promise and returns { installPath }', async () => {
let resolveInstallSource;
@@ -355,6 +361,25 @@ describe('#run()', () => {
]
`);
});
+
+ it('sets default Java heap', async () => {
+ mockEsBin({ start: true });
+
+ const cluster = new Cluster({ log });
+ await cluster.run();
+
+ expect(execa.mock.calls[0][2].env.ES_JAVA_OPTS).toEqual('-Xms1g -Xmx1g');
+ });
+
+ it('allows Java heap to be overwritten', async () => {
+ mockEsBin({ start: true });
+ process.env.ES_JAVA_OPTS = '-Xms5g -Xmx5g';
+
+ const cluster = new Cluster({ log });
+ await cluster.run();
+
+ expect(execa.mock.calls[0][2].env.ES_JAVA_OPTS).toEqual('-Xms5g -Xmx5g');
+ });
});
describe('#stop()', () => {
diff --git a/packages/kbn-i18n/BUILD.bazel b/packages/kbn-i18n/BUILD.bazel
new file mode 100644
index 000000000000000..02f5874a69a8389
--- /dev/null
+++ b/packages/kbn-i18n/BUILD.bazel
@@ -0,0 +1,132 @@
+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-i18n"
+PKG_REQUIRE_NAME = "@kbn/i18n"
+
+SOURCE_FILES = glob(
+ [
+ "src/**/*.ts",
+ "src/**/*.tsx",
+ "src/core/locales.js",
+ "types/**/*.ts",
+ ],
+ exclude = [
+ "**/*.test.*",
+ "**/__fixtures__/**",
+ "**/__snapshots__/**",
+ ],
+)
+
+SRCS = SOURCE_FILES
+
+filegroup(
+ name = "srcs",
+ srcs = SRCS,
+)
+
+NPM_MODULE_EXTRA_FILES = [
+ "angular/package.json",
+ "react/package.json",
+ "package.json",
+ "GUIDELINE.md",
+ "README.md"
+]
+
+SRC_DEPS = [
+ "//packages/kbn-babel-preset",
+ "//packages/kbn-dev-utils",
+ "@npm//@babel/core",
+ "@npm//babel-loader",
+ "@npm//del",
+ "@npm//getopts",
+ "@npm//intl-format-cache",
+ "@npm//intl-messageformat",
+ "@npm//intl-relativeformat",
+ "@npm//prop-types",
+ "@npm//react",
+ "@npm//react-intl",
+ "@npm//supports-color",
+]
+
+TYPES_DEPS = [
+ "@npm//typescript",
+ "@npm//@types/angular",
+ "@npm//@types/intl-relativeformat",
+ "@npm//@types/jest",
+ "@npm//@types/prop-types",
+ "@npm//@types/react",
+ "@npm//@types/react-intl",
+]
+
+DEPS = SRC_DEPS + TYPES_DEPS
+
+ts_config(
+ name = "tsconfig",
+ src = "tsconfig.json",
+ deps = [
+ "//:tsconfig.base.json",
+ ],
+)
+
+ts_config(
+ name = "tsconfig_browser",
+ src = "tsconfig.browser.json",
+ deps = [
+ "//:tsconfig.base.json",
+ "//:tsconfig.browser.json",
+ ],
+)
+
+ts_project(
+ name = "tsc",
+ args = ['--pretty'],
+ srcs = SRCS,
+ deps = DEPS,
+ allow_js = True,
+ declaration = True,
+ declaration_dir = "target_types",
+ declaration_map = True,
+ incremental = True,
+ out_dir = "target_node",
+ source_map = True,
+ root_dir = "src",
+ tsconfig = ":tsconfig",
+)
+
+ts_project(
+ name = "tsc_browser",
+ args = ['--pretty'],
+ srcs = SRCS,
+ deps = DEPS,
+ allow_js = True,
+ declaration = False,
+ incremental = True,
+ out_dir = "target_web",
+ source_map = True,
+ root_dir = "src",
+ tsconfig = ":tsconfig_browser",
+)
+
+js_library(
+ name = PKG_BASE_NAME,
+ srcs = NPM_MODULE_EXTRA_FILES,
+ deps = [":tsc", ":tsc_browser"] + DEPS,
+ 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-i18n/angular/package.json b/packages/kbn-i18n/angular/package.json
index 974058ec0ac9141..11c842a9fc49b2e 100644
--- a/packages/kbn-i18n/angular/package.json
+++ b/packages/kbn-i18n/angular/package.json
@@ -1,5 +1,5 @@
{
- "browser": "../target/web/angular",
- "main": "../target/node/angular",
- "types": "../target/types/angular/index.d.ts"
+ "browser": "../target_web/angular",
+ "main": "../target_node/angular",
+ "types": "../target_types/angular/index.d.ts"
}
\ No newline at end of file
diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json
index 1f9d21f724ea891..d91b81a88e098da 100644
--- a/packages/kbn-i18n/package.json
+++ b/packages/kbn-i18n/package.json
@@ -1,14 +1,9 @@
{
"name": "@kbn/i18n",
- "browser": "./target/web/browser.js",
- "main": "./target/node/index.js",
- "types": "./target/types/index.d.ts",
+ "browser": "./target_web/browser.js",
+ "main": "./target_node/index.js",
+ "types": "./target_types/index.d.ts",
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0",
- "private": true,
- "scripts": {
- "build": "node scripts/build",
- "kbn:bootstrap": "node scripts/build --source-maps",
- "kbn:watch": "node scripts/build --watch --source-maps"
- }
+ "private": true
}
\ No newline at end of file
diff --git a/packages/kbn-i18n/react/package.json b/packages/kbn-i18n/react/package.json
index d4cf1a0a30f61c0..c29ddd45f084d8c 100644
--- a/packages/kbn-i18n/react/package.json
+++ b/packages/kbn-i18n/react/package.json
@@ -1,5 +1,5 @@
{
- "browser": "../target/web/react",
- "main": "../target/node/react",
- "types": "../target/types/react/index.d.ts"
+ "browser": "../target_web/react",
+ "main": "../target_node/react",
+ "types": "../target_types/react/index.d.ts"
}
\ No newline at end of file
diff --git a/packages/kbn-i18n/scripts/build.js b/packages/kbn-i18n/scripts/build.js
deleted file mode 100644
index 62ef2f59239d0a5..000000000000000
--- a/packages/kbn-i18n/scripts/build.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the 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.
- */
-
-const { resolve } = require('path');
-
-const del = require('del');
-const supportsColor = require('supports-color');
-const { run, withProcRunner } = require('@kbn/dev-utils');
-
-const ROOT_DIR = resolve(__dirname, '..');
-const BUILD_DIR = resolve(ROOT_DIR, 'target');
-
-const padRight = (width, str) =>
- str.length >= width ? str : `${str}${' '.repeat(width - str.length)}`;
-
-run(
- async ({ log, flags }) => {
- await withProcRunner(log, async (proc) => {
- log.info('Deleting old output');
- await del(BUILD_DIR);
-
- const cwd = ROOT_DIR;
- const env = { ...process.env };
- if (supportsColor.stdout) {
- env.FORCE_COLOR = 'true';
- }
-
- log.info(`Starting babel and typescript${flags.watch ? ' in watch mode' : ''}`);
- await Promise.all([
- ...['web', 'node'].map((subTask) =>
- proc.run(padRight(10, `babel:${subTask}`), {
- cmd: 'babel',
- args: [
- 'src',
- '--config-file',
- require.resolve('../babel.config.js'),
- '--out-dir',
- resolve(BUILD_DIR, subTask),
- '--extensions',
- '.ts,.js,.tsx',
- ...(flags.watch ? ['--watch'] : ['--quiet']),
- ...(!flags['source-maps'] || !!process.env.CODE_COVERAGE
- ? []
- : ['--source-maps', 'inline']),
- ],
- wait: true,
- env: {
- ...env,
- BABEL_ENV: subTask,
- },
- cwd,
- })
- ),
-
- proc.run(padRight(10, 'tsc'), {
- cmd: 'tsc',
- args: [
- ...(flags.watch ? ['--watch', '--preserveWatchOutput', 'true'] : []),
- ...(flags['source-maps'] ? ['--declarationMap', 'true'] : []),
- ],
- wait: true,
- env,
- cwd,
- }),
- ]);
-
- log.success('Complete');
- });
- },
- {
- description: 'Simple build tool for @kbn/i18n package',
- flags: {
- boolean: ['watch', 'source-maps'],
- help: `
- --watch Run in watch mode
- --source-maps Include sourcemaps
- `,
- },
- }
-);
diff --git a/packages/kbn-i18n/tsconfig.browser.json b/packages/kbn-i18n/tsconfig.browser.json
new file mode 100644
index 000000000000000..707e3294bf1e7b8
--- /dev/null
+++ b/packages/kbn-i18n/tsconfig.browser.json
@@ -0,0 +1,22 @@
+{
+ "extends": "../../tsconfig.browser.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "incremental": true,
+ "outDir": "./target_web",
+ "declaration": false,
+ "isolatedModules": true,
+ "sourceMap": true,
+ "sourceRoot": "../../../../../packages/kbn-i18n/src",
+ "types": ["node"],
+ },
+ "include": [
+ "src/**/*.ts",
+ "src/**/*.tsx",
+ "types/intl_format_cache.d.ts",
+ "types/intl_relativeformat.d.ts"
+ ],
+ "exclude": [
+ "**/__fixtures__/**/*"
+ ]
+}
diff --git a/packages/kbn-i18n/tsconfig.json b/packages/kbn-i18n/tsconfig.json
index 9d4cb8c9b0972b9..787e9b45123ace8 100644
--- a/packages/kbn-i18n/tsconfig.json
+++ b/packages/kbn-i18n/tsconfig.json
@@ -1,9 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "incremental": false,
- "outDir": "./target/types",
- "emitDeclarationOnly": true,
+ "allowJs": true,
+ "incremental": true,
+ "declarationDir": "./target_types",
+ "outDir": "./target_node",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
diff --git a/packages/kbn-interpreter/package.json b/packages/kbn-interpreter/package.json
index 997fbb0eb8a4ff0..fc0936f4b5f53bf 100644
--- a/packages/kbn-interpreter/package.json
+++ b/packages/kbn-interpreter/package.json
@@ -8,8 +8,5 @@
"build": "node scripts/build",
"kbn:bootstrap": "node scripts/build --dev",
"kbn:watch": "node scripts/build --dev --watch"
- },
- "dependencies": {
- "@kbn/i18n": "link:../kbn-i18n"
}
}
\ No newline at end of file
diff --git a/packages/kbn-legacy-logging/src/rotate/log_rotator.ts b/packages/kbn-legacy-logging/src/rotate/log_rotator.ts
index 4d57d869b9008f3..4b1e34839030f48 100644
--- a/packages/kbn-legacy-logging/src/rotate/log_rotator.ts
+++ b/packages/kbn-legacy-logging/src/rotate/log_rotator.ts
@@ -149,7 +149,7 @@ export class LogRotator {
if (this.usePolling && !this.shouldUsePolling) {
this.log(
['warning', 'logging:rotate'],
- 'Looks like your current environment support a faster algorithm then polling. You can try to disable `usePolling`'
+ 'Looks like your current environment support a faster algorithm than polling. You can try to disable `usePolling`'
);
}
diff --git a/packages/kbn-monaco/package.json b/packages/kbn-monaco/package.json
index 75f1d74f1c9c965..e818351e7e4700d 100644
--- a/packages/kbn-monaco/package.json
+++ b/packages/kbn-monaco/package.json
@@ -9,8 +9,5 @@
"build": "node ./scripts/build.js",
"kbn:bootstrap": "yarn build --dev",
"build:antlr4ts": "../../node_modules/antlr4ts-cli/antlr4ts ./src/painless/antlr/painless_lexer.g4 ./src/painless/antlr/painless_parser.g4 && node ./scripts/fix_generated_antlr.js"
- },
- "dependencies": {
- "@kbn/i18n": "link:../kbn-i18n"
}
}
diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml
index 63dd64f9202b3b6..2639f6fd273f7b2 100644
--- a/packages/kbn-optimizer/limits.yml
+++ b/packages/kbn-optimizer/limits.yml
@@ -46,7 +46,7 @@ pageLoadAssetSize:
lens: 96624
licenseManagement: 41817
licensing: 29004
- lists: 228500
+ lists: 200000
logstash: 53548
management: 46112
maps: 80000
@@ -61,7 +61,6 @@ pageLoadAssetSize:
remoteClusters: 51327
reporting: 183418
rollup: 97204
- ruleRegistry: 100000
savedObjects: 108518
savedObjectsManagement: 101836
savedObjectsTagging: 59482
@@ -69,7 +68,7 @@ pageLoadAssetSize:
searchprofiler: 67080
security: 95864
securityOss: 30806
- securitySolution: 187863
+ securitySolution: 76000
share: 99061
snapshotRestore: 79032
spaces: 57868
@@ -111,3 +110,4 @@ pageLoadAssetSize:
mapsEms: 26072
timelines: 28613
cases: 162385
+ screenshotMode: 17856
diff --git a/packages/kbn-plugin-generator/BUILD.bazel b/packages/kbn-plugin-generator/BUILD.bazel
new file mode 100644
index 000000000000000..e22d41076db00f7
--- /dev/null
+++ b/packages/kbn-plugin-generator/BUILD.bazel
@@ -0,0 +1,106 @@
+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-plugin-generator"
+PKG_REQUIRE_NAME = "@kbn/plugin-generator"
+
+SOURCE_FILES = glob(
+ [
+ "src/**/*.ts",
+ ],
+ exclude = [
+ "**/integration_tests/**/*",
+ ],
+)
+
+SRCS = SOURCE_FILES
+
+filegroup(
+ name = "srcs",
+ srcs = SRCS,
+)
+
+filegroup(
+ name = "template",
+ srcs = glob(
+ [
+ "template/**/*",
+ ],
+ ),
+)
+
+NPM_MODULE_EXTRA_FILES = [
+ "package.json",
+ "README.md",
+ ":template",
+]
+
+SRC_DEPS = [
+ "//packages/kbn-utils",
+ "//packages/kbn-dev-utils",
+ "@npm//del",
+ "@npm//ejs",
+ "@npm//execa",
+ "@npm//globby",
+ "@npm//inquirer",
+ "@npm//minimatch",
+ "@npm//prettier",
+ "@npm//vinyl-fs",
+]
+
+TYPES_DEPS = [
+ "@npm//@types/ejs",
+ "@npm//@types/inquirer",
+ "@npm//@types/jest",
+ "@npm//@types/minimatch",
+ "@npm//@types/node",
+ "@npm//@types/prettier",
+ "@npm//@types/vinyl-fs",
+]
+
+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 = [":tsc"] + DEPS,
+ 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-plugin-generator/package.json b/packages/kbn-plugin-generator/package.json
index 583085430d915b6..298373afd2f249a 100644
--- a/packages/kbn-plugin-generator/package.json
+++ b/packages/kbn-plugin-generator/package.json
@@ -4,9 +4,5 @@
"private": true,
"license": "SSPL-1.0 OR Elastic License 2.0",
"main": "target/index.js",
- "types": "target/index.d.ts",
- "scripts": {
- "kbn:bootstrap": "node scripts/build",
- "kbn:watch": "node scripts/build --watch"
- }
+ "types": "target/index.d.ts"
}
\ No newline at end of file
diff --git a/packages/kbn-plugin-generator/scripts/build.js b/packages/kbn-plugin-generator/scripts/build.js
deleted file mode 100644
index e17f564bc482c00..000000000000000
--- a/packages/kbn-plugin-generator/scripts/build.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the 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.
- */
-
-const Path = require('path');
-
-const { run } = require('@kbn/dev-utils');
-const del = require('del');
-const execa = require('execa');
-
-run(
- async ({ flags }) => {
- await del(Path.resolve(__dirname, '../target'));
-
- await execa(require.resolve('typescript/bin/tsc'), flags.watch ? ['--watch'] : [], {
- cwd: Path.resolve(__dirname, '..'),
- stdio: 'inherit',
- });
- },
- {
- flags: {
- boolean: ['watch'],
- help: `
- --watch Watch files and rebuild on changes
- `,
- },
- }
-);
diff --git a/packages/kbn-plugin-generator/tsconfig.json b/packages/kbn-plugin-generator/tsconfig.json
index 5e885527a76083b..9165fd21ebea0a7 100644
--- a/packages/kbn-plugin-generator/tsconfig.json
+++ b/packages/kbn-plugin-generator/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "incremental": false,
+ "incremental": true,
"outDir": "target",
"target": "ES2019",
"declaration": true,
diff --git a/packages/kbn-rule-data-utils/jest.config.js b/packages/kbn-rule-data-utils/jest.config.js
new file mode 100644
index 000000000000000..26cb39fe8b55ae8
--- /dev/null
+++ b/packages/kbn-rule-data-utils/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-rule-data-utils'],
+};
diff --git a/packages/kbn-rule-data-utils/package.json b/packages/kbn-rule-data-utils/package.json
new file mode 100644
index 000000000000000..6f0b8439ec89158
--- /dev/null
+++ b/packages/kbn-rule-data-utils/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "@kbn/rule-data-utils",
+ "main": "./target/index.js",
+ "types": "./target/index.d.ts",
+ "version": "1.0.0",
+ "license": "SSPL-1.0 OR Elastic License 2.0",
+ "private": true,
+ "scripts": {
+ "build": "../../node_modules/.bin/tsc",
+ "kbn:bootstrap": "yarn build",
+ "kbn:watch": "yarn build --watch"
+ }
+}
diff --git a/packages/kbn-rule-data-utils/src/index.ts b/packages/kbn-rule-data-utils/src/index.ts
new file mode 100644
index 000000000000000..93a2538c7aa2c09
--- /dev/null
+++ b/packages/kbn-rule-data-utils/src/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 './technical_field_names';
diff --git a/packages/kbn-rule-data-utils/src/technical_field_names.ts b/packages/kbn-rule-data-utils/src/technical_field_names.ts
new file mode 100644
index 000000000000000..31779c9f08e819c
--- /dev/null
+++ b/packages/kbn-rule-data-utils/src/technical_field_names.ts
@@ -0,0 +1,77 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { ValuesType } from 'utility-types';
+
+const ALERT_NAMESPACE = 'kibana.rac.alert';
+
+const TIMESTAMP = '@timestamp' as const;
+const EVENT_KIND = 'event.kind' as const;
+const EVENT_ACTION = 'event.action' as const;
+const RULE_UUID = 'rule.uuid' as const;
+const RULE_ID = 'rule.id' as const;
+const RULE_NAME = 'rule.name' as const;
+const RULE_CATEGORY = 'rule.category' as const;
+const TAGS = 'tags' as const;
+const PRODUCER = `${ALERT_NAMESPACE}.producer` as const;
+const ALERT_ID = `${ALERT_NAMESPACE}.id` as const;
+const ALERT_UUID = `${ALERT_NAMESPACE}.uuid` as const;
+const ALERT_START = `${ALERT_NAMESPACE}.start` as const;
+const ALERT_END = `${ALERT_NAMESPACE}.end` as const;
+const ALERT_DURATION = `${ALERT_NAMESPACE}.duration.us` as const;
+const ALERT_SEVERITY_LEVEL = `${ALERT_NAMESPACE}.severity.level` as const;
+const ALERT_SEVERITY_VALUE = `${ALERT_NAMESPACE}.severity.value` as const;
+const ALERT_STATUS = `${ALERT_NAMESPACE}.status` as const;
+const ALERT_EVALUATION_THRESHOLD = `${ALERT_NAMESPACE}.evaluation.threshold` as const;
+const ALERT_EVALUATION_VALUE = `${ALERT_NAMESPACE}.evaluation.value` as const;
+
+const fields = {
+ TIMESTAMP,
+ EVENT_KIND,
+ EVENT_ACTION,
+ RULE_UUID,
+ RULE_ID,
+ RULE_NAME,
+ RULE_CATEGORY,
+ TAGS,
+ PRODUCER,
+ ALERT_ID,
+ ALERT_UUID,
+ ALERT_START,
+ ALERT_END,
+ ALERT_DURATION,
+ ALERT_SEVERITY_LEVEL,
+ ALERT_SEVERITY_VALUE,
+ ALERT_STATUS,
+ ALERT_EVALUATION_THRESHOLD,
+ ALERT_EVALUATION_VALUE,
+};
+
+export {
+ TIMESTAMP,
+ EVENT_KIND,
+ EVENT_ACTION,
+ RULE_UUID,
+ RULE_ID,
+ RULE_NAME,
+ RULE_CATEGORY,
+ TAGS,
+ PRODUCER,
+ ALERT_ID,
+ ALERT_UUID,
+ ALERT_START,
+ ALERT_END,
+ ALERT_DURATION,
+ ALERT_SEVERITY_LEVEL,
+ ALERT_SEVERITY_VALUE,
+ ALERT_STATUS,
+ ALERT_EVALUATION_THRESHOLD,
+ ALERT_EVALUATION_VALUE,
+};
+
+export type TechnicalRuleDataFieldName = ValuesType;
diff --git a/packages/kbn-rule-data-utils/tsconfig.json b/packages/kbn-rule-data-utils/tsconfig.json
new file mode 100644
index 000000000000000..4b1262d11f3aff1
--- /dev/null
+++ b/packages/kbn-rule-data-utils/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "incremental": false,
+ "outDir": "./target",
+ "stripInternal": false,
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true,
+ "sourceRoot": "../../../../packages/kbn-rule-data-utils/src",
+ "types": [
+ "jest",
+ "node"
+ ]
+ },
+ "include": [
+ "./src/**/*.ts"
+ ]
+}
diff --git a/packages/kbn-securitysolution-es-utils/BUILD.bazel b/packages/kbn-securitysolution-es-utils/BUILD.bazel
new file mode 100644
index 000000000000000..0cc27358c5da277
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/BUILD.bazel
@@ -0,0 +1,86 @@
+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-es-utils"
+
+PKG_REQUIRE_NAME = "@kbn/securitysolution-es-utils"
+
+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 = [
+ "@npm//@elastic/elasticsearch",
+ "@npm//@hapi/hapi",
+ "@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 = [":tsc"] + DEPS,
+)
+
+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-es-utils/README.md b/packages/kbn-securitysolution-es-utils/README.md
new file mode 100644
index 000000000000000..b99aa095c84f474
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/README.md
@@ -0,0 +1,6 @@
+# kbn-securitysolution-es-utils
+
+This is the shared security solution elastic search utilities among plugins. This was originally created
+to remove the dependencies between security_solution and other projects such as lists. This should only be
+used within server side code and not client side code since it is all elastic search utilities and packages.
+
diff --git a/packages/kbn-securitysolution-es-utils/jest.config.js b/packages/kbn-securitysolution-es-utils/jest.config.js
new file mode 100644
index 000000000000000..6b86ec6e2da52b7
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/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-es-utils'],
+};
diff --git a/packages/kbn-securitysolution-es-utils/package.json b/packages/kbn-securitysolution-es-utils/package.json
new file mode 100644
index 000000000000000..7d0c0993c6c3263
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@kbn/securitysolution-es-utils",
+ "version": "1.0.0",
+ "description": "security solution elastic search utilities 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/src/bad_request_error/index.ts b/packages/kbn-securitysolution-es-utils/src/bad_request_error/index.ts
new file mode 100644
index 000000000000000..525f6cfa5c9ffe1
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/bad_request_error/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 class BadRequestError extends Error {}
diff --git a/packages/kbn-securitysolution-es-utils/src/create_boostrap_index/index.ts b/packages/kbn-securitysolution-es-utils/src/create_boostrap_index/index.ts
new file mode 100644
index 000000000000000..9671d35dc554e07
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/create_boostrap_index/index.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { ElasticsearchClient } from '../elasticsearch_client';
+
+// See the reference(s) below on explanations about why -000001 was chosen and
+// why the is_write_index is true as well as the bootstrapping step which is needed.
+// Ref: https://www.elastic.co/guide/en/elasticsearch/reference/current/applying-policy-to-template.html
+export const createBootstrapIndex = async (
+ esClient: ElasticsearchClient,
+ index: string
+): Promise => {
+ return (
+ await esClient.transport.request({
+ path: `/${index}-000001`,
+ method: 'PUT',
+ body: {
+ aliases: {
+ [index]: {
+ is_write_index: true,
+ },
+ },
+ },
+ })
+ ).body;
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/delete_all_index/index.ts b/packages/kbn-securitysolution-es-utils/src/delete_all_index/index.ts
new file mode 100644
index 000000000000000..4df4724aaf2b5ee
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/delete_all_index/index.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { ElasticsearchClient } from '../elasticsearch_client';
+
+export const deleteAllIndex = async (
+ esClient: ElasticsearchClient,
+ pattern: string,
+ maxAttempts = 5
+): Promise => {
+ for (let attempt = 1; ; attempt++) {
+ if (attempt > maxAttempts) {
+ throw new Error(
+ `Failed to delete indexes with pattern [${pattern}] after ${maxAttempts} attempts`
+ );
+ }
+
+ // resolve pattern to concrete index names
+ const { body: resp } = await esClient.indices.getAlias(
+ {
+ index: pattern,
+ },
+ { ignore: [404] }
+ );
+
+ // @ts-expect-error status doesn't exist on response
+ if (resp.status === 404) {
+ return true;
+ }
+
+ const indices = Object.keys(resp) as string[];
+
+ // if no indexes exits then we're done with this pattern
+ if (!indices.length) {
+ return true;
+ }
+
+ // delete the concrete indexes we found and try again until this pattern resolves to no indexes
+ await esClient.indices.delete({
+ index: indices,
+ ignore_unavailable: true,
+ });
+ }
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/delete_policy/index.ts b/packages/kbn-securitysolution-es-utils/src/delete_policy/index.ts
new file mode 100644
index 000000000000000..34c1d2e5da45f84
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/delete_policy/index.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { ElasticsearchClient } from '../elasticsearch_client';
+
+export const deletePolicy = async (
+ esClient: ElasticsearchClient,
+ policy: string
+): Promise => {
+ return (
+ await esClient.transport.request({
+ path: `/_ilm/policy/${policy}`,
+ method: 'DELETE',
+ })
+ ).body;
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/delete_template/index.ts b/packages/kbn-securitysolution-es-utils/src/delete_template/index.ts
new file mode 100644
index 000000000000000..2e7a71af9f772bf
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/delete_template/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 { ElasticsearchClient } from '../elasticsearch_client';
+
+export const deleteTemplate = async (
+ esClient: ElasticsearchClient,
+ name: string
+): Promise => {
+ return (
+ await esClient.indices.deleteTemplate({
+ name,
+ })
+ ).body;
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/elasticsearch_client/index.ts b/packages/kbn-securitysolution-es-utils/src/elasticsearch_client/index.ts
new file mode 100644
index 000000000000000..0c2252bdc1f033c
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/elasticsearch_client/index.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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.
+ */
+
+// Copied from src/core/server/elasticsearch/client/types.ts
+// as these types aren't part of any package yet. Once they are, remove this completely
+
+import type { KibanaClient } from '@elastic/elasticsearch/api/kibana';
+import type {
+ ApiResponse,
+ TransportRequestOptions,
+ TransportRequestParams,
+ TransportRequestPromise,
+} from '@elastic/elasticsearch/lib/Transport';
+
+/**
+ * Client used to query the elasticsearch cluster.
+ * @deprecated At some point use the one from src/core/server/elasticsearch/client/types.ts when it is made into a package. If it never is, then keep using this one.
+ * @public
+ */
+export type ElasticsearchClient = Omit<
+ KibanaClient,
+ 'connectionPool' | 'transport' | 'serializer' | 'extend' | 'child' | 'close'
+> & {
+ transport: {
+ request(
+ params: TransportRequestParams,
+ options?: TransportRequestOptions
+ ): TransportRequestPromise;
+ };
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/get_index_aliases/index.ts b/packages/kbn-securitysolution-es-utils/src/get_index_aliases/index.ts
new file mode 100644
index 000000000000000..885103c1fb584ae
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/get_index_aliases/index.ts
@@ -0,0 +1,51 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { ElasticsearchClient } from '../elasticsearch_client';
+
+interface AliasesResponse {
+ [indexName: string]: {
+ aliases: {
+ [aliasName: string]: {
+ is_write_index: boolean;
+ };
+ };
+ };
+}
+
+interface IndexAlias {
+ alias: string;
+ index: string;
+ isWriteIndex: boolean;
+}
+
+/**
+ * Retrieves all index aliases for a given alias name
+ *
+ * @param esClient An {@link ElasticsearchClient}
+ * @param alias alias name used to filter results
+ *
+ * @returns an array of {@link IndexAlias} objects
+ */
+export const getIndexAliases = async ({
+ esClient,
+ alias,
+}: {
+ esClient: ElasticsearchClient;
+ alias: string;
+}): Promise => {
+ const response = await esClient.indices.getAlias({
+ name: alias,
+ });
+
+ return Object.keys(response.body).map((index) => ({
+ alias,
+ index,
+ isWriteIndex: response.body[index].aliases[alias]?.is_write_index === true,
+ }));
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/get_index_count/index.ts b/packages/kbn-securitysolution-es-utils/src/get_index_count/index.ts
new file mode 100644
index 000000000000000..523b41303a5691b
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/get_index_count/index.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { ElasticsearchClient } from '../elasticsearch_client';
+
+/**
+ * Retrieves the count of documents in a given index
+ *
+ * @param esClient An {@link ElasticsearchClient}
+ * @param index index whose documents will be counted
+ *
+ * @returns the document count
+ */
+export const getIndexCount = async ({
+ esClient,
+ index,
+}: {
+ esClient: ElasticsearchClient;
+ index: string;
+}): Promise => {
+ const response = await esClient.count<{ count: number }>({
+ index,
+ });
+
+ return response.body.count;
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/get_index_exists/index.ts b/packages/kbn-securitysolution-es-utils/src/get_index_exists/index.ts
new file mode 100644
index 000000000000000..b7d12cab3f48c12
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/get_index_exists/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 { ElasticsearchClient } from '../elasticsearch_client';
+
+export const getIndexExists = async (
+ esClient: ElasticsearchClient,
+ index: string
+): Promise => {
+ try {
+ const { body: response } = await esClient.search({
+ index,
+ size: 0,
+ allow_no_indices: true,
+ body: {
+ terminate_after: 1,
+ },
+ });
+ return response._shards.total > 0;
+ } catch (err) {
+ if (err.body != null && err.body.status === 404) {
+ return false;
+ } else {
+ throw err.body ? err.body : err;
+ }
+ }
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/get_policy_exists/index.ts b/packages/kbn-securitysolution-es-utils/src/get_policy_exists/index.ts
new file mode 100644
index 000000000000000..cefd47dbe9d078d
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/get_policy_exists/index.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { ElasticsearchClient } from '../elasticsearch_client';
+
+export const getPolicyExists = async (
+ esClient: ElasticsearchClient,
+ policy: string
+): Promise => {
+ try {
+ await esClient.transport.request({
+ path: `/_ilm/policy/${policy}`,
+ method: 'GET',
+ });
+ // Return true that there exists a policy which is not 404 or some error
+ // Since there is not a policy exists API, this is how we create one by calling
+ // into the API to get it if it exists or rely on it to throw a 404
+ return true;
+ } catch (err) {
+ if (err.statusCode === 404) {
+ return false;
+ } else {
+ throw err;
+ }
+ }
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/get_template_exists/index.ts b/packages/kbn-securitysolution-es-utils/src/get_template_exists/index.ts
new file mode 100644
index 000000000000000..c56c5b968d45c81
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/get_template_exists/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 { ElasticsearchClient } from '../elasticsearch_client';
+
+export const getTemplateExists = async (
+ esClient: ElasticsearchClient,
+ template: string
+): Promise => {
+ return (
+ await esClient.indices.existsTemplate({
+ name: template,
+ })
+ ).body;
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/index.ts b/packages/kbn-securitysolution-es-utils/src/index.ts
new file mode 100644
index 000000000000000..cfa6820e9aac526
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/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 './bad_request_error';
+export * from './create_boostrap_index';
+export * from './delete_all_index';
+export * from './delete_policy';
+export * from './delete_template';
+export * from './elasticsearch_client';
+export * from './get_index_aliases';
+export * from './get_index_count';
+export * from './get_index_exists';
+export * from './get_policy_exists';
+export * from './get_template_exists';
+export * from './read_index';
+export * from './read_privileges';
+export * from './set_policy';
+export * from './set_template';
+export * from './transform_error';
diff --git a/packages/kbn-securitysolution-es-utils/src/read_index/index.ts b/packages/kbn-securitysolution-es-utils/src/read_index/index.ts
new file mode 100644
index 000000000000000..cc16645120b7022
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/read_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 { ElasticsearchClient } from '../elasticsearch_client';
+
+export const readIndex = async (esClient: ElasticsearchClient, index: string): Promise => {
+ return esClient.indices.get({
+ index,
+ });
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/read_privileges/index.ts b/packages/kbn-securitysolution-es-utils/src/read_privileges/index.ts
new file mode 100644
index 000000000000000..8b11387a1d02085
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/read_privileges/index.ts
@@ -0,0 +1,94 @@
+/*
+ * Copyright 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.
+ */
+
+/**
+ * Copied from src/core/server/elasticsearch/legacy/api_types.ts including its deprecation mentioned below
+ * TODO: Remove this and refactor the readPrivileges to utilize any newer client side ways rather than all this deprecated legacy stuff
+ */
+export interface LegacyCallAPIOptions {
+ /**
+ * Indicates whether `401 Unauthorized` errors returned from the Elasticsearch API
+ * should be wrapped into `Boom` error instances with properly set `WWW-Authenticate`
+ * header that could have been returned by the API itself. If API didn't specify that
+ * then `Basic realm="Authorization Required"` is used as `WWW-Authenticate`.
+ */
+ wrap401Errors?: boolean;
+ /**
+ * A signal object that allows you to abort the request via an AbortController object.
+ */
+ signal?: AbortSignal;
+}
+
+type CallWithRequest, V> = (
+ endpoint: string,
+ params: T,
+ options?: LegacyCallAPIOptions
+) => Promise;
+
+export const readPrivileges = async (
+ callWithRequest: CallWithRequest<{}, unknown>,
+ index: string
+): Promise => {
+ return callWithRequest('transport.request', {
+ path: '/_security/user/_has_privileges',
+ method: 'POST',
+ body: {
+ cluster: [
+ 'all',
+ 'create_snapshot',
+ 'manage',
+ 'manage_api_key',
+ 'manage_ccr',
+ 'manage_transform',
+ 'manage_ilm',
+ 'manage_index_templates',
+ 'manage_ingest_pipelines',
+ 'manage_ml',
+ 'manage_own_api_key',
+ 'manage_pipeline',
+ 'manage_rollup',
+ 'manage_saml',
+ 'manage_security',
+ 'manage_token',
+ 'manage_watcher',
+ 'monitor',
+ 'monitor_transform',
+ 'monitor_ml',
+ 'monitor_rollup',
+ 'monitor_watcher',
+ 'read_ccr',
+ 'read_ilm',
+ 'transport_client',
+ ],
+ index: [
+ {
+ names: [index],
+ privileges: [
+ 'all',
+ 'create',
+ 'create_doc',
+ 'create_index',
+ 'delete',
+ 'delete_index',
+ 'index',
+ 'manage',
+ 'maintenance',
+ 'manage_follow_index',
+ 'manage_ilm',
+ 'manage_leader_index',
+ 'monitor',
+ 'read',
+ 'read_cross_cluster',
+ 'view_index_metadata',
+ 'write',
+ ],
+ },
+ ],
+ },
+ });
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/set_policy/index.ts b/packages/kbn-securitysolution-es-utils/src/set_policy/index.ts
new file mode 100644
index 000000000000000..dc45ca3e1c0894c
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/set_policy/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 { ElasticsearchClient } from '../elasticsearch_client';
+
+export const setPolicy = async (
+ esClient: ElasticsearchClient,
+ policy: string,
+ body: Record
+): Promise => {
+ return (
+ await esClient.transport.request({
+ path: `/_ilm/policy/${policy}`,
+ method: 'PUT',
+ body,
+ })
+ ).body;
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/set_template/index.ts b/packages/kbn-securitysolution-es-utils/src/set_template/index.ts
new file mode 100644
index 000000000000000..89aaa44f29e0d0a
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/set_template/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 { ElasticsearchClient } from '../elasticsearch_client';
+
+export const setTemplate = async (
+ esClient: ElasticsearchClient,
+ name: string,
+ body: Record
+): Promise => {
+ return (
+ await esClient.indices.putTemplate({
+ name,
+ body,
+ })
+ ).body;
+};
diff --git a/packages/kbn-securitysolution-es-utils/src/transform_error/index.test.ts b/packages/kbn-securitysolution-es-utils/src/transform_error/index.test.ts
new file mode 100644
index 000000000000000..e0f520f1ebfd4f4
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/transform_error/index.test.ts
@@ -0,0 +1,101 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 Boom from '@hapi/boom';
+import { transformError } from '.';
+import { BadRequestError } from '../bad_request_error';
+import { errors } from '@elastic/elasticsearch';
+
+describe('transformError', () => {
+ test('returns transformed output error from boom object with a 500 and payload of internal server error', () => {
+ const boom = new Boom.Boom('some boom message');
+ const transformed = transformError(boom);
+ expect(transformed).toEqual({
+ message: 'An internal server error occurred',
+ statusCode: 500,
+ });
+ });
+
+ test('returns transformed output if it is some non boom object that has a statusCode', () => {
+ const error: Error & { statusCode?: number } = {
+ statusCode: 403,
+ name: 'some name',
+ message: 'some message',
+ };
+ const transformed = transformError(error);
+ expect(transformed).toEqual({
+ message: 'some message',
+ statusCode: 403,
+ });
+ });
+
+ test('returns a transformed message with the message set and statusCode', () => {
+ const error: Error & { statusCode?: number } = {
+ statusCode: 403,
+ name: 'some name',
+ message: 'some message',
+ };
+ const transformed = transformError(error);
+ expect(transformed).toEqual({
+ message: 'some message',
+ statusCode: 403,
+ });
+ });
+
+ test('transforms best it can if it is some non boom object but it does not have a status Code.', () => {
+ const error: Error = {
+ name: 'some name',
+ message: 'some message',
+ };
+ const transformed = transformError(error);
+ expect(transformed).toEqual({
+ message: 'some message',
+ statusCode: 500,
+ });
+ });
+
+ test('it detects a BadRequestError and returns a status code of 400 from that particular error type', () => {
+ const error: BadRequestError = new BadRequestError('I have a type error');
+ const transformed = transformError(error);
+ expect(transformed).toEqual({
+ message: 'I have a type error',
+ statusCode: 400,
+ });
+ });
+
+ test('it detects a BadRequestError and returns a Boom status of 400', () => {
+ const error: BadRequestError = new BadRequestError('I have a type error');
+ const transformed = transformError(error);
+ expect(transformed).toEqual({
+ message: 'I have a type error',
+ statusCode: 400,
+ });
+ });
+
+ it('transforms a ResponseError returned by the elasticsearch client', () => {
+ const error: errors.ResponseError = {
+ name: 'ResponseError',
+ message: 'illegal_argument_exception',
+ headers: {},
+ body: {
+ error: {
+ type: 'illegal_argument_exception',
+ reason: 'detailed explanation',
+ },
+ },
+ meta: ({} as unknown) as errors.ResponseError['meta'],
+ statusCode: 400,
+ };
+ const transformed = transformError(error);
+
+ expect(transformed).toEqual({
+ message: 'illegal_argument_exception: detailed explanation',
+ statusCode: 400,
+ });
+ });
+});
diff --git a/packages/kbn-securitysolution-es-utils/src/transform_error/index.ts b/packages/kbn-securitysolution-es-utils/src/transform_error/index.ts
new file mode 100644
index 000000000000000..b532dc5d1b6d070
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/src/transform_error/index.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 Boom from '@hapi/boom';
+import { errors } from '@elastic/elasticsearch';
+import { BadRequestError } from '../bad_request_error';
+
+export interface OutputError {
+ message: string;
+ statusCode: number;
+}
+
+export const transformError = (err: Error & Partial): OutputError => {
+ if (Boom.isBoom(err)) {
+ return {
+ message: err.output.payload.message,
+ statusCode: err.output.statusCode,
+ };
+ } else {
+ if (err.statusCode != null) {
+ if (err.body != null && err.body.error != null) {
+ return {
+ statusCode: err.statusCode,
+ message: `${err.body.error.type}: ${err.body.error.reason}`,
+ };
+ } else {
+ return {
+ statusCode: err.statusCode,
+ message: err.message,
+ };
+ }
+ } else if (err instanceof BadRequestError) {
+ // allows us to throw request validation errors in the absence of Boom
+ return {
+ message: err.message,
+ statusCode: 400,
+ };
+ } else {
+ // natively return the err and allow the regular framework
+ // to deal with the error when it is a non Boom
+ return {
+ message: err.message != null ? err.message : '(unknown error message)',
+ statusCode: 500,
+ };
+ }
+ }
+};
diff --git a/packages/kbn-securitysolution-es-utils/tsconfig.json b/packages/kbn-securitysolution-es-utils/tsconfig.json
new file mode 100644
index 000000000000000..be8848d781caea5
--- /dev/null
+++ b/packages/kbn-securitysolution-es-utils/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-es-utils/src",
+ "types": [
+ "jest",
+ "node"
+ ]
+ },
+ "include": [
+ "src/**/*"
+ ]
+}
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel b/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel
new file mode 100644
index 000000000000000..ba7123d0c1f21a0
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel
@@ -0,0 +1,94 @@
+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-io-ts-alerting-types"
+PKG_REQUIRE_NAME = "@kbn/securitysolution-io-ts-alerting-types"
+
+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-types",
+ "//packages/kbn-securitysolution-io-ts-utils",
+ "//packages/elastic-datemath",
+ "@npm//fp-ts",
+ "@npm//io-ts",
+ "@npm//lodash",
+ "@npm//moment",
+ "@npm//tslib",
+ "@npm//uuid",
+]
+
+TYPES_DEPS = [
+ "@npm//@types/flot",
+ "@npm//@types/jest",
+ "@npm//@types/lodash",
+ "@npm//@types/node",
+ "@npm//@types/uuid"
+]
+
+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 = [":tsc"] + DEPS,
+ 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-securitysolution-io-ts-alerting-types/README.md b/packages/kbn-securitysolution-io-ts-alerting-types/README.md
new file mode 100644
index 000000000000000..b8fa8234f2d85f6
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/README.md
@@ -0,0 +1,8 @@
+# kbn-securitysolution-io-ts-alerting-types
+
+Types that are specific to the security solution alerting to be shared among plugins.
+
+Related packages are
+* kbn-securitysolution-io-ts-utils
+* kbn-securitysolution-io-ts-list-types
+* kbn-securitysolution-io-ts-types
\ No newline at end of file
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/jest.config.js b/packages/kbn-securitysolution-io-ts-alerting-types/jest.config.js
new file mode 100644
index 000000000000000..6125b95a9bce580
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/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-io-ts-alerting-types'],
+};
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/package.json b/packages/kbn-securitysolution-io-ts-alerting-types/package.json
new file mode 100644
index 000000000000000..ac972e06c1dc90e
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@kbn/securitysolution-io-ts-alerting-types",
+ "version": "1.0.0",
+ "description": "io ts utilities and types to be shared with plugins from the security solution project",
+ "license": "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-io-ts-utils/src/actions/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/actions/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/constants/index.mock.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.mock.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/constants/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.mock.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/constants/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/constants/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/constants/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_actions_array/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_actions_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_actions_array/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_actions_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts
new file mode 100644
index 000000000000000..f0fe7f44a6f3e5e
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { DefaultExportFileName } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_export_file_name', () => {
+ test('it should validate a regular string', () => {
+ const payload = 'some string';
+ const decoded = DefaultExportFileName.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should not validate a number', () => {
+ const payload = 5;
+ const decoded = DefaultExportFileName.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "DefaultExportFileName"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default of "export.ndjson"', () => {
+ const payload = null;
+ const decoded = DefaultExportFileName.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual('export.ndjson');
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts
new file mode 100644
index 000000000000000..ccfb7923a230c51
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { DefaultFromString } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_from_string', () => {
+ test('it should validate a from string', () => {
+ const payload = 'now-20m';
+ const decoded = DefaultFromString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should not validate a number', () => {
+ const payload = 5;
+ const decoded = DefaultFromString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "DefaultFromString"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default of "now-6m"', () => {
+ const payload = null;
+ const decoded = DefaultFromString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual('now-6m');
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts
new file mode 100644
index 000000000000000..f5706677e6c5d88
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { DefaultIntervalString } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_interval_string', () => {
+ test('it should validate a interval string', () => {
+ const payload = '20m';
+ const decoded = DefaultIntervalString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should not validate a number', () => {
+ const payload = 5;
+ const decoded = DefaultIntervalString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "DefaultIntervalString"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default of "5m"', () => {
+ const payload = null;
+ const decoded = DefaultIntervalString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual('5m');
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts
new file mode 100644
index 000000000000000..82bd8607dae727b
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { Language } from '../language';
+import { DefaultLanguageString } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_language_string', () => {
+ test('it should validate a string', () => {
+ const payload: Language = 'lucene';
+ const decoded = DefaultLanguageString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should not validate a number', () => {
+ const payload = 5;
+ const decoded = DefaultLanguageString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "DefaultLanguageString"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default of "kuery"', () => {
+ const payload = null;
+ const decoded = DefaultLanguageString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual('kuery');
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts
new file mode 100644
index 000000000000000..eb2af1dbea41a07
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts
@@ -0,0 +1,66 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { DefaultMaxSignalsNumber } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { DEFAULT_MAX_SIGNALS } from '../constants';
+
+describe('default_from_string', () => {
+ test('it should validate a max signal number', () => {
+ const payload = 5;
+ const decoded = DefaultMaxSignalsNumber.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should not validate a string', () => {
+ const payload = '5';
+ const decoded = DefaultMaxSignalsNumber.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "DefaultMaxSignals"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should not validate a zero', () => {
+ const payload = 0;
+ const decoded = DefaultMaxSignalsNumber.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "0" supplied to "DefaultMaxSignals"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should not validate a negative number', () => {
+ const payload = -1;
+ const decoded = DefaultMaxSignalsNumber.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "-1" supplied to "DefaultMaxSignals"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default of DEFAULT_MAX_SIGNALS', () => {
+ const payload = null;
+ const decoded = DefaultMaxSignalsNumber.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(DEFAULT_MAX_SIGNALS);
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts
new file mode 100644
index 000000000000000..cca1c7e2774f454
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts
@@ -0,0 +1,85 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { DefaultPage } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_page', () => {
+ test('it should validate a regular number greater than zero', () => {
+ const payload = 5;
+ const decoded = DefaultPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should validate a string of a number', () => {
+ const payload = '5';
+ const decoded = DefaultPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(5);
+ });
+
+ test('it should not validate a junk string', () => {
+ const payload = 'invalid-string';
+ const decoded = DefaultPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "NaN" supplied to "DefaultPerPage"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should not validate an empty string', () => {
+ const payload = '';
+ const decoded = DefaultPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "NaN" supplied to "DefaultPerPage"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should not validate a zero', () => {
+ const payload = 0;
+ const decoded = DefaultPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "0" supplied to "DefaultPerPage"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should not validate a negative number', () => {
+ const payload = -1;
+ const decoded = DefaultPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "-1" supplied to "DefaultPerPage"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default of 20', () => {
+ const payload = null;
+ const decoded = DefaultPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(1);
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.ts
new file mode 100644
index 000000000000000..f9140be68ec8d78
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/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 * as t from 'io-ts';
+import { Either } from 'fp-ts/lib/Either';
+import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types';
+
+/**
+ * Types the DefaultPerPage as:
+ * - If a string this will convert the string to a number
+ * - If null or undefined, then a default of 1 will be used
+ * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero
+ */
+export const DefaultPage = new t.Type(
+ 'DefaultPerPage',
+ t.number.is,
+ (input, context): Either => {
+ if (input == null) {
+ return t.success(1);
+ } else if (typeof input === 'string') {
+ return PositiveIntegerGreaterThanZero.validate(parseInt(input, 10), context);
+ } else {
+ return PositiveIntegerGreaterThanZero.validate(input, context);
+ }
+ },
+ t.identity
+);
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts
new file mode 100644
index 000000000000000..88e91986a65dd87
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts
@@ -0,0 +1,85 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { DefaultPerPage } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_per_page', () => {
+ test('it should validate a regular number greater than zero', () => {
+ const payload = 5;
+ const decoded = DefaultPerPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should validate a string of a number', () => {
+ const payload = '5';
+ const decoded = DefaultPerPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(5);
+ });
+
+ test('it should not validate a junk string', () => {
+ const payload = 'invalid-string';
+ const decoded = DefaultPerPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "NaN" supplied to "DefaultPerPage"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should not validate an empty string', () => {
+ const payload = '';
+ const decoded = DefaultPerPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "NaN" supplied to "DefaultPerPage"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should not validate a zero', () => {
+ const payload = 0;
+ const decoded = DefaultPerPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "0" supplied to "DefaultPerPage"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should not validate a negative number', () => {
+ const payload = -1;
+ const decoded = DefaultPerPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "-1" supplied to "DefaultPerPage"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default of 20', () => {
+ const payload = null;
+ const decoded = DefaultPerPage.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(20);
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.ts
new file mode 100644
index 000000000000000..ea8f30c74506296
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/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 * as t from 'io-ts';
+import { Either } from 'fp-ts/lib/Either';
+import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types';
+
+/**
+ * Types the DefaultPerPage as:
+ * - If a string this will convert the string to a number
+ * - If null or undefined, then a default of 20 will be used
+ * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero
+ */
+export const DefaultPerPage = new t.Type(
+ 'DefaultPerPage',
+ t.number.is,
+ (input, context): Either => {
+ if (input == null) {
+ return t.success(20);
+ } else if (typeof input === 'string') {
+ return PositiveIntegerGreaterThanZero.validate(parseInt(input, 10), context);
+ } else {
+ return PositiveIntegerGreaterThanZero.validate(input, context);
+ }
+ },
+ t.identity
+);
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_risk_score_mapping_array/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_risk_score_mapping_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_risk_score_mapping_array/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_risk_score_mapping_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_severity_mapping_array/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_severity_mapping_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_severity_mapping_array/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_severity_mapping_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts
new file mode 100644
index 000000000000000..5f1ef3fc61fab2e
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts
@@ -0,0 +1,66 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { Threats } from '../threat';
+import { DefaultThreatArray } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_threat_null', () => {
+ test('it should validate an empty array', () => {
+ const payload: Threats = [];
+ const decoded = DefaultThreatArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should validate an array of threats', () => {
+ const payload: Threats = [
+ {
+ framework: 'MITRE ATTACK',
+ technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }],
+ tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA000999' },
+ },
+ ];
+ const decoded = DefaultThreatArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should not validate an array with a number', () => {
+ const payload = [
+ {
+ framework: 'MITRE ATTACK',
+ technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }],
+ tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA000999' },
+ },
+ 5,
+ ];
+ const decoded = DefaultThreatArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "DefaultThreatArray"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default empty array if not provided a value', () => {
+ const payload = null;
+ const decoded = DefaultThreatArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual([]);
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.test.ts
new file mode 100644
index 000000000000000..b92815d4fe828e1
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.test.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { Throttle } from '../throttle';
+import { DefaultThrottleNull } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_throttle_null', () => {
+ test('it should validate a throttle string', () => {
+ const payload: Throttle = 'some string';
+ const decoded = DefaultThrottleNull.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should not validate an array with a number', () => {
+ const payload = 5;
+ const decoded = DefaultThrottleNull.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "DefaultThreatNull"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default "null" if not provided a value', () => {
+ const payload = undefined;
+ const decoded = DefaultThrottleNull.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(null);
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts
new file mode 100644
index 000000000000000..31c35c8319fab88
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { DefaultToString } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_to_string', () => {
+ test('it should validate a to string', () => {
+ const payload = 'now-5m';
+ const decoded = DefaultToString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should not validate a number', () => {
+ const payload = 5;
+ const decoded = DefaultToString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "DefaultToString"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default of "now"', () => {
+ const payload = null;
+ const decoded = DefaultToString.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual('now');
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts
new file mode 100644
index 000000000000000..c471141a99a7636
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { DefaultUuid } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_uuid', () => {
+ test('it should validate a regular string', () => {
+ const payload = '1';
+ const decoded = DefaultUuid.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should not validate a number', () => {
+ const payload = 5;
+ const decoded = DefaultUuid.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "DefaultUuid"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default of a uuid', () => {
+ const payload = null;
+ const decoded = DefaultUuid.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toMatch(
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i
+ );
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts
new file mode 100644
index 000000000000000..73bf807e92c433f
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts
@@ -0,0 +1,25 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 * as t from 'io-ts';
+import { Either } from 'fp-ts/lib/Either';
+import uuid from 'uuid';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
+
+/**
+ * Types the DefaultUuid as:
+ * - If null or undefined, then a default string uuid.v4() will be
+ * created otherwise it will be checked just against an empty string
+ */
+export const DefaultUuid = new t.Type(
+ 'DefaultUuid',
+ t.string.is,
+ (input, context): Either =>
+ input == null ? t.success(uuid.v4()) : NonEmptyString.validate(input, context),
+ t.identity
+);
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
new file mode 100644
index 000000000000000..37ed4b2daa51001
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { Either } from 'fp-ts/lib/Either';
+import * as t from 'io-ts';
+import { parseScheduleDates } from '@kbn/securitysolution-io-ts-types';
+
+const stringValidator = (input: unknown): input is string => typeof input === 'string';
+
+export const from = new t.Type(
+ 'From',
+ t.string.is,
+ (input, context): Either => {
+ if (stringValidator(input) && parseScheduleDates(input) == null) {
+ return t.failure(input, context, 'Failed to parse "from" on rule param');
+ }
+ return t.string.validate(input, context);
+ },
+ t.identity
+);
+export type From = t.TypeOf;
+
+export const fromOrUndefined = t.union([from, t.undefined]);
+export type FromOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/index.ts
new file mode 100644
index 000000000000000..c6f29862206e6b3
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/index.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 './actions';
+export * from './constants';
+export * from './default_actions_array';
+export * from './default_export_file_name';
+export * from './default_from_string';
+export * from './default_interval_string';
+export * from './default_language_string';
+export * from './default_max_signals_number';
+export * from './default_page';
+export * from './default_per_page';
+export * from './default_risk_score_mapping_array';
+export * from './default_severity_mapping_array';
+export * from './default_threat_array';
+export * from './default_throttle_null';
+export * from './default_to_string';
+export * from './default_uuid';
+export * from './from';
+export * from './language';
+export * from './machine_learning_job_id';
+export * from './max_signals';
+export * from './normalized_ml_job_id';
+export * from './references_default_array';
+export * from './risk_score';
+export * from './risk_score_mapping';
+export * from './saved_object_attributes';
+export * from './severity';
+export * from './severity_mapping';
+export * from './threat';
+export * from './threat_mapping';
+export * from './threat_subtechnique';
+export * from './threat_tactic';
+export * from './threat_technique';
+export * from './throttle';
+export * from './type';
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/language/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/language/index.ts
new file mode 100644
index 000000000000000..0632f09e6a393ab
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/language/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 * as t from 'io-ts';
+
+export const language = t.keyof({ eql: null, kuery: null, lucene: null });
+export type Language = t.TypeOf;
+
+export const languageOrUndefined = t.union([language, t.undefined]);
+export type LanguageOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/machine_learning_job_id/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/machine_learning_job_id/index.ts
new file mode 100644
index 000000000000000..9e9c25c62b938b7
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/machine_learning_job_id/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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.
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+import * as t from 'io-ts';
+
+import { machine_learning_job_id_normalized } from '../normalized_ml_job_id';
+
+export const machine_learning_job_id = t.union([t.string, machine_learning_job_id_normalized]);
+export type MachineLearningJobId = t.TypeOf;
+
+export const machineLearningJobIdOrUndefined = t.union([machine_learning_job_id, t.undefined]);
+export type MachineLearningJobIdOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts
new file mode 100644
index 000000000000000..ef7a225d93733d7
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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.
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+import * as t from 'io-ts';
+import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types';
+
+export const max_signals = PositiveIntegerGreaterThanZero;
+export type MaxSignals = t.TypeOf;
+
+export const maxSignalsOrUndefined = t.union([max_signals, t.undefined]);
+export type MaxSignalsOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.ts
new file mode 100644
index 000000000000000..db26264c029cddb
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/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.
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+import * as t from 'io-ts';
+
+import { NonEmptyArray } from '@kbn/securitysolution-io-ts-types';
+
+export const machine_learning_job_id_normalized = NonEmptyArray(t.string);
+export type MachineLearningJobIdNormalized = t.TypeOf;
+
+export const machineLearningJobIdNormalizedOrUndefined = t.union([
+ machine_learning_job_id_normalized,
+ t.undefined,
+]);
+export type MachineLearningJobIdNormalizedOrUndefined = t.TypeOf<
+ typeof machineLearningJobIdNormalizedOrUndefined
+>;
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts
new file mode 100644
index 000000000000000..38fd27ac40fdf85
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { ReferencesDefaultArray } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_string_array', () => {
+ test('it should validate an empty array', () => {
+ const payload: string[] = [];
+ const decoded = ReferencesDefaultArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should validate an array of strings', () => {
+ const payload = ['value 1', 'value 2'];
+ const decoded = ReferencesDefaultArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should not validate an array with a number', () => {
+ const payload = ['value 1', 5];
+ const decoded = ReferencesDefaultArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "referencesWithDefaultArray"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default array entry', () => {
+ const payload = null;
+ const decoded = ReferencesDefaultArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual([]);
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts
new file mode 100644
index 000000000000000..d341ca8b3b4f784
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { RiskScore } from '.';
+
+describe('risk_score', () => {
+ test('it should validate a positive number', () => {
+ const payload = 1;
+ const decoded = RiskScore.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should validate a zero', () => {
+ const payload = 0;
+ const decoded = RiskScore.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should NOT validate a negative number', () => {
+ const payload = -1;
+ const decoded = RiskScore.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "-1" supplied to "RiskScore"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should NOT validate a string', () => {
+ const payload = 'some string';
+ const decoded = RiskScore.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "some string" supplied to "RiskScore"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should NOT validate a risk score greater than 100', () => {
+ const payload = 101;
+ const decoded = RiskScore.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "101" supplied to "RiskScore"']);
+ expect(message.schema).toEqual({});
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts
new file mode 100644
index 000000000000000..98b9c33e7e3ea52
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/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.
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+import * as t from 'io-ts';
+import { Either } from 'fp-ts/lib/Either';
+
+/**
+ * Types the risk score as:
+ * - Natural Number (positive integer and not a float),
+ * - Between the values [0 and 100] inclusive.
+ */
+export const RiskScore = new t.Type(
+ 'RiskScore',
+ t.number.is,
+ (input, context): Either => {
+ return typeof input === 'number' && Number.isSafeInteger(input) && input >= 0 && input <= 100
+ ? t.success(input)
+ : t.failure(input, context);
+ },
+ t.identity
+);
+
+export type RiskScoreC = typeof RiskScore;
+
+export const risk_score = RiskScore;
+export type RiskScore = t.TypeOf;
+
+export const riskScoreOrUndefined = t.union([risk_score, t.undefined]);
+export type RiskScoreOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.ts
new file mode 100644
index 000000000000000..be07bab64f4698e
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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.
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+import * as t from 'io-ts';
+import { operator } from '@kbn/securitysolution-io-ts-types';
+import { riskScoreOrUndefined } from '../risk_score';
+
+export const risk_score_mapping_field = t.string;
+export const risk_score_mapping_value = t.string;
+export const risk_score_mapping_item = t.exact(
+ t.type({
+ field: risk_score_mapping_field,
+ value: risk_score_mapping_value,
+ operator,
+ risk_score: riskScoreOrUndefined,
+ })
+);
+
+export const risk_score_mapping = t.array(risk_score_mapping_item);
+export type RiskScoreMapping = t.TypeOf;
+
+export const riskScoreMappingOrUndefined = t.union([risk_score_mapping, t.undefined]);
+export type RiskScoreMappingOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/saved_object_attributes/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/saved_object_attributes/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/saved_object_attributes/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/saved_object_attributes/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/severity/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/severity/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/severity/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/severity/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts
new file mode 100644
index 000000000000000..1a3fd50039c29b2
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/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.
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+import * as t from 'io-ts';
+
+import { operator } from '@kbn/securitysolution-io-ts-types';
+import { severity } from '../severity';
+
+export const severity_mapping_field = t.string;
+export const severity_mapping_value = t.string;
+export const severity_mapping_item = t.exact(
+ t.type({
+ field: severity_mapping_field,
+ operator,
+ value: severity_mapping_value,
+ severity,
+ })
+);
+export type SeverityMappingItem = t.TypeOf;
+
+export const severity_mapping = t.array(severity_mapping_item);
+export type SeverityMapping = t.TypeOf;
+
+export const severityMappingOrUndefined = t.union([severity_mapping, t.undefined]);
+export type SeverityMappingOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts
new file mode 100644
index 000000000000000..08ff6cca60a499f
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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.
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+import * as t from 'io-ts';
+import { threat_tactic } from '../threat_tactic';
+import { threat_techniques } from '../threat_technique';
+
+export const threat_framework = t.string;
+
+export const threat = t.intersection([
+ t.exact(
+ t.type({
+ framework: threat_framework,
+ tactic: threat_tactic,
+ })
+ ),
+ t.exact(
+ t.partial({
+ technique: threat_techniques,
+ })
+ ),
+]);
+
+export type Threat = t.TypeOf;
+
+export const threats = t.array(threat);
+export type Threats = t.TypeOf;
+
+export const threatsOrUndefined = t.union([threats, t.undefined]);
+export type ThreatsOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts
new file mode 100644
index 000000000000000..16fd1647e5bfc61
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts
@@ -0,0 +1,236 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import {
+ concurrent_searches,
+ items_per_search,
+ ThreatMapping,
+ threatMappingEntries,
+ ThreatMappingEntries,
+ threat_mapping,
+} from '.';
+import { foldLeftRight, getPaths, exactCheck } from '@kbn/securitysolution-io-ts-utils';
+
+describe('threat_mapping', () => {
+ describe('threatMappingEntries', () => {
+ test('it should validate an entry', () => {
+ const payload: ThreatMappingEntries = [
+ {
+ field: 'field.one',
+ type: 'mapping',
+ value: 'field.one',
+ },
+ ];
+ const decoded = threatMappingEntries.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should fail validation with an extra entry item', () => {
+ const payload: ThreatMappingEntries & Array<{ extra: string }> = [
+ {
+ field: 'field.one',
+ type: 'mapping',
+ value: 'field.one',
+ extra: 'blah',
+ },
+ ];
+ const decoded = threatMappingEntries.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation with a non string', () => {
+ const payload = ([
+ {
+ field: 5,
+ type: 'mapping',
+ value: 'field.one',
+ },
+ ] as unknown) as ThreatMappingEntries[];
+ const decoded = threatMappingEntries.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "field"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation with a wrong type', () => {
+ const payload = ([
+ {
+ field: 'field.one',
+ type: 'invalid',
+ value: 'field.one',
+ },
+ ] as unknown) as ThreatMappingEntries[];
+ const decoded = threatMappingEntries.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "invalid" supplied to "type"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+ });
+
+ describe('threat_mapping', () => {
+ test('it should validate a threat mapping', () => {
+ const payload: ThreatMapping = [
+ {
+ entries: [
+ {
+ field: 'field.one',
+ type: 'mapping',
+ value: 'field.one',
+ },
+ ],
+ },
+ ];
+ const decoded = threat_mapping.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+ });
+
+ test('it should fail validate with an extra key', () => {
+ const payload: ThreatMapping & Array<{ extra: string }> = [
+ {
+ entries: [
+ {
+ field: 'field.one',
+ type: 'mapping',
+ value: 'field.one',
+ },
+ ],
+ extra: 'invalid',
+ },
+ ];
+
+ const decoded = threat_mapping.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validate with an extra inner entry', () => {
+ const payload: ThreatMapping & Array<{ entries: Array<{ extra: string }> }> = [
+ {
+ entries: [
+ {
+ field: 'field.one',
+ type: 'mapping',
+ value: 'field.one',
+ extra: 'blah',
+ },
+ ],
+ },
+ ];
+
+ const decoded = threat_mapping.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validate with an extra inner entry with the wrong data type', () => {
+ const payload = ([
+ {
+ entries: [
+ {
+ field: 5,
+ type: 'mapping',
+ value: 'field.one',
+ },
+ ],
+ },
+ ] as unknown) as ThreatMapping;
+
+ const decoded = threat_mapping.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "entries,field"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validate with empty array', () => {
+ const payload: string[] = [];
+
+ const decoded = threat_mapping.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "[]" supplied to "NonEmptyArray"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation when concurrent_searches is < 0', () => {
+ const payload = -1;
+ const decoded = concurrent_searches.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "-1" supplied to "PositiveIntegerGreaterThanZero"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation when concurrent_searches is 0', () => {
+ const payload = 0;
+ const decoded = concurrent_searches.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "0" supplied to "PositiveIntegerGreaterThanZero"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation when items_per_search is 0', () => {
+ const payload = 0;
+ const decoded = items_per_search.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "0" supplied to "PositiveIntegerGreaterThanZero"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation when items_per_search is < 0', () => {
+ const payload = -1;
+ const decoded = items_per_search.decode(payload);
+ const checked = exactCheck(payload, decoded);
+ const message = pipe(checked, foldLeftRight);
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "-1" supplied to "PositiveIntegerGreaterThanZero"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts
new file mode 100644
index 000000000000000..abee0d2baceb06f
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts
@@ -0,0 +1,78 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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.
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+import * as t from 'io-ts';
+import {
+ NonEmptyArray,
+ NonEmptyString,
+ PositiveIntegerGreaterThanZero,
+} from '@kbn/securitysolution-io-ts-types';
+import { language } from '../language';
+
+export const threat_query = t.string;
+export type ThreatQuery = t.TypeOf;
+export const threatQueryOrUndefined = t.union([threat_query, t.undefined]);
+export type ThreatQueryOrUndefined = t.TypeOf;
+
+export const threat_indicator_path = t.string;
+export type ThreatIndicatorPath = t.TypeOf;
+export const threatIndicatorPathOrUndefined = t.union([threat_indicator_path, t.undefined]);
+export type ThreatIndicatorPathOrUndefined = t.TypeOf;
+
+export const threat_filters = t.array(t.unknown); // Filters are not easily type-able yet
+export type ThreatFilters = t.TypeOf;
+export const threatFiltersOrUndefined = t.union([threat_filters, t.undefined]);
+export type ThreatFiltersOrUndefined = t.TypeOf;
+
+export const threatMapEntry = t.exact(
+ t.type({
+ field: NonEmptyString,
+ type: t.keyof({ mapping: null }),
+ value: NonEmptyString,
+ })
+);
+
+export type ThreatMapEntry = t.TypeOf;
+
+export const threatMappingEntries = t.array(threatMapEntry);
+export type ThreatMappingEntries = t.TypeOf;
+
+export const threatMap = t.exact(
+ t.type({
+ entries: threatMappingEntries,
+ })
+);
+export type ThreatMap = t.TypeOf;
+
+export const threat_mapping = NonEmptyArray(threatMap, 'NonEmptyArray');
+export type ThreatMapping = t.TypeOf;
+
+export const threatMappingOrUndefined = t.union([threat_mapping, t.undefined]);
+export type ThreatMappingOrUndefined = t.TypeOf;
+
+export const threat_index = t.array(t.string);
+export type ThreatIndex = t.TypeOf;
+export const threatIndexOrUndefined = t.union([threat_index, t.undefined]);
+export type ThreatIndexOrUndefined = t.TypeOf;
+
+export const threat_language = t.union([language, t.undefined]);
+export type ThreatLanguage = t.TypeOf;
+export const threatLanguageOrUndefined = t.union([threat_language, t.undefined]);
+export type ThreatLanguageOrUndefined = t.TypeOf;
+
+export const concurrent_searches = PositiveIntegerGreaterThanZero;
+export type ConcurrentSearches = t.TypeOf;
+export const concurrentSearchesOrUndefined = t.union([concurrent_searches, t.undefined]);
+export type ConcurrentSearchesOrUndefined = t.TypeOf;
+
+export const items_per_search = PositiveIntegerGreaterThanZero;
+export type ItemsPerSearch = t.TypeOf;
+export const itemsPerSearchOrUndefined = t.union([items_per_search, t.undefined]);
+export type ItemsPerSearchOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts
new file mode 100644
index 000000000000000..4909b82d8ec5403
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts
@@ -0,0 +1,25 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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.
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+import * as t from 'io-ts';
+
+export const threat_subtechnique_id = t.string;
+export const threat_subtechnique_name = t.string;
+export const threat_subtechnique_reference = t.string;
+
+export const threat_subtechnique = t.type({
+ id: threat_subtechnique_id,
+ name: threat_subtechnique_name,
+ reference: threat_subtechnique_reference,
+});
+
+export const threat_subtechniques = t.array(threat_subtechnique);
+
+export type ThreatSubtechnique = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/threat_tactic/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_tactic/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/threat_tactic/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/threat_tactic/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts
new file mode 100644
index 000000000000000..2d56e842287d87a
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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.
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+import * as t from 'io-ts';
+import { threat_subtechniques } from '../threat_subtechnique';
+
+export const threat_technique_id = t.string;
+export const threat_technique_name = t.string;
+export const threat_technique_reference = t.string;
+
+export const threat_technique = t.intersection([
+ t.exact(
+ t.type({
+ id: threat_technique_id,
+ name: threat_technique_name,
+ reference: threat_technique_reference,
+ })
+ ),
+ t.exact(
+ t.partial({
+ subtechnique: threat_subtechniques,
+ })
+ ),
+]);
+export const threat_techniques = t.array(threat_technique);
+
+export type ThreatTechnique = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/throttle/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/throttle/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.ts
new file mode 100644
index 000000000000000..0e740378789920e
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 * as t from 'io-ts';
+
+export const type = t.keyof({
+ eql: null,
+ machine_learning: null,
+ query: null,
+ saved_query: null,
+ threshold: null,
+ threat_match: null,
+});
+export type Type = t.TypeOf;
+
+export const typeOrUndefined = t.union([type, t.undefined]);
+export type TypeOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json b/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json
new file mode 100644
index 000000000000000..3411ce2c93d0531
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/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-io-ts-alerting-types/src",
+ "types": [
+ "jest",
+ "node"
+ ]
+ },
+ "include": [
+ "src/**/*"
+ ]
+}
diff --git a/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel b/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel
new file mode 100644
index 000000000000000..e9b806288adddb7
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel
@@ -0,0 +1,94 @@
+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-io-ts-list-types"
+PKG_REQUIRE_NAME = "@kbn/securitysolution-io-list-types"
+
+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-types",
+ "//packages/kbn-securitysolution-io-ts-utils",
+ "//packages/elastic-datemath",
+ "@npm//fp-ts",
+ "@npm//io-ts",
+ "@npm//lodash",
+ "@npm//moment",
+ "@npm//tslib",
+ "@npm//uuid",
+]
+
+TYPES_DEPS = [
+ "@npm//@types/flot",
+ "@npm//@types/jest",
+ "@npm//@types/lodash",
+ "@npm//@types/node",
+ "@npm//@types/uuid"
+]
+
+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 = [":tsc"] + DEPS,
+ 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-securitysolution-io-ts-list-types/README.md b/packages/kbn-securitysolution-io-ts-list-types/README.md
new file mode 100644
index 000000000000000..090ede2ed7d625f
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/README.md
@@ -0,0 +1,8 @@
+# kbn-securitysolution-io-ts-list-types
+
+io-ts types that are specific to lists to be shared among plugins
+
+Related packages are
+* kbn-securitysolution-io-ts-alerting-types
+* kbn-securitysolution-io-ts-ts-utils
+* kbn-securitysolution-io-ts-types
\ No newline at end of file
diff --git a/packages/kbn-securitysolution-io-ts-list-types/jest.config.js b/packages/kbn-securitysolution-io-ts-list-types/jest.config.js
new file mode 100644
index 000000000000000..0312733b6a02bc5
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/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-io-ts-list-types'],
+};
diff --git a/packages/kbn-securitysolution-io-ts-list-types/package.json b/packages/kbn-securitysolution-io-ts-list-types/package.json
new file mode 100644
index 000000000000000..74893e59855bc4e
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@kbn/securitysolution-io-ts-list-types",
+ "version": "1.0.0",
+ "description": "io ts utilities and types to be shared with plugins from the security solution project",
+ "license": "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-io-ts-list-types/src/comment/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.mock.ts
new file mode 100644
index 000000000000000..380f7f13b621061
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.mock.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { Comment, CommentsArray } from '.';
+import { DATE_NOW, ID, USER } from '../constants/index.mock';
+
+export const getCommentsMock = (): Comment => ({
+ comment: 'some old comment',
+ created_at: DATE_NOW,
+ created_by: USER,
+ id: ID,
+});
+
+export const getCommentsArrayMock = (): CommentsArray => [getCommentsMock(), getCommentsMock()];
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.test.ts
new file mode 100644
index 000000000000000..89e734a92fd04e3
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.test.ts
@@ -0,0 +1,237 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { getCommentsArrayMock, getCommentsMock } from './index.mock';
+import {
+ Comment,
+ comment,
+ CommentsArray,
+ commentsArray,
+ CommentsArrayOrUndefined,
+ commentsArrayOrUndefined,
+} from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { DATE_NOW } from '../constants/index.mock';
+
+describe('Comment', () => {
+ describe('comment', () => {
+ test('it fails validation when "id" is undefined', () => {
+ const payload = { ...getCommentsMock(), id: undefined };
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "undefined" supplied to "id"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it passes validation with a typical comment', () => {
+ const payload = getCommentsMock();
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it passes validation with "updated_at" and "updated_by" fields included', () => {
+ const payload = getCommentsMock();
+ payload.updated_at = DATE_NOW;
+ payload.updated_by = 'someone';
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it fails validation when undefined', () => {
+ const payload = undefined;
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "undefined" supplied to "({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it fails validation when "comment" is an empty string', () => {
+ const payload: Omit & { comment: string } = {
+ ...getCommentsMock(),
+ comment: '',
+ };
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "comment"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it fails validation when "comment" is not a string', () => {
+ const payload: Omit & { comment: string[] } = {
+ ...getCommentsMock(),
+ comment: ['some value'],
+ };
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "["some value"]" supplied to "comment"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it fails validation when "created_at" is not a string', () => {
+ const payload: Omit & { created_at: number } = {
+ ...getCommentsMock(),
+ created_at: 1,
+ };
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "created_at"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it fails validation when "created_by" is not a string', () => {
+ const payload: Omit & { created_by: number } = {
+ ...getCommentsMock(),
+ created_by: 1,
+ };
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "created_by"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it fails validation when "updated_at" is not a string', () => {
+ const payload: Omit & { updated_at: number } = {
+ ...getCommentsMock(),
+ updated_at: 1,
+ };
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "updated_at"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it fails validation when "updated_by" is not a string', () => {
+ const payload: Omit & { updated_by: number } = {
+ ...getCommentsMock(),
+ updated_by: 1,
+ };
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "updated_by"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should strip out extra keys', () => {
+ const payload: Comment & {
+ extraKey?: string;
+ } = getCommentsMock();
+ payload.extraKey = 'some value';
+ const decoded = comment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(getCommentsMock());
+ });
+ });
+
+ describe('commentsArray', () => {
+ test('it passes validation an array of Comment', () => {
+ const payload = getCommentsArrayMock();
+ const decoded = commentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it passes validation when a Comment includes "updated_at" and "updated_by"', () => {
+ const commentsPayload = getCommentsMock();
+ commentsPayload.updated_at = DATE_NOW;
+ commentsPayload.updated_by = 'someone';
+ const payload = [{ ...commentsPayload }, ...getCommentsArrayMock()];
+ const decoded = commentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it fails validation when undefined', () => {
+ const payload = undefined;
+ const decoded = commentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "undefined" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it fails validation when array includes non Comment types', () => {
+ const payload = ([1] as unknown) as CommentsArray;
+ const decoded = commentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+ });
+
+ describe('commentsArrayOrUndefined', () => {
+ test('it passes validation an array of Comment', () => {
+ const payload = getCommentsArrayMock();
+ const decoded = commentsArrayOrUndefined.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it passes validation when undefined', () => {
+ const payload = undefined;
+ const decoded = commentsArrayOrUndefined.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it fails validation when array includes non Comment types', () => {
+ const payload = ([1] as unknown) as CommentsArrayOrUndefined;
+ const decoded = commentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.ts
new file mode 100644
index 000000000000000..3b8cc6cc6ce95c2
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 * as t from 'io-ts';
+
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
+import { created_at } from '../created_at';
+import { created_by } from '../created_by';
+import { id } from '../id';
+import { updated_at } from '../updated_at';
+import { updated_by } from '../updated_by';
+
+export const comment = t.intersection([
+ t.exact(
+ t.type({
+ comment: NonEmptyString,
+ created_at,
+ created_by,
+ id,
+ })
+ ),
+ t.exact(
+ t.partial({
+ updated_at,
+ updated_by,
+ })
+ ),
+]);
+
+export const commentsArray = t.array(comment);
+export type CommentsArray = t.TypeOf;
+export type Comment = t.TypeOf;
+export const commentsArrayOrUndefined = t.union([commentsArray, t.undefined]);
+export type CommentsArrayOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.mock.ts
new file mode 100644
index 000000000000000..d2107ae864f15a5
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.mock.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 const ENTRY_VALUE = 'some host name';
+export const FIELD = 'host.name';
+export const MATCH = 'match';
+export const MATCH_ANY = 'match_any';
+export const OPERATOR = 'included';
+export const NESTED = 'nested';
+export const NESTED_FIELD = 'parent.field';
+export const LIST_ID = 'some-list-id';
+export const LIST = 'list';
+export const TYPE = 'ip';
+export const EXISTS = 'exists';
+export const WILDCARD = 'wildcard';
+export const USER = 'some user';
+export const DATE_NOW = '2020-04-20T15:25:31.830Z';
+
+// Exception List specific
+export const ID = 'uuid_here';
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.ts
new file mode 100644
index 000000000000000..f86986fc328c586
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/constants/index.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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';
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.mock.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.mock.ts
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.test.ts
new file mode 100644
index 000000000000000..3baf0054221db63
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.test.ts
@@ -0,0 +1,134 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { getCreateCommentsArrayMock, getCreateCommentsMock } from './index.mock';
+import {
+ CreateComment,
+ createComment,
+ CreateCommentsArray,
+ createCommentsArray,
+ CreateCommentsArrayOrUndefined,
+ createCommentsArrayOrUndefined,
+} from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('CreateComment', () => {
+ describe('createComment', () => {
+ test('it passes validation with a default comment', () => {
+ const payload = getCreateCommentsMock();
+ const decoded = createComment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it fails validation when undefined', () => {
+ const payload = undefined;
+ const decoded = createComment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "undefined" supplied to "{| comment: NonEmptyString |}"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it fails validation when "comment" is not a string', () => {
+ const payload: Omit & { comment: string[] } = {
+ ...getCreateCommentsMock(),
+ comment: ['some value'],
+ };
+ const decoded = createComment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "["some value"]" supplied to "comment"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should strip out extra keys', () => {
+ const payload: CreateComment & {
+ extraKey?: string;
+ } = getCreateCommentsMock();
+ payload.extraKey = 'some value';
+ const decoded = createComment.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(getCreateCommentsMock());
+ });
+ });
+
+ describe('createCommentsArray', () => {
+ test('it passes validation an array of comments', () => {
+ const payload = getCreateCommentsArrayMock();
+ const decoded = createCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it fails validation when undefined', () => {
+ const payload = undefined;
+ const decoded = createCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "undefined" supplied to "Array<{| comment: NonEmptyString |}>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it fails validation when array includes non comments types', () => {
+ const payload = ([1] as unknown) as CreateCommentsArray;
+ const decoded = createCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+ });
+
+ describe('createCommentsArrayOrUndefined', () => {
+ test('it passes validation an array of comments', () => {
+ const payload = getCreateCommentsArrayMock();
+ const decoded = createCommentsArrayOrUndefined.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it passes validation when undefined', () => {
+ const payload = undefined;
+ const decoded = createCommentsArrayOrUndefined.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it fails validation when array includes non comments types', () => {
+ const payload = ([1] as unknown) as CreateCommentsArrayOrUndefined;
+ const decoded = createCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "Array<{| comment: NonEmptyString |}>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.ts
new file mode 100644
index 000000000000000..883675ce51f919a
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/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 * as t from 'io-ts';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
+
+export const createComment = t.exact(
+ t.type({
+ comment: NonEmptyString,
+ })
+);
+
+export type CreateComment = t.TypeOf;
+export const createCommentsArray = t.array(createComment);
+export type CreateCommentsArray = t.TypeOf;
+export type CreateComments = t.TypeOf;
+export const createCommentsArrayOrUndefined = t.union([createCommentsArray, t.undefined]);
+export type CreateCommentsArrayOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/created_at/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/created_at/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/created_at/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/created_at/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/created_by/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/created_by/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/created_by/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/created_by/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/default_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_comments_array/index.test.ts
new file mode 100644
index 000000000000000..440c60187668272
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/default_comments_array/index.test.ts
@@ -0,0 +1,65 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { CommentsArray } from '../comment';
+import { DefaultCommentsArray } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { getCommentsArrayMock } from '../comment/index.mock';
+
+describe('default_comments_array', () => {
+ test('it should pass validation when supplied an empty array', () => {
+ const payload: CommentsArray = [];
+ const decoded = DefaultCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should pass validation when supplied an array of comments', () => {
+ const payload: CommentsArray = getCommentsArrayMock();
+ const decoded = DefaultCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should fail validation when supplied an array of numbers', () => {
+ const payload = [1];
+ const decoded = DefaultCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation when supplied an array of strings', () => {
+ const payload = ['some string'];
+ const decoded = DefaultCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default array entry', () => {
+ const payload = null;
+ const decoded = DefaultCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual([]);
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_comments_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/default_comments_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/default_create_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_create_comments_array/index.test.ts
new file mode 100644
index 000000000000000..de45fd9f300fac3
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/default_create_comments_array/index.test.ts
@@ -0,0 +1,65 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { CommentsArray } from '../comment';
+import { DefaultCommentsArray } from '../default_comments_array';
+import { getCommentsArrayMock } from '../comment/index.mock';
+
+describe('default_comments_array', () => {
+ test('it should pass validation when supplied an empty array', () => {
+ const payload: CommentsArray = [];
+ const decoded = DefaultCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should pass validation when supplied an array of comments', () => {
+ const payload: CommentsArray = getCommentsArrayMock();
+ const decoded = DefaultCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should fail validation when supplied an array of numbers', () => {
+ const payload = [1];
+ const decoded = DefaultCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation when supplied an array of strings', () => {
+ const payload = ['some string'];
+ const decoded = DefaultCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString, created_at: string, created_by: string, id: NonEmptyString |} & Partial<{| updated_at: string, updated_by: string |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default array entry', () => {
+ const payload = null;
+ const decoded = DefaultCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual([]);
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_create_comments_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/default_create_comments_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace/index.test.ts
new file mode 100644
index 000000000000000..21e8c375b3d01bc
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace/index.test.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { DefaultNamespace } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_namespace', () => {
+ test('it should validate "single"', () => {
+ const payload = 'single';
+ const decoded = DefaultNamespace.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should validate "agnostic"', () => {
+ const payload = 'agnostic';
+ const decoded = DefaultNamespace.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it defaults to "single" if "undefined"', () => {
+ const payload = undefined;
+ const decoded = DefaultNamespace.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual('single');
+ });
+
+ test('it defaults to "single" if "null"', () => {
+ const payload = null;
+ const decoded = DefaultNamespace.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual('single');
+ });
+
+ test('it should FAIL validation if not "single" or "agnostic"', () => {
+ const payload = 'something else';
+ const decoded = DefaultNamespace.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ `Invalid value "something else" supplied to "DefaultNamespace"`,
+ ]);
+ expect(message.schema).toEqual({});
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/default_namespace/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace_array/index.test.ts
new file mode 100644
index 000000000000000..b02a3b96a5a3d36
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace_array/index.test.ts
@@ -0,0 +1,99 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { DefaultNamespaceArray, DefaultNamespaceArrayType } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+
+describe('default_namespace_array', () => {
+ test('it should validate "null" single item as an array with a "single" value', () => {
+ const payload: DefaultNamespaceArrayType = null;
+ const decoded = DefaultNamespaceArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(['single']);
+ });
+
+ test('it should FAIL validation of numeric value', () => {
+ const payload = 5;
+ const decoded = DefaultNamespaceArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "5" supplied to "DefaultNamespaceArray"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should validate "undefined" item as an array with a "single" value', () => {
+ const payload: DefaultNamespaceArrayType = undefined;
+ const decoded = DefaultNamespaceArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(['single']);
+ });
+
+ test('it should validate "single" as an array of a "single" value', () => {
+ const payload: DefaultNamespaceArrayType = 'single';
+ const decoded = DefaultNamespaceArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual([payload]);
+ });
+
+ test('it should validate "agnostic" as an array of a "agnostic" value', () => {
+ const payload: DefaultNamespaceArrayType = 'agnostic';
+ const decoded = DefaultNamespaceArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual([payload]);
+ });
+
+ test('it should validate "single,agnostic" as an array of 2 values of ["single", "agnostic"] values', () => {
+ const payload: DefaultNamespaceArrayType = 'agnostic,single';
+ const decoded = DefaultNamespaceArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(['agnostic', 'single']);
+ });
+
+ test('it should validate 3 elements of "single,agnostic,single" as an array of 3 values of ["single", "agnostic", "single"] values', () => {
+ const payload: DefaultNamespaceArrayType = 'single,agnostic,single';
+ const decoded = DefaultNamespaceArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(['single', 'agnostic', 'single']);
+ });
+
+ test('it should validate 3 elements of "single,agnostic, single" as an array of 3 values of ["single", "agnostic", "single"] values when there are spaces', () => {
+ const payload: DefaultNamespaceArrayType = ' single, agnostic, single ';
+ const decoded = DefaultNamespaceArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(['single', 'agnostic', 'single']);
+ });
+
+ test('it should FAIL validation when given 3 elements of "single,agnostic,junk" since the 3rd value is junk', () => {
+ const payload: DefaultNamespaceArrayType = 'single,agnostic,junk';
+ const decoded = DefaultNamespaceArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "junk" supplied to "DefaultNamespaceArray"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/default_namespace_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/default_update_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_update_comments_array/index.test.ts
new file mode 100644
index 000000000000000..fa6613538b18ece
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/default_update_comments_array/index.test.ts
@@ -0,0 +1,65 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { UpdateCommentsArray } from '../update_comment';
+import { DefaultUpdateCommentsArray } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { getUpdateCommentsArrayMock } from '../update_comment/index.mock';
+
+describe('default_update_comments_array', () => {
+ test('it should pass validation when supplied an empty array', () => {
+ const payload: UpdateCommentsArray = [];
+ const decoded = DefaultUpdateCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should pass validation when supplied an array of comments', () => {
+ const payload: UpdateCommentsArray = getUpdateCommentsArrayMock();
+ const decoded = DefaultUpdateCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should fail validation when supplied an array of numbers', () => {
+ const payload = [1];
+ const decoded = DefaultUpdateCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "1" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should fail validation when supplied an array of strings', () => {
+ const payload = ['some string'];
+ const decoded = DefaultUpdateCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "some string" supplied to "Array<({| comment: NonEmptyString |} & Partial<{| id: NonEmptyString |}>)>"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should return a default array entry', () => {
+ const payload = null;
+ const decoded = DefaultUpdateCommentsArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual([]);
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_update_comments_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/default_update_comments_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/description/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/description/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/description/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/description/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entries/index.mock.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entries/index.mock.ts
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entries/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entries/index.test.ts
new file mode 100644
index 000000000000000..09f1740567bc12c
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entries/index.test.ts
@@ -0,0 +1,111 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { getEndpointEntryMatchMock } from '../entry_match/index.mock';
+import {
+ endpointEntriesArray,
+ nonEmptyEndpointEntriesArray,
+ NonEmptyEndpointEntriesArray,
+} from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock';
+import { getEndpointEntryNestedMock } from '../entry_nested/index.mock';
+import { getEndpointEntriesArrayMock } from './index.mock';
+import { getEntryListMock } from '../../entries_list/index.mock';
+import { getEntryExistsMock } from '../../entries_exist/index.mock';
+
+describe('Endpoint', () => {
+ describe('entriesArray', () => {
+ test('it should validate an array with match entry', () => {
+ const payload = [getEndpointEntryMatchMock()];
+ const decoded = endpointEntriesArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should validate an array with match_any entry', () => {
+ const payload = [getEndpointEntryMatchAnyMock()];
+ const decoded = endpointEntriesArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should NOT validate an empty array', () => {
+ const payload: NonEmptyEndpointEntriesArray = [];
+ const decoded = nonEmptyEndpointEntriesArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "[]" supplied to "NonEmptyEndpointEntriesArray"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('type guard for nonEmptyEndpointNestedEntries should allow array of endpoint entries', () => {
+ const payload: NonEmptyEndpointEntriesArray = [getEndpointEntryMatchAnyMock()];
+ const guarded = nonEmptyEndpointEntriesArray.is(payload);
+ expect(guarded).toBeTruthy();
+ });
+
+ test('type guard for nonEmptyEndpointNestedEntries should disallow empty arrays', () => {
+ const payload: NonEmptyEndpointEntriesArray = [];
+ const guarded = nonEmptyEndpointEntriesArray.is(payload);
+ expect(guarded).toBeFalsy();
+ });
+
+ test('it should NOT validate an array with exists entry', () => {
+ const payload = [getEntryExistsMock()];
+ const decoded = endpointEntriesArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "exists" supplied to "type"',
+ 'Invalid value "undefined" supplied to "value"',
+ 'Invalid value "undefined" supplied to "entries"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should NOT validate an array with list entry', () => {
+ const payload = [getEntryListMock()];
+ const decoded = endpointEntriesArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "list" supplied to "type"',
+ 'Invalid value "undefined" supplied to "value"',
+ 'Invalid value "undefined" supplied to "entries"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should validate an array with nested entry', () => {
+ const payload = [getEndpointEntryNestedMock()];
+ const decoded = endpointEntriesArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should validate an array with all types of entries', () => {
+ const payload = getEndpointEntriesArrayMock();
+ const decoded = endpointEntriesArray.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entries/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entries/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.mock.ts
new file mode 100644
index 000000000000000..17a1a083d73d873
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.mock.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { EndpointEntryMatch } from '.';
+import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../../constants/index.mock';
+
+export const getEndpointEntryMatchMock = (): EndpointEntryMatch => ({
+ field: FIELD,
+ operator: OPERATOR,
+ type: MATCH,
+ value: ENTRY_VALUE,
+});
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.test.ts
new file mode 100644
index 000000000000000..fc3a2dded177d5e
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.test.ts
@@ -0,0 +1,102 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { pipe } from 'fp-ts/lib/pipeable';
+import { left } from 'fp-ts/lib/Either';
+import { getEndpointEntryMatchMock } from './index.mock';
+import { EndpointEntryMatch, endpointEntryMatch } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { getEntryMatchMock } from '../../entry_match/index.mock';
+
+describe('endpointEntryMatch', () => {
+ test('it should validate an entry', () => {
+ const payload = getEndpointEntryMatchMock();
+ const decoded = endpointEntryMatch.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should NOT validate when "operator" is "excluded"', () => {
+ // Use the generic entry mock so we can test operator: excluded
+ const payload = getEntryMatchMock();
+ payload.operator = 'excluded';
+ const decoded = endpointEntryMatch.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "excluded" supplied to "operator"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should FAIL validation when "field" is empty string', () => {
+ const payload: Omit & { field: string } = {
+ ...getEndpointEntryMatchMock(),
+ field: '',
+ };
+ const decoded = endpointEntryMatch.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should FAIL validation when "value" is not string', () => {
+ const payload: Omit & { value: string[] } = {
+ ...getEndpointEntryMatchMock(),
+ value: ['some value'],
+ };
+ const decoded = endpointEntryMatch.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "["some value"]" supplied to "value"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should FAIL validation when "value" is empty string', () => {
+ const payload: Omit & { value: string } = {
+ ...getEndpointEntryMatchMock(),
+ value: '',
+ };
+ const decoded = endpointEntryMatch.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should FAIL validation when "type" is not "match"', () => {
+ const payload: Omit & { type: string } = {
+ ...getEndpointEntryMatchMock(),
+ type: 'match_any',
+ };
+ const decoded = endpointEntryMatch.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "match_any" supplied to "type"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should strip out extra keys', () => {
+ const payload: EndpointEntryMatch & {
+ extraKey?: string;
+ } = getEndpointEntryMatchMock();
+ payload.extraKey = 'some value';
+ const decoded = endpointEntryMatch.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(getEntryMatchMock());
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.ts
new file mode 100644
index 000000000000000..07a1fc58a3d54a0
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 * as t from 'io-ts';
+import { NonEmptyString, operatorIncluded } from '@kbn/securitysolution-io-ts-types';
+
+export const endpointEntryMatch = t.exact(
+ t.type({
+ field: NonEmptyString,
+ operator: operatorIncluded,
+ type: t.keyof({ match: null }),
+ value: NonEmptyString,
+ })
+);
+export type EndpointEntryMatch = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.mock.ts
new file mode 100644
index 000000000000000..13fb16d73457da4
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.mock.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../../constants/index.mock';
+import { EndpointEntryMatchAny } from '.';
+
+export const getEndpointEntryMatchAnyMock = (): EndpointEntryMatchAny => ({
+ field: FIELD,
+ operator: OPERATOR,
+ type: MATCH_ANY,
+ value: [ENTRY_VALUE],
+});
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.test.ts
new file mode 100644
index 000000000000000..cf646477725196f
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.test.ts
@@ -0,0 +1,100 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { getEndpointEntryMatchAnyMock } from './index.mock';
+import { EndpointEntryMatchAny, endpointEntryMatchAny } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { getEntryMatchAnyMock } from '../../entry_match_any/index.mock';
+
+describe('endpointEntryMatchAny', () => {
+ test('it should validate an entry', () => {
+ const payload = getEndpointEntryMatchAnyMock();
+ const decoded = endpointEntryMatchAny.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should NOT validate when operator is "excluded"', () => {
+ // Use the generic entry mock so we can test operator: excluded
+ const payload = getEntryMatchAnyMock();
+ payload.operator = 'excluded';
+ const decoded = endpointEntryMatchAny.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "excluded" supplied to "operator"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should FAIL validation when field is empty string', () => {
+ const payload: Omit & { field: string } = {
+ ...getEndpointEntryMatchAnyMock(),
+ field: '',
+ };
+ const decoded = endpointEntryMatchAny.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should FAIL validation when value is empty array', () => {
+ const payload: Omit & { value: string[] } = {
+ ...getEndpointEntryMatchAnyMock(),
+ value: [],
+ };
+ const decoded = endpointEntryMatchAny.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "[]" supplied to "value"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should FAIL validation when value is not string array', () => {
+ const payload: Omit & { value: string } = {
+ ...getEndpointEntryMatchAnyMock(),
+ value: 'some string',
+ };
+ const decoded = endpointEntryMatchAny.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([
+ 'Invalid value "some string" supplied to "value"',
+ ]);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should FAIL validation when "type" is not "match_any"', () => {
+ const payload: Omit & { type: string } = {
+ ...getEndpointEntryMatchAnyMock(),
+ type: 'match',
+ };
+ const decoded = endpointEntryMatchAny.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should strip out extra keys', () => {
+ const payload: EndpointEntryMatchAny & {
+ extraKey?: string;
+ } = getEndpointEntryMatchAnyMock();
+ payload.extraKey = 'some extra key';
+ const decoded = endpointEntryMatchAny.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(getEntryMatchAnyMock());
+ });
+});
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.ts
new file mode 100644
index 000000000000000..23c15767a511c7c
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/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.
+ */
+
+import * as t from 'io-ts';
+import {
+ NonEmptyString,
+ nonEmptyOrNullableStringArray,
+ operatorIncluded,
+} from '@kbn/securitysolution-io-ts-types';
+
+export const endpointEntryMatchAny = t.exact(
+ t.type({
+ field: NonEmptyString,
+ operator: operatorIncluded,
+ type: t.keyof({ match_any: null }),
+ value: nonEmptyOrNullableStringArray,
+ })
+);
+export type EndpointEntryMatchAny = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_wildcard/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_wildcard/index.ts
new file mode 100644
index 000000000000000..2697f3edc3db4fe
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_wildcard/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 * as t from 'io-ts';
+import { NonEmptyString, operatorIncluded } from '@kbn/securitysolution-io-ts-types';
+
+export const endpointEntryMatchWildcard = t.exact(
+ t.type({
+ field: NonEmptyString,
+ operator: operatorIncluded,
+ type: t.keyof({ wildcard: null }),
+ value: NonEmptyString,
+ })
+);
+export type EndpointEntryMatchWildcard = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.mock.ts
new file mode 100644
index 000000000000000..31d983ba58fe3e7
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.mock.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { EndpointEntryNested } from '.';
+import { FIELD, NESTED } from '../../constants/index.mock';
+import { getEndpointEntryMatchMock } from '../entry_match/index.mock';
+import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock';
+
+export const getEndpointEntryNestedMock = (): EndpointEntryNested => ({
+ entries: [getEndpointEntryMatchMock(), getEndpointEntryMatchAnyMock()],
+ field: FIELD,
+ type: NESTED,
+});
diff --git a/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.test.ts
new file mode 100644
index 000000000000000..f8e54e495652707
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.test.ts
@@ -0,0 +1,137 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { left } from 'fp-ts/lib/Either';
+import { EndpointEntryNested, endpointEntryNested } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { getEndpointEntryNestedMock } from './index.mock';
+import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock';
+import {
+ nonEmptyEndpointNestedEntriesArray,
+ NonEmptyEndpointNestedEntriesArray,
+} from '../non_empty_nested_entries_array';
+import { getEndpointEntryMatchMock } from '../entry_match/index.mock';
+import { getEntryExistsMock } from '../../entries_exist/index.mock';
+
+describe('endpointEntryNested', () => {
+ test('it should validate a nested entry', () => {
+ const payload = getEndpointEntryNestedMock();
+ const decoded = endpointEntryNested.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual([]);
+ expect(message.schema).toEqual(payload);
+ });
+
+ test('it should FAIL validation when "type" is not "nested"', () => {
+ const payload: Omit & { type: 'match' } = {
+ ...getEndpointEntryNestedMock(),
+ type: 'match',
+ };
+ const decoded = endpointEntryNested.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should FAIL validation when "field" is empty string', () => {
+ const payload: Omit & {
+ field: string;
+ } = { ...getEndpointEntryNestedMock(), field: '' };
+ const decoded = endpointEntryNested.decode(payload);
+ const message = pipe(decoded, foldLeftRight);
+
+ expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']);
+ expect(message.schema).toEqual({});
+ });
+
+ test('it should FAIL validation when "field" is not a string', () => {
+ const payload: Omit