diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index de323128afed1..39daa5780436f 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -203,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
@@ -215,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/api_docs/spaces.json b/api_docs/spaces.json
index d53b69d5bd6b5..940bbcf88a484 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/dev_docs/building_blocks.mdx b/dev_docs/building_blocks.mdx
index 95851ea66b8cb..327492a20d5b8 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 ec78fa49aa231..aa38890a8ac9e 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/docs/developer/getting-started/monorepo-packages.asciidoc b/docs/developer/getting-started/monorepo-packages.asciidoc
index 1e7a95b83dd67..e81875d7893dd 100644
--- a/docs/developer/getting-started/monorepo-packages.asciidoc
+++ b/docs/developer/getting-started/monorepo-packages.asciidoc
@@ -74,19 +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/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md
index b868a7f8216df..5280d85f3d3b3 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 0000000000000..10e01d7e7a931
--- /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 0000000000000..722b11f0c7ba9
--- /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 0000000000000..8a4b378850764
--- /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 0000000000000..a79fa96695e36
--- /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 0000000000000..9140e94721f1e
--- /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 0000000000000..02b0c9c0949df
--- /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 0000000000000..d2e341627153c
--- /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 0000000000000..a6e0a274008a6
--- /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 0000000000000..66a7a19d18288
--- /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.elasticsearchstatusmeta.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md
index 2398410fa4b84..90aa2f0100d88 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 0000000000000..1b46078a1a453
--- /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.isavedobjectspointintimefinder.find.md b/docs/development/core/server/kibana-plugin-core-server.isavedobjectspointintimefinder.find.md
index 1755ff40c2bc0..29d4668becffc 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 4686df18e0134..950d6c078654c 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.md b/docs/development/core/server/kibana-plugin-core-server.md
index 3a9118a9c56bd..d638b84224e23 100644
--- a/docs/development/core/server/kibana-plugin-core-server.md
+++ b/docs/development/core/server/kibana-plugin-core-server.md
@@ -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.nodesversioncompatibility.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md
index 6fcfacc3bc908..cbdac9d5455b0 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 0000000000000..aa9421afed6e8
--- /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 0000000000000..8ac532c601efc
--- /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 0e8fa73039d40..cd0c352086425 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 2a30693f4da84..9fe43a2f3f477 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 a1b3378afc53b..20a0e99275a39 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 7b20ae41048f6..a1c2717e6e4a0 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 0000000000000..7ef1a2fb1bd41
--- /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 0000000000000..058c27032d065
--- /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 0000000000000..d46d5a6bf2a0a
--- /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 0000000000000..1f8b33c6e94e8
--- /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 0000000000000..2c2114103b29a
--- /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 0000000000000..07f4158a84950
--- /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 0000000000000..118d9744e4276
--- /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 711588bdd608c..0000000000000
--- 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 c0a1008ab5331..0000000000000
--- 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 9432b4bf80da6..0000000000000
--- 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 306f502f0b0b3..0000000000000
--- 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 4fc2e376304d4..0000000000000
--- 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 567390faba9b2..0000000000000
--- 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 0000000000000..155167d32a738
--- /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 8afd963464574..39d09807e4f3b 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 18ef5c3e6350c..0000000000000
--- 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 95c2251f72c90..2e293889b1794 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 0000000000000..7ababbbe1f535
--- /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 0000000000000..21522a0f32d6d
--- /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 0000000000000..e675658f2bf76
--- /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 0000000000000..c376a9e4258c8
--- /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 0000000000000..9311a66269753
--- /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 0000000000000..a36301a6451bc
--- /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 0000000000000..bc72e73994468
--- /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 0000000000000..4b5707d7228a5
--- /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 8a2afe6656fa4..0000000000000
--- 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 1175b79bc1abd..0000000000000
--- 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 6021c8866f018..0000000000000
--- 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 9600a9e891380..0000000000000
--- 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 4b69b10318ed3..0000000000000
--- 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 0000000000000..450cd14a20524
--- /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 5d9d2857f6e0b..c92a1986966fd 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 d5ffb6d9ff9d8..0000000000000
--- 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 00e6ed3aeddfc..191b125ef3f74 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 0000000000000..6914c1b46b829
--- /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 3fc386f263141..d71db9caf6a3b 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 0000000000000..dac110ac4f475
--- /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 0000000000000..847e40a8896b4
--- /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 0000000000000..2e54d1636c5e9
--- /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 0000000000000..49ee013c5d2da
--- /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 0000000000000..3d210f6ac51c7
--- /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 0000000000000..bf53277887bda
--- /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 0000000000000..13328e2aed094
--- /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 0000000000000..7d7ac4ada884d
--- /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 0000000000000..28a81ee5dfd6a
--- /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 0000000000000..03802278ee5a3
--- /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 0000000000000..52b1ca187925c
--- /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 0000000000000..da0bbb1088507
--- /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 2b3d3df1ec8d0..4e3dea5549b56 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 3a78395b42754..bf7f88ab37039 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 58a225a3a4bc3..7f5a042e0ab81 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/server/kibana-plugin-plugins-data-server.ifieldtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldtype.md
index 48836a1b620b8..5ac48d26a85d6 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 118b0104fbee6..7559695a0a331 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 f4404521561d2..dd1f3806c1408 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/management/action-types.asciidoc b/docs/management/action-types.asciidoc
index 6cdb1dbfa712e..ec5677bd04a6e 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/images/connectors-with-missing-secrets.png b/docs/management/images/connectors-with-missing-secrets.png
new file mode 100644
index 0000000000000..ffc902d4a4768
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 0000000000000..55a6e91d28c8d
Binary files /dev/null and b/docs/management/images/coonectors-import-banner.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 0000000000000..54dd5205a488d
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 b15c46254b770..e47858f58cd1a 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/timelion.asciidoc b/docs/user/dashboard/timelion.asciidoc
index ff71cd7b383bd..12d0169c13f66 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/package.json b/package.json
index d46617f2a6f2a..8024ecafde769 100644
--- a/package.json
+++ b/package.json
@@ -129,7 +129,7 @@
"@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",
@@ -137,9 +137,12 @@
"@kbn/monaco": "link:packages/kbn-monaco",
"@kbn/securitysolution-constants": "link:bazel-bin/packages/kbn-securitysolution-constants/npm_module",
"@kbn/securitysolution-es-utils": "link:bazel-bin/packages/kbn-securitysolution-es-utils/npm_module",
+ "@kbn/securitysolution-io-ts-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-types/npm_module",
+ "@kbn/securitysolution-io-ts-alerting-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-alerting-types/npm_module",
+ "@kbn/securitysolution-io-ts-list-types": "link:bazel-bin/packages/kbn-securitysolution-io-ts-list-types/npm_module",
"@kbn/securitysolution-io-ts-utils": "link:bazel-bin/packages/kbn-securitysolution-io-ts-utils/npm_module",
"@kbn/securitysolution-utils": "link:bazel-bin/packages/kbn-securitysolution-utils/npm_module",
- "@kbn/server-http-tools": "link:packages/kbn-server-http-tools",
+ "@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",
@@ -448,7 +451,7 @@
"@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",
diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel
index 2ae04e02cffd2..76250d8a1e864 100644
--- a/packages/BUILD.bazel
+++ b/packages/BUILD.bazel
@@ -16,17 +16,23 @@ 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",
diff --git a/packages/elastic-eslint-config-kibana/.eslintrc.js b/packages/elastic-eslint-config-kibana/.eslintrc.js
index 2e978c543cc69..a8c2e9546510e 100644
--- a/packages/elastic-eslint-config-kibana/.eslintrc.js
+++ b/packages/elastic-eslint-config-kibana/.eslintrc.js
@@ -70,6 +70,11 @@ module.exports = {
to: '@kbn/tinymath',
disallowedMessage: `Don't use 'tinymath', use '@kbn/tinymath'`
},
+ {
+ from: '@kbn/test/types/ftr',
+ to: '@kbn/test',
+ disallowedMessage: `import from the root of @kbn/test instead`
+ },
],
],
},
diff --git a/packages/kbn-cli-dev-mode/package.json b/packages/kbn-cli-dev-mode/package.json
index 0401e6a82e11a..dd491de55c075 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-docs-utils/BUILD.bazel b/packages/kbn-docs-utils/BUILD.bazel
new file mode 100644
index 0000000000000..e72d83851f5d2
--- /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 27d38d2d8ed4f..b2a52b2d1f78e 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 6f4a6fa2af8a5..9868c8b3d2bb4 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-i18n/BUILD.bazel b/packages/kbn-i18n/BUILD.bazel
new file mode 100644
index 0000000000000..d71f7d78b1221
--- /dev/null
+++ b/packages/kbn-i18n/BUILD.bazel
@@ -0,0 +1,154 @@
+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 = "types",
+ declaration_map = True,
+ incremental = True,
+ out_dir = "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 = "web",
+ source_map = True,
+ root_dir = "src",
+ tsconfig = ":tsconfig_browser",
+)
+
+filegroup(
+ name = "tsc_types",
+ srcs = [":tsc"],
+ output_group = "types",
+)
+
+filegroup(
+ name = "target_files",
+ srcs = [
+ ":tsc",
+ ":tsc_browser",
+ ":tsc_types",
+ ],
+)
+
+pkg_npm(
+ name = "target",
+ deps = [
+ ":target_files",
+ ],
+)
+
+js_library(
+ name = PKG_BASE_NAME,
+ srcs = NPM_MODULE_EXTRA_FILES,
+ deps = [":target"] + DEPS,
+ 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/package.json b/packages/kbn-i18n/package.json
index 1f9d21f724ea8..36b625b1097bf 100644
--- a/packages/kbn-i18n/package.json
+++ b/packages/kbn-i18n/package.json
@@ -5,10 +5,5 @@
"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/scripts/build.js b/packages/kbn-i18n/scripts/build.js
deleted file mode 100644
index 62ef2f59239d0..0000000000000
--- 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 0000000000000..9ee4aeed8da21
--- /dev/null
+++ b/packages/kbn-i18n/tsconfig.browser.json
@@ -0,0 +1,21 @@
+{
+ "extends": "../../tsconfig.browser.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "incremental": true,
+ "outDir": "./target/web",
+ "declaration": false,
+ "isolatedModules": true,
+ "sourceMap": true,
+ "sourceRoot": "../../../../../packages/kbn-i18n/src"
+ },
+ "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 9d4cb8c9b0972..ddb21915eac50 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 997fbb0eb8a4f..fc0936f4b5f53 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 4d57d869b9008..4b1e34839030f 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 75f1d74f1c9c9..e818351e7e470 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 448b5ad650da5..348ecb7eea7f2 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: 280504
+ lists: 200000
logstash: 53548
management: 46112
maps: 80000
@@ -68,7 +68,7 @@ pageLoadAssetSize:
searchprofiler: 67080
security: 95864
securityOss: 30806
- securitySolution: 187863
+ securitySolution: 76000
share: 99061
snapshotRestore: 79032
spaces: 57868
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/index/get_index_aliases.ts b/packages/kbn-securitysolution-es-utils/src/get_index_aliases/index.ts
similarity index 80%
rename from x-pack/plugins/security_solution/server/lib/detection_engine/index/get_index_aliases.ts
rename to packages/kbn-securitysolution-es-utils/src/get_index_aliases/index.ts
index d9e90cccc20af..885103c1fb584 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/index/get_index_aliases.ts
+++ b/packages/kbn-securitysolution-es-utils/src/get_index_aliases/index.ts
@@ -1,11 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
+ * 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 'src/core/server';
+import { ElasticsearchClient } from '../elasticsearch_client';
interface AliasesResponse {
[indexName: string]: {
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/index/get_index_count.ts b/packages/kbn-securitysolution-es-utils/src/get_index_count/index.ts
similarity index 71%
rename from x-pack/plugins/security_solution/server/lib/detection_engine/index/get_index_count.ts
rename to packages/kbn-securitysolution-es-utils/src/get_index_count/index.ts
index f45ef0a9ff59f..523b41303a569 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/index/get_index_count.ts
+++ b/packages/kbn-securitysolution-es-utils/src/get_index_count/index.ts
@@ -1,11 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
+ * 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 'src/core/server';
+import { ElasticsearchClient } from '../elasticsearch_client';
/**
* Retrieves the count of documents in a given index
diff --git a/packages/kbn-securitysolution-es-utils/src/index.ts b/packages/kbn-securitysolution-es-utils/src/index.ts
index 657a63eef15cd..cfa6820e9aac5 100644
--- a/packages/kbn-securitysolution-es-utils/src/index.ts
+++ b/packages/kbn-securitysolution-es-utils/src/index.ts
@@ -12,9 +12,12 @@ 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';
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 0000000000000..cc16645120b70
--- /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-io-ts-alerting-types/BUILD.bazel b/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel
new file mode 100644
index 0000000000000..ba7123d0c1f21
--- /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 0000000000000..b8fa8234f2d85
--- /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 0000000000000..6125b95a9bce5
--- /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 0000000000000..ac972e06c1dc9
--- /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-utils/src/default_export_file_name/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts
index 1f81f056386d7..f0fe7f44a6f3e 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_export_file_name/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_export_file_name/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultExportFileName } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_export_file_name', () => {
test('it should validate a regular string', () => {
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-utils/src/default_from_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts
similarity index 94%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts
index c1261f514540b..ccfb7923a230c 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_from_string/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_from_string/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultFromString } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_from_string', () => {
test('it should validate a from string', () => {
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-utils/src/default_interval_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts
index c4a0dc3664d0e..f5706677e6c5d 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_interval_string/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_interval_string/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultIntervalString } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_interval_string', () => {
test('it should validate a interval string', () => {
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-utils/src/default_language_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts
index 072c541a808a3..82bd8607dae72 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_language_string/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_language_string/index.test.ts
@@ -10,7 +10,7 @@ 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 '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_language_string', () => {
test('it should validate a string', () => {
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-utils/src/default_max_signals_number/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts
similarity index 96%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts
index bf703fa52d844..eb2af1dbea41a 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_max_signals_number/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_max_signals_number/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultMaxSignalsNumber } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { DEFAULT_MAX_SIGNALS } from '../constants';
describe('default_from_string', () => {
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-utils/src/default_page/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_page/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts
index 3bcad15a7ebb8..cca1c7e2774f4 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_page/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultPage } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_page', () => {
test('it should validate a regular number greater than zero', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_page/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.ts
similarity index 92%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_page/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.ts
index 056005b452a03..f9140be68ec8d 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_page/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_page/index.ts
@@ -8,7 +8,7 @@
import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';
-import { PositiveIntegerGreaterThanZero } from '../positive_integer_greater_than_zero';
+import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types';
/**
* Types the DefaultPerPage as:
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts
index f7361ba12a570..88e91986a65dd 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultPerPage } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_per_page', () => {
test('it should validate a regular number greater than zero', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.ts
similarity index 92%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.ts
index 026642f91c08a..ea8f30c745062 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_per_page/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_per_page/index.ts
@@ -8,7 +8,7 @@
import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';
-import { PositiveIntegerGreaterThanZero } from '../positive_integer_greater_than_zero';
+import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types';
/**
* Types the DefaultPerPage as:
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-utils/src/default_threat_array/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts
similarity index 96%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts
index ac86b5508ff14..5f1ef3fc61fab 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_threat_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_threat_array/index.test.ts
@@ -10,7 +10,7 @@ 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 '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_threat_null', () => {
test('it should validate an empty array', () => {
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-utils/src/default_throttle_null/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.test.ts
index 4b8877bd532c2..b92815d4fe828 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_throttle_null/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_throttle_null/index.test.ts
@@ -10,7 +10,7 @@ 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 '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_throttle_null', () => {
test('it should validate a throttle string', () => {
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-utils/src/default_to_string/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts
similarity index 94%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts
index bcab8ebd5f17c..31c35c8319fab 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_to_string/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_to_string/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultToString } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_to_string', () => {
test('it should validate a to string', () => {
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-utils/src/default_uuid/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts
index d8cdff416037c..c471141a99a76 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultUuid } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_uuid', () => {
test('it should validate a regular string', () => {
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts
similarity index 73%
rename from x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts
index ecb8cd00ff308..73bf807e92c43 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/default_uuid/index.ts
@@ -1,21 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
*/
import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';
import uuid from 'uuid';
-
-import { NonEmptyString } from './non_empty_string';
+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
- * @deprecated Use packages/kbn-securitysolution-io-ts-utils
*/
export const DefaultUuid = new t.Type(
'DefaultUuid',
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/from/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts
similarity index 82%
rename from packages/kbn-securitysolution-io-ts-utils/src/from/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts
index 963e2fa0444f0..37ed4b2daa510 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/from/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/from/index.ts
@@ -8,7 +8,7 @@
import { Either } from 'fp-ts/lib/Either';
import * as t from 'io-ts';
-import { parseScheduleDates } from '../parse_schedule_dates';
+import { parseScheduleDates } from '@kbn/securitysolution-io-ts-types';
const stringValidator = (input: unknown): input is string => typeof input === 'string';
@@ -24,3 +24,6 @@ export const from = new t.Type(
t.identity
);
export type From = t.TypeOf;
+
+export const fromOrUndefined = t.union([from, t.undefined]);
+export type FromOrUndefined = t.TypeOf;
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/index.ts
similarity index 53%
rename from x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/index.ts
index ff69075580ad9..c6f29862206e6 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/index.ts
@@ -1,15 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
*/
+export * from './actions';
+export * from './constants';
export * from './default_actions_array';
-export * from './default_array';
-export * from './default_boolean_false';
-export * from './default_boolean_true';
-export * from './default_empty_string';
export * from './default_export_file_name';
export * from './default_from_string';
export * from './default_interval_string';
@@ -19,22 +18,25 @@ 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_string_array';
-export * from './default_string_boolean_false';
export * from './default_threat_array';
export * from './default_throttle_null';
export * from './default_to_string';
export * from './default_uuid';
-export * from './default_version_number';
-export * from './iso_date_string';
-export * from './lists';
-export * from './lists_default_array';
-export * from './non_empty_array';
-export * from './non_empty_string';
-export * from './only_false_allowed';
-export * from './positive_integer';
-export * from './positive_integer_greater_than_zero';
+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 './uuid';
+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-utils/src/language/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/language/index.ts
similarity index 78%
rename from packages/kbn-securitysolution-io-ts-utils/src/language/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/language/index.ts
index fc3f70f1f2d88..0632f09e6a393 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/language/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/language/index.ts
@@ -10,3 +10,6 @@ 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 0000000000000..9e9c25c62b938
--- /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-utils/src/max_signals/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts
similarity index 70%
rename from packages/kbn-securitysolution-io-ts-utils/src/max_signals/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts
index 4c68cb01cf00f..ef7a225d93733 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/max_signals/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/max_signals/index.ts
@@ -9,7 +9,10 @@
/* eslint-disable @typescript-eslint/naming-convention */
import * as t from 'io-ts';
-import { PositiveIntegerGreaterThanZero } from '../positive_integer_greater_than_zero';
+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-utils/src/normalized_ml_job_id/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.ts
similarity index 92%
rename from packages/kbn-securitysolution-io-ts-utils/src/normalized_ml_job_id/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.ts
index 6c7eb0ae33ab9..db26264c029cd 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/normalized_ml_job_id/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/normalized_ml_job_id/index.ts
@@ -10,7 +10,7 @@
import * as t from 'io-ts';
-import { NonEmptyArray } from '../non_empty_array';
+import { NonEmptyArray } from '@kbn/securitysolution-io-ts-types';
export const machine_learning_job_id_normalized = NonEmptyArray(t.string);
export type MachineLearningJobIdNormalized = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts
similarity index 77%
rename from packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts
index 41754a7ce0606..38fd27ac40fdf 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/references_default_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/references_default_array/index.test.ts
@@ -8,13 +8,13 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
-import { DefaultStringArray } from '../default_string_array';
-import { foldLeftRight, getPaths } from '../test_utils';
+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 = DefaultStringArray.decode(payload);
+ const decoded = ReferencesDefaultArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
@@ -23,7 +23,7 @@ describe('default_string_array', () => {
test('it should validate an array of strings', () => {
const payload = ['value 1', 'value 2'];
- const decoded = DefaultStringArray.decode(payload);
+ const decoded = ReferencesDefaultArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
@@ -32,18 +32,18 @@ describe('default_string_array', () => {
test('it should not validate an array with a number', () => {
const payload = ['value 1', 5];
- const decoded = DefaultStringArray.decode(payload);
+ const decoded = ReferencesDefaultArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "5" supplied to "DefaultStringArray"',
+ 'Invalid value "5" supplied to "referencesWithDefaultArray"',
]);
expect(message.schema).toEqual({});
});
test('it should return a default array entry', () => {
const payload = null;
- const decoded = DefaultStringArray.decode(payload);
+ const decoded = ReferencesDefaultArray.decode(payload);
const message = pipe(decoded, foldLeftRight);
expect(getPaths(left(message.errors))).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-utils/src/risk_score/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts
similarity index 96%
rename from packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts
index bca8b92134928..d341ca8b3b4f7 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.test.ts
@@ -8,7 +8,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { RiskScore } from '.';
describe('risk_score', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts
similarity index 75%
rename from packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts
index 0aca7dd70ba1d..98b9c33e7e3ea 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/risk_score/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score/index.ts
@@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
+/* eslint-disable @typescript-eslint/naming-convention */
+
import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';
@@ -26,3 +28,9 @@ export const RiskScore = new t.Type(
);
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-utils/src/risk_score_mapping/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.ts
similarity index 75%
rename from packages/kbn-securitysolution-io-ts-utils/src/risk_score_mapping/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.ts
index 1d7ca20e80b3b..be07bab64f469 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/risk_score_mapping/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/risk_score_mapping/index.ts
@@ -9,12 +9,8 @@
/* eslint-disable @typescript-eslint/naming-convention */
import * as t from 'io-ts';
-import { RiskScore } from '../risk_score';
-
-import { operator } from '../operator';
-
-export const riskScoreOrUndefined = t.union([RiskScore, t.undefined]);
-export type RiskScoreOrUndefined = t.TypeOf;
+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;
@@ -29,3 +25,6 @@ export const risk_score_mapping_item = t.exact(
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-utils/src/severity_mapping/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts
similarity index 94%
rename from packages/kbn-securitysolution-io-ts-utils/src/severity_mapping/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts
index 9e7ee7d2831cd..1a3fd50039c29 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/severity_mapping/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/severity_mapping/index.ts
@@ -10,7 +10,7 @@
import * as t from 'io-ts';
-import { operator } from '../operator';
+import { operator } from '@kbn/securitysolution-io-ts-types';
import { severity } from '../severity';
export const severity_mapping_field = t.string;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/threat/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts
similarity index 87%
rename from packages/kbn-securitysolution-io-ts-utils/src/threat/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts
index 0e4022e3ec26e..08ff6cca60a49 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/threat/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat/index.ts
@@ -32,3 +32,6 @@ 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-utils/src/threat_mapping/index.test.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.test.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts
index 7f754fb2d87de..16fd1647e5bfc 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.test.ts
@@ -16,8 +16,7 @@ import {
ThreatMappingEntries,
threat_mapping,
} from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
-import { exactCheck } from '../exact_check';
+import { foldLeftRight, getPaths, exactCheck } from '@kbn/securitysolution-io-ts-utils';
describe('threat_mapping', () => {
describe('threatMappingEntries', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts
similarity index 94%
rename from packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts
index 4fc64fe1e0982..abee0d2baceb0 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/threat_mapping/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_mapping/index.ts
@@ -9,10 +9,12 @@
/* 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';
-import { NonEmptyArray } from '../non_empty_array';
-import { NonEmptyString } from '../non_empty_string';
-import { PositiveIntegerGreaterThanZero } from '../positive_integer_greater_than_zero';
export const threat_query = t.string;
export type ThreatQuery = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/threat_subtechnique/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts
similarity index 91%
rename from packages/kbn-securitysolution-io-ts-utils/src/threat_subtechnique/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts
index 8d64f53cb1623..4909b82d8ec54 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/threat_subtechnique/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_subtechnique/index.ts
@@ -21,3 +21,5 @@ export const threat_subtechnique = t.type({
});
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-utils/src/threat_technique/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts
similarity index 93%
rename from packages/kbn-securitysolution-io-ts-utils/src/threat_technique/index.ts
rename to packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts
index ed2e771e1e118..2d56e842287d8 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/threat_technique/index.ts
+++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/threat_technique/index.ts
@@ -30,3 +30,5 @@ export const threat_technique = t.intersection([
),
]);
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 0000000000000..0e74037878992
--- /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 0000000000000..3411ce2c93d05
--- /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 0000000000000..e9b806288addd
--- /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 0000000000000..090ede2ed7d62
--- /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 0000000000000..0312733b6a02b
--- /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 0000000000000..74893e59855bc
--- /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-utils/src/list_types/comment/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.mock.ts
similarity index 90%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/comment/index.mock.ts
index 56440d628e4aa..380f7f13b6210 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { Comment, CommentsArray } from '.';
-import { DATE_NOW, ID, USER } from '../../constants/index.mock';
+import { DATE_NOW, ID, USER } from '../constants/index.mock';
export const getCommentsMock = (): Comment => ({
comment: 'some old comment',
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/comment/index.test.ts
index 0f0bfac5e2068..89e734a92fd04 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.test.ts
@@ -17,8 +17,8 @@ import {
CommentsArrayOrUndefined,
commentsArrayOrUndefined,
} from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
-import { DATE_NOW } from '../../constants/index.mock';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
+import { DATE_NOW } from '../constants/index.mock';
describe('Comment', () => {
describe('comment', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.ts
similarity index 77%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/comment/index.ts
index 783d8606b8a96..3b8cc6cc6ce95 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/comment/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/comment/index.ts
@@ -8,12 +8,12 @@
import * as t from 'io-ts';
-import { NonEmptyString } from '../../non_empty_string';
-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';
+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(
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 0000000000000..d2107ae864f15
--- /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 0000000000000..f86986fc328c5
--- /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-utils/src/list_types/create_comment/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.test.ts
index 1ac605e232ea1..3baf0054221db 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.test.ts
@@ -17,7 +17,7 @@ import {
CreateCommentsArrayOrUndefined,
createCommentsArrayOrUndefined,
} from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('CreateComment', () => {
describe('createComment', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.ts
similarity index 93%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.ts
index 438f946e796d6..883675ce51f91 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/create_comment/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/create_comment/index.ts
@@ -7,7 +7,7 @@
*/
import * as t from 'io-ts';
-import { NonEmptyString } from '../../non_empty_string';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
export const createComment = t.exact(
t.type({
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-utils/src/list_types/default_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_comments_array/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/default_comments_array/index.test.ts
index 5e667380e2adf..440c601876682 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_comments_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/default_comments_array/index.test.ts
@@ -10,7 +10,7 @@ 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 '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getCommentsArrayMock } from '../comment/index.mock';
describe('default_comments_array', () => {
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-utils/src/list_types/default_create_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_create_comments_array/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/default_create_comments_array/index.test.ts
index a4581fabbf6a9..de45fd9f300fa 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_create_comments_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/default_create_comments_array/index.test.ts
@@ -8,7 +8,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
-import { foldLeftRight, getPaths } from '../../test_utils';
+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';
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-utils/src/list_types/default_namespace/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace/index.test.ts
similarity index 96%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/default_namespace/index.test.ts
index 1decca0de6c50..21e8c375b3d01 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultNamespace } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_namespace', () => {
test('it should validate "single"', () => {
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-utils/src/list_types/default_namespace_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace_array/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/default_namespace_array/index.test.ts
index 8bc7a16b96097..b02a3b96a5a3d 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_namespace_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/default_namespace_array/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultNamespaceArray, DefaultNamespaceArrayType } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+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', () => {
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-utils/src/list_types/default_update_comments_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/default_update_comments_array/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/default_update_comments_array/index.test.ts
index f52baa49530ec..fa6613538b18e 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/default_update_comments_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/default_update_comments_array/index.test.ts
@@ -10,7 +10,7 @@ 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 '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getUpdateCommentsArrayMock } from '../update_comment/index.mock';
describe('default_update_comments_array', () => {
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-utils/src/list_types/endpoint/entries/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entries/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entries/index.test.ts
index f5cb89ee79607..09f1740567bc1 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entries/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entries/index.test.ts
@@ -14,7 +14,7 @@ import {
nonEmptyEndpointEntriesArray,
NonEmptyEndpointEntriesArray,
} from '.';
-import { foldLeftRight, getPaths } from '../../../test_utils';
+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';
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-utils/src/list_types/endpoint/entry_match/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.mock.ts
similarity index 86%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.mock.ts
index 7104406c4869c..17a1a083d73d8 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { EndpointEntryMatch } from '.';
-import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../../../constants/index.mock';
+import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../../constants/index.mock';
export const getEndpointEntryMatchMock = (): EndpointEntryMatch => ({
field: FIELD,
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.test.ts
index cc0423fc119c7..fc3a2dded177d 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.test.ts
@@ -10,7 +10,7 @@ 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 '../../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getEntryMatchMock } from '../../entry_match/index.mock';
describe('endpointEntryMatch', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.ts
similarity index 84%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.ts
index 83e2a0f61bb4a..07a1fc58a3d54 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match/index.ts
@@ -7,8 +7,7 @@
*/
import * as t from 'io-ts';
-import { operatorIncluded } from '../../../operator';
-import { NonEmptyString } from '../../../non_empty_string';
+import { NonEmptyString, operatorIncluded } from '@kbn/securitysolution-io-ts-types';
export const endpointEntryMatch = t.exact(
t.type({
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.mock.ts
similarity index 86%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.mock.ts
index 95bd6008f1d7c..13fb16d73457d 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.mock.ts
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../../../constants/index.mock';
+import { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../../constants/index.mock';
import { EndpointEntryMatchAny } from '.';
export const getEndpointEntryMatchAnyMock = (): EndpointEntryMatchAny => ({
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.test.ts
index 0fd878986d5a2..cf64647772519 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.test.ts
@@ -10,7 +10,7 @@ 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 '../../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getEntryMatchAnyMock } from '../../entry_match_any/index.mock';
describe('endpointEntryMatchAny', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.ts
similarity index 76%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.ts
index b39a428bb49dd..23c15767a511c 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_any/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_any/index.ts
@@ -7,9 +7,11 @@
*/
import * as t from 'io-ts';
-import { nonEmptyOrNullableStringArray } from '../../../non_empty_or_nullable_string_array';
-import { operatorIncluded } from '../../../operator';
-import { NonEmptyString } from '../../../non_empty_string';
+import {
+ NonEmptyString,
+ nonEmptyOrNullableStringArray,
+ operatorIncluded,
+} from '@kbn/securitysolution-io-ts-types';
export const endpointEntryMatchAny = t.exact(
t.type({
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_wildcard/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_wildcard/index.ts
similarity index 85%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_wildcard/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_wildcard/index.ts
index b66c5a2588eef..2697f3edc3db4 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_match_wildcard/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_match_wildcard/index.ts
@@ -7,8 +7,7 @@
*/
import * as t from 'io-ts';
-import { operatorIncluded } from '../../../operator';
-import { NonEmptyString } from '../../../non_empty_string';
+import { NonEmptyString, operatorIncluded } from '@kbn/securitysolution-io-ts-types';
export const endpointEntryMatchWildcard = t.exact(
t.type({
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.mock.ts
similarity index 92%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.mock.ts
index f59e29c8ce526..31d983ba58fe3 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { EndpointEntryNested } from '.';
-import { FIELD, NESTED } from '../../../constants/index.mock';
+import { FIELD, NESTED } from '../../constants/index.mock';
import { getEndpointEntryMatchMock } from '../entry_match/index.mock';
import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock';
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.test.ts
index 03c02f67b71ad..f8e54e4956527 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { EndpointEntryNested, endpointEntryNested } from '.';
-import { foldLeftRight, getPaths } from '../../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getEndpointEntryNestedMock } from './index.mock';
import { getEndpointEntryMatchAnyMock } from '../entry_match_any/index.mock';
import {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.ts
similarity index 91%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.ts
index 249dcc9077b34..bd4c90d851a90 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/entry_nested/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/entry_nested/index.ts
@@ -7,7 +7,7 @@
*/
import * as t from 'io-ts';
-import { NonEmptyString } from '../../../non_empty_string';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
import { nonEmptyEndpointNestedEntriesArray } from '../non_empty_nested_entries_array';
export const endpointEntryNested = t.exact(
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/non_empty_nested_entries_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/endpoint/non_empty_nested_entries_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/endpoint/non_empty_nested_entries_array/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/endpoint/non_empty_nested_entries_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entries/index.mock.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entries/index.mock.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entries/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entries/index.test.ts
index b6e448f94ce6a..f68fea35e6fdf 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entries/index.test.ts
@@ -10,7 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { getEntryMatchMock } from '../entry_match/index.mock';
import { entriesArray, entriesArrayOrUndefined, entry } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getEntryMatchAnyMock } from '../entry_match_any/index.mock';
import { getEntryExistsMock } from '../entries_exist/index.mock';
import { getEntryListMock } from '../entries_list/index.mock';
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entries/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entries/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entries/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entries_exist/index.mock.ts
similarity index 89%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entries_exist/index.mock.ts
index 0882883f4d239..ad2164a3862eb 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entries_exist/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { EntryExists } from '.';
-import { EXISTS, FIELD, OPERATOR } from '../../constants/index.mock';
+import { EXISTS, FIELD, OPERATOR } from '../constants/index.mock';
export const getEntryExistsMock = (): EntryExists => ({
field: FIELD,
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entries_exist/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entries_exist/index.test.ts
index db4edb54dfc29..05451b11de7a6 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entries_exist/index.test.ts
@@ -10,7 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { getEntryExistsMock } from './index.mock';
import { entriesExists, EntryExists } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('entriesExists', () => {
test('it should validate an entry', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entries_exist/index.ts
similarity index 90%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entries_exist/index.ts
index f8f1ddecc9ff9..6d65d458583bd 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_exist/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entries_exist/index.ts
@@ -8,8 +8,8 @@
import * as t from 'io-ts';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
import { listOperator as operator } from '../list_operator';
-import { NonEmptyString } from '../../non_empty_string';
export const entriesExists = t.exact(
t.type({
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entries_list/index.mock.ts
similarity index 86%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entries_list/index.mock.ts
index c4afb28f5ac54..2349b9d5ab2b3 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entries_list/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { EntryList } from '.';
-import { FIELD, LIST, LIST_ID, OPERATOR, TYPE } from '../../constants/index.mock';
+import { FIELD, LIST, LIST_ID, OPERATOR, TYPE } from '../constants/index.mock';
export const getEntryListMock = (): EntryList => ({
field: FIELD,
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entries_list/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entries_list/index.test.ts
index 2be3803c356de..5b72242777875 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entries_list/index.test.ts
@@ -11,7 +11,7 @@ import { left } from 'fp-ts/lib/Either';
import { getEntryListMock } from './index.mock';
import { entriesList, EntryList } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('entriesList', () => {
test('it should validate an entry', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entries_list/index.ts
similarity index 91%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entries_list/index.ts
index b386ca35d2bbb..61d3c7b156fd2 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entries_list/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entries_list/index.ts
@@ -7,7 +7,7 @@
*/
import * as t from 'io-ts';
-import { NonEmptyString } from '../../non_empty_string';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
import { type } from '../type';
import { listOperator as operator } from '../list_operator';
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match/index.mock.ts
similarity index 88%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_match/index.mock.ts
index 4fdd8d915fe04..38c9f0f922c46 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { EntryMatch } from '.';
-import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../../constants/index.mock';
+import { ENTRY_VALUE, FIELD, MATCH, OPERATOR } from '../constants/index.mock';
export const getEntryMatchMock = (): EntryMatch => ({
field: FIELD,
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_match/index.test.ts
index 744c74c1223df..bff65ad7f6bec 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match/index.test.ts
@@ -10,7 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { getEntryMatchMock } from './index.mock';
import { entriesMatch, EntryMatch } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('entriesMatch', () => {
test('it should validate an entry', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match/index.ts
similarity index 90%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_match/index.ts
index cab6d0dd4a07f..4f04e01cf8f63 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match/index.ts
@@ -7,7 +7,7 @@
*/
import * as t from 'io-ts';
-import { NonEmptyString } from '../../non_empty_string';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
import { listOperator as operator } from '../list_operator';
export const entriesMatch = t.exact(
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_any/index.mock.ts
similarity index 89%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_match_any/index.mock.ts
index 0022b00c604b0..efaf23fe1e784 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_any/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { EntryMatchAny } from '.';
-import { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../../constants/index.mock';
+import { ENTRY_VALUE, FIELD, MATCH_ANY, OPERATOR } from '../constants/index.mock';
export const getEntryMatchAnyMock = (): EntryMatchAny => ({
field: FIELD,
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_any/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_match_any/index.test.ts
index 60fc4cdc26005..c0eb017fdab54 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_any/index.test.ts
@@ -10,7 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { getEntryMatchAnyMock } from './index.mock';
import { entriesMatchAny, EntryMatchAny } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('entriesMatchAny', () => {
test('it should validate an entry', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_any/index.ts
similarity index 82%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_match_any/index.ts
index 0add9a610f30b..86e97c579a02c 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_any/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_any/index.ts
@@ -8,9 +8,8 @@
import * as t from 'io-ts';
+import { NonEmptyString, nonEmptyOrNullableStringArray } from '@kbn/securitysolution-io-ts-types';
import { listOperator as operator } from '../list_operator';
-import { nonEmptyOrNullableStringArray } from '../../non_empty_or_nullable_string_array';
-import { NonEmptyString } from '../../non_empty_string';
export const entriesMatchAny = t.exact(
t.type({
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_wildcard/index.mock.ts
similarity index 89%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_match_wildcard/index.mock.ts
index 9810fe5e9875b..f81a8c6cba2ef 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_wildcard/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { EntryMatchWildcard } from '.';
-import { ENTRY_VALUE, FIELD, OPERATOR, WILDCARD } from '../../constants/index.mock';
+import { ENTRY_VALUE, FIELD, OPERATOR, WILDCARD } from '../constants/index.mock';
export const getEntryMatchWildcardMock = (): EntryMatchWildcard => ({
field: FIELD,
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_wildcard/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_match_wildcard/index.test.ts
index d9170dd60ab40..8a5a152ce7e65 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_wildcard/index.test.ts
@@ -10,7 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { getEntryMatchWildcardMock } from './index.mock';
import { entriesMatchWildcard, EntryMatchWildcard } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('entriesMatchWildcard', () => {
test('it should validate an entry', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_wildcard/index.ts
similarity index 91%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_match_wildcard/index.ts
index aab5ba5e8e32c..ea1953b983d45 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_match_wildcard/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_match_wildcard/index.ts
@@ -7,7 +7,7 @@
*/
import * as t from 'io-ts';
-import { NonEmptyString } from '../../non_empty_string';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
import { listOperator as operator } from '../list_operator';
export const entriesMatchWildcard = t.exact(
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_nested/index.mock.ts
similarity index 94%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_nested/index.mock.ts
index acde4443cccb7..05f42cdf69bc0 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_nested/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { EntryNested } from '.';
-import { NESTED, NESTED_FIELD } from '../../constants/index.mock';
+import { NESTED, NESTED_FIELD } from '../constants/index.mock';
import { getEntryExistsMock } from '../entries_exist/index.mock';
import { getEntryMatchExcludeMock, getEntryMatchMock } from '../entry_match/index.mock';
import { getEntryMatchAnyExcludeMock, getEntryMatchAnyMock } from '../entry_match_any/index.mock';
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_nested/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_nested/index.test.ts
index b6bbc4dbef4a3..b21737535fd77 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_nested/index.test.ts
@@ -10,7 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { getEntryNestedMock } from './index.mock';
import { entriesNested, EntryNested } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getEntryMatchAnyMock } from '../entry_match_any/index.mock';
import { getEntryExistsMock } from '../entries_exist/index.mock';
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/entry_nested/index.ts
similarity index 90%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/entry_nested/index.ts
index ff224dd836a19..f5ac68cc98702 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/entry_nested/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/entry_nested/index.ts
@@ -7,7 +7,7 @@
*/
import * as t from 'io-ts';
-import { NonEmptyString } from '../../non_empty_string';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
import { nonEmptyNestedEntriesArray } from '../non_empty_nested_entries_array';
export const entriesNested = t.exact(
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/exception_list/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/exception_list/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list_item_type/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/exception_list_item_type/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/exception_list_item_type/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/exception_list_item_type/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/id/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/id/index.ts
similarity index 89%
rename from packages/kbn-securitysolution-io-ts-utils/src/id/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/id/index.ts
index 7b187d7730f73..5952bd2eda21f 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/id/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/id/index.ts
@@ -7,7 +7,7 @@
*/
import * as t from 'io-ts';
-import { NonEmptyString } from '../non_empty_string';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
export const id = NonEmptyString;
export type Id = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/index.ts
similarity index 82%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/index.ts
index 9dd58e2a5a177..3c60df315e430 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/index.ts
@@ -5,13 +5,18 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
+
export * from './comment';
+export * from './constants';
export * from './create_comment';
+export * from './created_at';
+export * from './created_by';
export * from './default_comments_array';
export * from './default_create_comments_array';
export * from './default_namespace';
export * from './default_namespace_array';
export * from './default_update_comments_array';
+export * from './description';
export * from './endpoint';
export * from './entries';
export * from './entries_exist';
@@ -22,12 +27,18 @@ export * from './entry_match_wildcard';
export * from './entry_nested';
export * from './exception_list';
export * from './exception_list_item_type';
+export * from './id';
export * from './item_id';
+export * from './list_operator';
export * from './lists';
export * from './lists_default_array';
+export * from './meta';
+export * from './name';
export * from './non_empty_entries_array';
export * from './non_empty_nested_entries_array';
-export * from './list_operator';
export * from './os_type';
+export * from './tags';
export * from './type';
export * from './update_comment';
+export * from './updated_at';
+export * from './updated_by';
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/item_id/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/item_id/index.ts
similarity index 90%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/item_id/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/item_id/index.ts
index 171db8fd60fd1..dcb03884eadab 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/item_id/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/item_id/index.ts
@@ -9,7 +9,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import * as t from 'io-ts';
-import { NonEmptyString } from '../../non_empty_string';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
export const item_id = NonEmptyString;
export type ItemId = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/list_operator/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/list_operator/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/list_operator/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/list_operator/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/lists/index.mock.ts
similarity index 93%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/lists/index.mock.ts
index c6f54b57d937b..e9f34c4cf789f 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/lists/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { List, ListArray } from '.';
-import { ENDPOINT_LIST_ID } from '../../constants';
+import { ENDPOINT_LIST_ID } from '../constants';
export const getListMock = (): List => ({
id: 'some_uuid',
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/lists/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/lists/index.test.ts
index 77d5e72ef8bc8..88dcc1ced8607 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/lists/index.test.ts
@@ -10,7 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { getEndpointListMock, getListArrayMock, getListMock } from './index.mock';
import { List, list, ListArray, listArray, ListArrayOrUndefined, listArrayOrUndefined } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('Lists', () => {
describe('list', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/lists/index.ts
similarity index 93%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/lists/index.ts
index 1bd1806564856..7881a6bb3322e 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/lists/index.ts
@@ -7,9 +7,9 @@
*/
import * as t from 'io-ts';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
import { exceptionListType } from '../exception_list';
import { namespaceType } from '../default_namespace';
-import { NonEmptyString } from '../../non_empty_string';
export const list = t.exact(
t.type({
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/lists_default_array/index.test.ts
similarity index 96%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/lists_default_array/index.test.ts
index 03d16d8e1b5ca..58a52d26aa34f 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/lists_default_array/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultListArray } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getListArrayMock } from '../lists/index.mock';
describe('lists_default_array', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/lists_default_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/lists_default_array/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/lists_default_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/meta/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/meta/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/meta/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/meta/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/name/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/name/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/name/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/name/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/non_empty_entries_array/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/non_empty_entries_array/index.test.ts
index 11e6e54b344a9..98976f3cd6d21 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/non_empty_entries_array/index.test.ts
@@ -10,7 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { EntriesArray } from '../entries';
import { nonEmptyEntriesArray } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getEntryMatchMock } from '../entry_match/index.mock';
import { getEntryMatchAnyMock } from '../entry_match_any/index.mock';
import { getEntryExistsMock } from '../entries_exist/index.mock';
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/non_empty_entries_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_entries_array/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/non_empty_entries_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/non_empty_nested_entries_array/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/non_empty_nested_entries_array/index.test.ts
index 95b74a6d4fe43..8ac958577f8d7 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/non_empty_nested_entries_array/index.test.ts
@@ -10,7 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { EntriesArray } from '../entries';
import { nonEmptyNestedEntriesArray } from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getEntryMatchMock } from '../entry_match/index.mock';
import { getEntryMatchAnyMock } from '../entry_match_any/index.mock';
import { getEntryExistsMock } from '../entries_exist/index.mock';
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/non_empty_nested_entries_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/non_empty_nested_entries_array/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/non_empty_nested_entries_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/os_type/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/os_type/index.ts
similarity index 92%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/os_type/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/os_type/index.ts
index 5ff60e05817d5..b7fa544c956ee 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/os_type/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/os_type/index.ts
@@ -7,7 +7,7 @@
*/
import * as t from 'io-ts';
-import { DefaultArray } from '../../default_array';
+import { DefaultArray } from '@kbn/securitysolution-io-ts-types';
export const osType = t.keyof({
linux: null,
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/tags/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/tags/index.ts
similarity index 89%
rename from packages/kbn-securitysolution-io-ts-utils/src/tags/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/tags/index.ts
index 48bcca0551352..f0f23d9e4717d 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/tags/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/tags/index.ts
@@ -8,7 +8,7 @@
import * as t from 'io-ts';
-import { DefaultStringArray } from '../default_string_array';
+import { DefaultStringArray } from '@kbn/securitysolution-io-ts-types';
export const tags = DefaultStringArray;
export type Tags = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/type/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/type/index.ts
similarity index 94%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/type/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/type/index.ts
index 90a8c36eb8b31..50cacb8e0259b 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/type/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/type/index.ts
@@ -40,3 +40,4 @@ export const type = t.keyof({
export const typeOrUndefined = t.union([type, t.undefined]);
export type Type = t.TypeOf;
+export type TypeOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.mock.ts b/packages/kbn-securitysolution-io-ts-list-types/src/update_comment/index.mock.ts
similarity index 92%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.mock.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/update_comment/index.mock.ts
index 3b5cb256b28bf..e9a56119dcc20 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.mock.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/update_comment/index.mock.ts
@@ -7,7 +7,7 @@
*/
import { UpdateComment, UpdateCommentsArray } from '.';
-import { ID } from '../../constants/index.mock';
+import { ID } from '../constants/index.mock';
export const getUpdateCommentMock = (): UpdateComment => ({
comment: 'some comment',
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.test.ts b/packages/kbn-securitysolution-io-ts-list-types/src/update_comment/index.test.ts
similarity index 98%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.test.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/update_comment/index.test.ts
index a6fc285f05465..8dd0301c54dd8 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/update_comment/index.test.ts
@@ -17,7 +17,7 @@ import {
UpdateCommentsArrayOrUndefined,
updateCommentsArrayOrUndefined,
} from '.';
-import { foldLeftRight, getPaths } from '../../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('CommentsUpdate', () => {
describe('updateComment', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/update_comment/index.ts
similarity index 90%
rename from packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/update_comment/index.ts
index 496ff07c5616f..5499690c97716 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/list_types/update_comment/index.ts
+++ b/packages/kbn-securitysolution-io-ts-list-types/src/update_comment/index.ts
@@ -7,8 +7,8 @@
*/
import * as t from 'io-ts';
-import { NonEmptyString } from '../../non_empty_string';
-import { id } from '../../id';
+import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
+import { id } from '../id';
export const updateComment = t.intersection([
t.exact(
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/updated_at/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/updated_at/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/updated_at/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/updated_at/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/updated_by/index.ts b/packages/kbn-securitysolution-io-ts-list-types/src/updated_by/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/updated_by/index.ts
rename to packages/kbn-securitysolution-io-ts-list-types/src/updated_by/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json b/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json
new file mode 100644
index 0000000000000..d926653a4230b
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-list-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-list-types/src",
+ "types": [
+ "jest",
+ "node"
+ ]
+ },
+ "include": [
+ "src/**/*"
+ ]
+}
diff --git a/packages/kbn-securitysolution-io-ts-types/BUILD.bazel b/packages/kbn-securitysolution-io-ts-types/BUILD.bazel
new file mode 100644
index 0000000000000..0a21f5ed94f01
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-types/BUILD.bazel
@@ -0,0 +1,93 @@
+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-types"
+PKG_REQUIRE_NAME = "@kbn/securitysolution-io-ts-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-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-types/README.md b/packages/kbn-securitysolution-io-ts-types/README.md
new file mode 100644
index 0000000000000..552c663d819e3
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-types/README.md
@@ -0,0 +1,8 @@
+# kbn-securitysolution-io-ts-types
+
+Generic io-ts types that are not specific to any particular domain for use with other packages or across different plugins/domains
+
+Related packages are:
+* kbn-securitysolution-io-ts-utils
+* kbn-securitysolution-io-ts-list-types
+* kbn-securitysolution-io-ts-alerting-types
\ No newline at end of file
diff --git a/packages/kbn-securitysolution-io-ts-types/jest.config.js b/packages/kbn-securitysolution-io-ts-types/jest.config.js
new file mode 100644
index 0000000000000..18d31eaa75219
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-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-types'],
+};
diff --git a/packages/kbn-securitysolution-io-ts-types/package.json b/packages/kbn-securitysolution-io-ts-types/package.json
new file mode 100644
index 0000000000000..0381a6d24a136
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-types/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@kbn/securitysolution-io-ts-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/default_array/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/default_array/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_array/index.test.ts
index 82fa884b1c577..4ca45e7de3377 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/default_array/index.test.ts
@@ -11,7 +11,7 @@ import * as t from 'io-ts';
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultArray } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
const testSchema = t.keyof({
valid: true,
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_array/index.ts b/packages/kbn-securitysolution-io-ts-types/src/default_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_array/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.test.ts
index bddf9cc0747ea..c87a67ec4e5d4 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultBooleanFalse } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_boolean_false', () => {
test('it should validate a boolean false', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.ts b/packages/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_boolean_false/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_boolean_false/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.test.ts
index a05fb586c2e92..3ec33fda392e4 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultBooleanTrue } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_boolean_true', () => {
test('it should validate a boolean false', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.ts b/packages/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_boolean_true/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_boolean_true/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/default_empty_string/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_empty_string/index.test.ts
index 5bdc9b298649e..02fb74510d604 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/default_empty_string/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultEmptyString } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_empty_string', () => {
test('it should validate a regular string', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.ts b/packages/kbn-securitysolution-io-ts-types/src/default_empty_string/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_empty_string/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_empty_string/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/default_string_array/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_string_array/index.test.ts
index c7137d9c56b0d..7b1f217f55ad5 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/default_string_array/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultStringArray } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_string_array', () => {
test('it should validate an empty array', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.ts b/packages/kbn-securitysolution-io-ts-types/src/default_string_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_string_array/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_string_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.test.ts
index 2443e8f71fecd..3e96c942de74a 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultStringBooleanFalse } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_string_boolean_false', () => {
test('it should validate a boolean false', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.ts b/packages/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_string_boolean_false/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_string_boolean_false/index.ts
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.test.ts b/packages/kbn-securitysolution-io-ts-types/src/default_uuid/index.test.ts
similarity index 80%
rename from x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_uuid/index.test.ts
index e2b2ac5015967..c471141a99a76 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/default_uuid/index.test.ts
@@ -1,14 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
+ * 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 { DefaultUuid } from './default_uuid';
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
-import { foldLeftRight, getPaths } from '../../../test_utils';
+import { DefaultUuid } from '.';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_uuid', () => {
test('it should validate a regular string', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.ts b/packages/kbn-securitysolution-io-ts-types/src/default_uuid/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/default_uuid/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_uuid/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/default_version_number/index.test.ts
similarity index 96%
rename from packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_version_number/index.test.ts
index f77903d2d030d..fd7b12123b6bb 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/default_version_number/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { DefaultVersionNumber } from '../default_version_number';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('default_version_number', () => {
test('it should validate a version number', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.ts b/packages/kbn-securitysolution-io-ts-types/src/default_version_number/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/deafult_version_number/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/default_version_number/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/empty_string_array/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/empty_string_array/index.test.ts
index 86ffba6eeb60a..5b7863947cad4 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/empty_string_array/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { EmptyStringArray, EmptyStringArrayEncoded } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('empty_string_array', () => {
test('it should validate "null" and create an empty array', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.ts b/packages/kbn-securitysolution-io-ts-types/src/empty_string_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/empty_string_array/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/empty_string_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-types/src/index.ts b/packages/kbn-securitysolution-io-ts-types/src/index.ts
new file mode 100644
index 0000000000000..fc0f017016e9f
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-types/src/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.
+ */
+
+export * from './default_array';
+export * from './default_boolean_false';
+export * from './default_boolean_true';
+export * from './default_empty_string';
+export * from './default_string_array';
+export * from './default_string_boolean_false';
+export * from './default_uuid';
+export * from './default_version_number';
+export * from './empty_string_array';
+export * from './iso_date_string';
+export * from './non_empty_array';
+export * from './non_empty_or_nullable_string_array';
+export * from './non_empty_string';
+export * from './non_empty_string_array';
+export * from './operator';
+export * from './only_false_allowed';
+export * from './parse_schedule_dates';
+export * from './positive_integer';
+export * from './positive_integer_greater_than_zero';
+export * from './string_to_positive_number';
+export * from './uuid';
+export * from './version';
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/iso_date_string/index.test.ts
similarity index 96%
rename from packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/iso_date_string/index.test.ts
index 4b73ed1b136dc..e70a738d7336e 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/iso_date_string/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { IsoDateString } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('ios_date_string', () => {
test('it should validate a iso string', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.ts b/packages/kbn-securitysolution-io-ts-types/src/iso_date_string/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/iso_date_string/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/iso_date_string/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/non_empty_array/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/non_empty_array/index.test.ts
index 0ea7eb5539ba9..0586195360142 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/non_empty_array/index.test.ts
@@ -11,7 +11,7 @@ import * as t from 'io-ts';
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { NonEmptyArray } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
const testSchema = t.keyof({
valid: true,
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.ts b/packages/kbn-securitysolution-io-ts-types/src/non_empty_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/non_empty_array/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/non_empty_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.test.ts
index fb2e91862d91e..355bd9d20061e 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { nonEmptyOrNullableStringArray } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('nonEmptyOrNullableStringArray', () => {
test('it should FAIL validation when given an empty array', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.ts b/packages/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/non_empty_or_nullable_string_array/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/non_empty_or_nullable_string_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/non_empty_string/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/non_empty_string/index.test.ts
index 15c8ced8c915f..ae3b8cd9acad5 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/non_empty_string/index.test.ts
@@ -8,7 +8,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { NonEmptyString } from '.';
describe('non_empty_string', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.ts b/packages/kbn-securitysolution-io-ts-types/src/non_empty_string/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/non_empty_string/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/non_empty_string/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string_array/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.test.ts
similarity index 97%
rename from packages/kbn-securitysolution-io-ts-utils/src/non_empty_string_array/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.test.ts
index 9fec36f46dd27..f56fa7faed2a4 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string_array/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.test.ts
@@ -8,7 +8,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { NonEmptyStringArray } from '.';
describe('non_empty_string_array', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/non_empty_string_array/index.ts b/packages/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/non_empty_string_array/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/non_empty_string_array/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.test.ts
index 7f06ec2153a50..de05872c0dc31 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { OnlyFalseAllowed } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('only_false_allowed', () => {
test('it should validate a boolean false as false', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.ts b/packages/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/only_false_allowed/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/only_false_allowed/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/operator/index.ts b/packages/kbn-securitysolution-io-ts-types/src/operator/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/operator/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/operator/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts b/packages/kbn-securitysolution-io-ts-types/src/parse_schedule_dates/index.ts
similarity index 85%
rename from packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/parse_schedule_dates/index.ts
index a2cc15d82391c..d6a99b5fbf880 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/parse_schedule_dates/index.ts
@@ -9,10 +9,6 @@
import moment from 'moment';
import dateMath from '@elastic/datemath';
-/**
- * TODO: Move this to kbn-securitysolution-utils
- * @deprecated Use the parseScheduleDates from the kbn-securitysolution-utils.
- */
export const parseScheduleDates = (time: string): moment.Moment | null => {
const isValidDateString = !isNaN(Date.parse(time));
const isValidInput = isValidDateString || time.trim().startsWith('now');
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/positive_integer/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/positive_integer/index.test.ts
index c6c841b746089..deea8951a3d39 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/positive_integer/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { PositiveInteger } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('positive_integer_greater_than_zero', () => {
test('it should validate a positive number', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.ts b/packages/kbn-securitysolution-io-ts-types/src/positive_integer/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/positive_integer/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/positive_integer/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.test.ts
similarity index 96%
rename from packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.test.ts
index 4655207a6448e..4ea6fe920cf14 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { PositiveIntegerGreaterThanZero } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('positive_integer_greater_than_zero', () => {
test('it should validate a positive number', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.ts b/packages/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/positive_integer_greater_than_zero/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/positive_integer_greater_than_zero/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/string_to_positive_number/index.ts b/packages/kbn-securitysolution-io-ts-types/src/string_to_positive_number/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/string_to_positive_number/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/string_to_positive_number/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/uuid/index.test.ts b/packages/kbn-securitysolution-io-ts-types/src/uuid/index.test.ts
similarity index 95%
rename from packages/kbn-securitysolution-io-ts-utils/src/uuid/index.test.ts
rename to packages/kbn-securitysolution-io-ts-types/src/uuid/index.test.ts
index e8214ac60313f..4333fab102d44 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/uuid/index.test.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/uuid/index.test.ts
@@ -9,7 +9,7 @@
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { UUID } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
+import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('uuid', () => {
test('it should validate a uuid', () => {
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/uuid/index.ts b/packages/kbn-securitysolution-io-ts-types/src/uuid/index.ts
similarity index 100%
rename from packages/kbn-securitysolution-io-ts-utils/src/uuid/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/uuid/index.ts
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/version/index.ts b/packages/kbn-securitysolution-io-ts-types/src/version/index.ts
similarity index 86%
rename from packages/kbn-securitysolution-io-ts-utils/src/version/index.ts
rename to packages/kbn-securitysolution-io-ts-types/src/version/index.ts
index 38cb47ebce53e..245b64781a7f8 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/version/index.ts
+++ b/packages/kbn-securitysolution-io-ts-types/src/version/index.ts
@@ -16,3 +16,6 @@ import { PositiveIntegerGreaterThanZero } from '../positive_integer_greater_than
*/
export const version = PositiveIntegerGreaterThanZero;
export type Version = t.TypeOf;
+
+export const versionOrUndefined = t.union([version, t.undefined]);
+export type VersionOrUndefined = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-types/tsconfig.json b/packages/kbn-securitysolution-io-ts-types/tsconfig.json
new file mode 100644
index 0000000000000..42a059439ecb5
--- /dev/null
+++ b/packages/kbn-securitysolution-io-ts-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-types/src",
+ "types": [
+ "jest",
+ "node"
+ ]
+ },
+ "include": [
+ "src/**/*"
+ ]
+}
diff --git a/packages/kbn-securitysolution-io-ts-utils/README.md b/packages/kbn-securitysolution-io-ts-utils/README.md
index 908651b50b80a..146f965391aa0 100644
--- a/packages/kbn-securitysolution-io-ts-utils/README.md
+++ b/packages/kbn-securitysolution-io-ts-utils/README.md
@@ -1,10 +1,12 @@
# kbn-securitysolution-io-ts-utils
-Temporary location for all the io-ts-utils from security solutions. This is a lift-and-shift, where
-we are moving them here for phase 1.
+Very small set of utilities for io-ts which we use across plugins within security solutions such as securitysolution, lists, cases, etc...
+This folder should remain small and concise since it is pulled into front end and the more files we add the more weight will be added to all
+of the plugins. Also, any new dependencies added to this will add weight here and the other plugins, so be careful of what is added here.
-Phase 2 is deprecating across plugins any copied code or sharing of io-ts utils that are now in here.
+You might consider making another package instead and putting a dependency on this one if needed, instead.
-Phase 3 is replacing those deprecated types with the ones in here.
-
-Phase 4+ is (potentially) consolidating any duplication or everything altogether with the `kbn-io-ts-utils` project
\ No newline at end of file
+Related packages are
+* kbn-securitysolution-io-ts-alerting-types
+* 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-utils/src/default_version_number/index.test.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.test.ts
deleted file mode 100644
index b9e9a3ff367e4..0000000000000
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.test.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 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 { DefaultVersionNumber } from '.';
-import { foldLeftRight, getPaths } from '../test_utils';
-
-describe('default_version_number', () => {
- test('it should validate a version number', () => {
- const payload = 5;
- const decoded = DefaultVersionNumber.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([]);
- expect(message.schema).toEqual(payload);
- });
-
- test('it should not validate a 0', () => {
- const payload = 0;
- const decoded = DefaultVersionNumber.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "0" supplied to "DefaultVersionNumber"',
- ]);
- expect(message.schema).toEqual({});
- });
-
- test('it should not validate a -1', () => {
- const payload = -1;
- const decoded = DefaultVersionNumber.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "-1" supplied to "DefaultVersionNumber"',
- ]);
- expect(message.schema).toEqual({});
- });
-
- test('it should not validate a string', () => {
- const payload = '5';
- const decoded = DefaultVersionNumber.decode(payload);
- const message = pipe(decoded, foldLeftRight);
-
- expect(getPaths(left(message.errors))).toEqual([
- 'Invalid value "5" supplied to "DefaultVersionNumber"',
- ]);
- expect(message.schema).toEqual({});
- });
-
- test('it should return a default of 1', () => {
- const payload = null;
- const decoded = DefaultVersionNumber.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-utils/src/default_version_number/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.ts
deleted file mode 100644
index 245ff9d0db7dd..0000000000000
--- a/packages/kbn-securitysolution-io-ts-utils/src/default_version_number/index.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the 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 { version, Version } from '../version';
-
-/**
- * Types the DefaultVersionNumber as:
- * - If null or undefined, then a default of the number 1 will be used
- */
-export const DefaultVersionNumber = new t.Type(
- 'DefaultVersionNumber',
- version.is,
- (input, context): Either =>
- input == null ? t.success(1) : version.validate(input, context),
- t.identity
-);
-
-export type DefaultVersionNumberDecoded = t.TypeOf;
diff --git a/packages/kbn-securitysolution-io-ts-utils/src/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/index.ts
index 1a18293393af5..c21096e497134 100644
--- a/packages/kbn-securitysolution-io-ts-utils/src/index.ts
+++ b/packages/kbn-securitysolution-io-ts-utils/src/index.ts
@@ -7,71 +7,7 @@
*/
export * from './format_errors';
-export * from './actions';
-export * from './constants';
-export * from './created_at';
-export * from './created_by';
-export * from './default_version_number';
-export * from './default_actions_array';
-export * from './default_array';
-export * from './default_boolean_false';
-export * from './default_boolean_true';
-export * from './default_empty_string';
-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_string_array';
-export * from './default_string_boolean_false';
-export * from './default_threat_array';
-export * from './default_throttle_null';
-export * from './default_to_string';
-export * from './default_uuid';
-export * from './default_version_number';
-export * from './description';
-export * from './empty_string_array';
export * from './exact_check';
export * from './format_errors';
-export * from './from';
-export * from './id';
-export * from './iso_date_string';
-export * from './language';
-export * from './list_types';
-export * from './max_signals';
-export * from './meta';
-export * from './name';
-export * from './non_empty_array';
-export * from './non_empty_or_nullable_string_array';
-export * from './non_empty_string';
-export * from './non_empty_string_array';
-export * from './normalized_ml_job_id';
-export * from './only_false_allowed';
-export * from './operator';
-export * from './parse_schedule_dates';
-export * from './positive_integer';
-export * from './positive_integer_greater_than_zero';
-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 './string_to_positive_number';
-export * from './tags';
export * from './test_utils';
-export * from './threat';
-export * from './threat_mapping';
-export * from './threat_subtechnique';
-export * from './threat_tactic';
-export * from './threat_technique';
-export * from './throttle';
-export * from './updated_at';
-export * from './updated_by';
-export * from './uuid';
export * from './validate';
-export * from './version';
diff --git a/packages/kbn-server-http-tools/BUILD.bazel b/packages/kbn-server-http-tools/BUILD.bazel
new file mode 100644
index 0000000000000..61570969c85f1
--- /dev/null
+++ b/packages/kbn-server-http-tools/BUILD.bazel
@@ -0,0 +1,90 @@
+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-server-http-tools"
+PKG_REQUIRE_NAME = "@kbn/server-http-tools"
+
+SOURCE_FILES = glob(
+ [
+ "src/**/*.ts",
+ ],
+ exclude = [
+ "**/*.test.*",
+ ],
+)
+
+SRCS = SOURCE_FILES
+
+filegroup(
+ name = "srcs",
+ srcs = SRCS,
+)
+
+NPM_MODULE_EXTRA_FILES = [
+ "package.json",
+ "README.md"
+]
+
+SRC_DEPS = [
+ "//packages/kbn-config-schema",
+ "//packages/kbn-crypto",
+ "@npm//@hapi/hapi",
+ "@npm//@hapi/hoek",
+ "@npm//joi",
+ "@npm//moment",
+ "@npm//uuid",
+]
+
+TYPES_DEPS = [
+ "@npm//@types/hapi__hapi",
+ "@npm//@types/joi",
+ "@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-server-http-tools/package.json b/packages/kbn-server-http-tools/package.json
index c44bf17079aab..7ec52743f027e 100644
--- a/packages/kbn-server-http-tools/package.json
+++ b/packages/kbn-server-http-tools/package.json
@@ -4,13 +4,5 @@
"types": "./target/index.d.ts",
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0",
- "private": true,
- "scripts": {
- "build": "rm -rf target && ../../node_modules/.bin/tsc",
- "kbn:bootstrap": "yarn build",
- "kbn:watch": "yarn build --watch"
- },
- "devDependencies": {
- "@kbn/utility-types": "link:../kbn-utility-types"
- }
+ "private": true
}
diff --git a/packages/kbn-server-http-tools/tsconfig.json b/packages/kbn-server-http-tools/tsconfig.json
index 2f3e4626a04ce..034cbd2334919 100644
--- a/packages/kbn-server-http-tools/tsconfig.json
+++ b/packages/kbn-server-http-tools/tsconfig.json
@@ -1,10 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "incremental": false,
+ "incremental": true,
"outDir": "./target",
"declaration": true,
"declarationMap": true,
+ "rootDir": "src",
"sourceMap": true,
"sourceRoot": "../../../../packages/kbn-server-http-tools/src"
},
diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json
index a668d8c1f8588..275d9fac73c58 100644
--- a/packages/kbn-test/package.json
+++ b/packages/kbn-test/package.json
@@ -3,8 +3,8 @@
"version": "1.0.0",
"private": true,
"license": "SSPL-1.0 OR Elastic License 2.0",
- "main": "./target/index.js",
- "types": "./target/types/index.d.ts",
+ "main": "./target",
+ "types": "./target/types",
"scripts": {
"build": "node scripts/build",
"kbn:bootstrap": "node scripts/build --source-maps",
@@ -14,7 +14,6 @@
"devOnly": true
},
"dependencies": {
- "@kbn/i18n": "link:../kbn-i18n",
"@kbn/optimizer": "link:../kbn-optimizer"
}
}
\ No newline at end of file
diff --git a/packages/kbn-test/src/functional_test_runner/index.ts b/packages/kbn-test/src/functional_test_runner/index.ts
index 80d257f1cfb23..268c6b2bd9a67 100644
--- a/packages/kbn-test/src/functional_test_runner/index.ts
+++ b/packages/kbn-test/src/functional_test_runner/index.ts
@@ -10,3 +10,4 @@ export { FunctionalTestRunner } from './functional_test_runner';
export { readConfigFile, Config } from './lib';
export { runFtrCli } from './cli';
export * from './lib/docker_servers';
+export * from './public_types';
diff --git a/packages/kbn-test/types/ftr.d.ts b/packages/kbn-test/src/functional_test_runner/public_types.ts
similarity index 94%
rename from packages/kbn-test/types/ftr.d.ts
rename to packages/kbn-test/src/functional_test_runner/public_types.ts
index 83f725d86857e..915cb34f6ffe5 100644
--- a/packages/kbn-test/types/ftr.d.ts
+++ b/packages/kbn-test/src/functional_test_runner/public_types.ts
@@ -7,13 +7,9 @@
*/
import { ToolingLog } from '@kbn/dev-utils';
-import {
- Config,
- Lifecycle,
- FailureMetadata,
- DockerServersService,
-} from '../src/functional_test_runner/lib';
-import { Test, Suite } from '../src/functional_test_runner/fake_mocha_types';
+
+import { Config, Lifecycle, FailureMetadata, DockerServersService } from './lib';
+import { Test, Suite } from './fake_mocha_types';
export { Lifecycle, Config, FailureMetadata };
diff --git a/packages/kbn-test/tsconfig.json b/packages/kbn-test/tsconfig.json
index 8536ad7e0c12f..3cb68029d74cf 100644
--- a/packages/kbn-test/tsconfig.json
+++ b/packages/kbn-test/tsconfig.json
@@ -8,19 +8,17 @@
"declaration": true,
"declarationMap": true,
"sourceMap": true,
- "sourceRoot": "../../../../../packages/kbn-test/src",
+ "sourceRoot": "../../../../../../packages/kbn-test/src",
"types": [
"jest",
"node"
],
},
"include": [
- "types/**/*",
"src/**/*",
"index.d.ts"
],
"exclude": [
- "types/ftr_globals/**/*",
"**/__fixtures__/**/*"
]
}
diff --git a/packages/kbn-test/types/README.md b/packages/kbn-test/types/README.md
deleted file mode 100644
index 1298d2a4afc0a..0000000000000
--- a/packages/kbn-test/types/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# @kbn/test/types
-
-Shared types used by different parts of the tests
-
- - **`ftr.d.ts`**: These types are generic types for using the functional test runner. They are here because we plan to move the functional test runner into the `@kbn/test` package at some point and having them here makes them a lot easier to import from all over the place like we do.
\ No newline at end of file
diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json
index 54d983bf1bf44..c284be4487a5f 100644
--- a/packages/kbn-ui-shared-deps/package.json
+++ b/packages/kbn-ui-shared-deps/package.json
@@ -9,7 +9,6 @@
"kbn:watch": "node scripts/build --dev --watch"
},
"dependencies": {
- "@kbn/i18n": "link:../kbn-i18n",
"@kbn/monaco": "link:../kbn-monaco"
}
}
\ No newline at end of file
diff --git a/src/core/public/index.ts b/src/core/public/index.ts
index 17ba37d075b78..7d2a585084758 100644
--- a/src/core/public/index.ts
+++ b/src/core/public/index.ts
@@ -144,6 +144,8 @@ export type {
SavedObjectsImportSimpleWarning,
SavedObjectsImportActionRequiredWarning,
SavedObjectsImportWarning,
+ SavedObjectReferenceWithContext,
+ SavedObjectsCollectMultiNamespaceReferencesResponse,
} from './saved_objects';
export { HttpFetchError } from './http';
diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md
index 4ea3b56c60a8f..129a7e565394f 100644
--- a/src/core/public/public.api.md
+++ b/src/core/public/public.api.md
@@ -1173,6 +1173,20 @@ export interface SavedObjectReference {
type: string;
}
+// @public
+export interface SavedObjectReferenceWithContext {
+ id: string;
+ inboundReferences: Array<{
+ type: string;
+ id: string;
+ name: string;
+ }>;
+ isMissing?: boolean;
+ spaces: string[];
+ spacesWithMatchingAliases?: string[];
+ type: string;
+}
+
// @public (undocumented)
export interface SavedObjectsBaseOptions {
namespace?: string;
@@ -1240,6 +1254,12 @@ export class SavedObjectsClient {
// @public
export type SavedObjectsClientContract = PublicMethodsOf;
+// @public
+export interface SavedObjectsCollectMultiNamespaceReferencesResponse {
+ // (undocumented)
+ objects: SavedObjectReferenceWithContext[];
+}
+
// @public (undocumented)
export interface SavedObjectsCreateOptions {
coreMigrationVersion?: string;
diff --git a/src/core/public/saved_objects/index.ts b/src/core/public/saved_objects/index.ts
index e8aef50376841..cd75bc16f8362 100644
--- a/src/core/public/saved_objects/index.ts
+++ b/src/core/public/saved_objects/index.ts
@@ -39,6 +39,8 @@ export type {
SavedObjectsImportSimpleWarning,
SavedObjectsImportActionRequiredWarning,
SavedObjectsImportWarning,
+ SavedObjectReferenceWithContext,
+ SavedObjectsCollectMultiNamespaceReferencesResponse,
} from '../../server/types';
export type {
diff --git a/src/core/server/elasticsearch/status.test.ts b/src/core/server/elasticsearch/status.test.ts
index 6f21fc204a1c2..c1f7cf0e35892 100644
--- a/src/core/server/elasticsearch/status.test.ts
+++ b/src/core/server/elasticsearch/status.test.ts
@@ -54,7 +54,7 @@ describe('calculateStatus', () => {
});
});
- it('changes to available with a differemnt message when isCompatible and warningNodes present', async () => {
+ it('changes to available with a different message when isCompatible and warningNodes present', async () => {
expect(
await calculateStatus$(
of({
@@ -204,4 +204,117 @@ describe('calculateStatus', () => {
]
`);
});
+
+ it('emits status updates when node info request error changes', () => {
+ const nodeCompat$ = new Subject();
+
+ const statusUpdates: ServiceStatus[] = [];
+ const subscription = calculateStatus$(nodeCompat$).subscribe((status) =>
+ statusUpdates.push(status)
+ );
+
+ nodeCompat$.next({
+ isCompatible: false,
+ kibanaVersion: '1.1.1',
+ incompatibleNodes: [],
+ warningNodes: [],
+ message: 'Unable to retrieve version info. connect ECONNREFUSED',
+ nodesInfoRequestError: new Error('connect ECONNREFUSED'),
+ });
+ nodeCompat$.next({
+ isCompatible: false,
+ kibanaVersion: '1.1.1',
+ incompatibleNodes: [],
+ warningNodes: [],
+ message: 'Unable to retrieve version info. security_exception',
+ nodesInfoRequestError: new Error('security_exception'),
+ });
+
+ subscription.unsubscribe();
+ expect(statusUpdates).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "level": unavailable,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "warningNodes": Array [],
+ },
+ "summary": "Waiting for Elasticsearch",
+ },
+ Object {
+ "level": critical,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "nodesInfoRequestError": [Error: connect ECONNREFUSED],
+ "warningNodes": Array [],
+ },
+ "summary": "Unable to retrieve version info. connect ECONNREFUSED",
+ },
+ Object {
+ "level": critical,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "nodesInfoRequestError": [Error: security_exception],
+ "warningNodes": Array [],
+ },
+ "summary": "Unable to retrieve version info. security_exception",
+ },
+ ]
+ `);
+ });
+
+ it('changes to available when a request error is resolved', () => {
+ const nodeCompat$ = new Subject();
+
+ const statusUpdates: ServiceStatus[] = [];
+ const subscription = calculateStatus$(nodeCompat$).subscribe((status) =>
+ statusUpdates.push(status)
+ );
+
+ nodeCompat$.next({
+ isCompatible: false,
+ kibanaVersion: '1.1.1',
+ incompatibleNodes: [],
+ warningNodes: [],
+ message: 'Unable to retrieve version info. security_exception',
+ nodesInfoRequestError: new Error('security_exception'),
+ });
+ nodeCompat$.next({
+ isCompatible: true,
+ kibanaVersion: '1.1.1',
+ warningNodes: [],
+ incompatibleNodes: [],
+ });
+
+ subscription.unsubscribe();
+ expect(statusUpdates).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "level": unavailable,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "warningNodes": Array [],
+ },
+ "summary": "Waiting for Elasticsearch",
+ },
+ Object {
+ "level": critical,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "nodesInfoRequestError": [Error: security_exception],
+ "warningNodes": Array [],
+ },
+ "summary": "Unable to retrieve version info. security_exception",
+ },
+ Object {
+ "level": available,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "warningNodes": Array [],
+ },
+ "summary": "Elasticsearch is available",
+ },
+ ]
+ `);
+ });
});
diff --git a/src/core/server/elasticsearch/status.ts b/src/core/server/elasticsearch/status.ts
index 68a61b07f498e..23e44b71863f1 100644
--- a/src/core/server/elasticsearch/status.ts
+++ b/src/core/server/elasticsearch/status.ts
@@ -32,6 +32,7 @@ export const calculateStatus$ = (
message,
incompatibleNodes,
warningNodes,
+ nodesInfoRequestError,
}): ServiceStatus => {
if (!isCompatible) {
return {
@@ -40,7 +41,11 @@ export const calculateStatus$ = (
// Message should always be present, but this is a safe fallback
message ??
`Some Elasticsearch nodes are not compatible with this version of Kibana`,
- meta: { warningNodes, incompatibleNodes },
+ meta: {
+ warningNodes,
+ incompatibleNodes,
+ ...(nodesInfoRequestError && { nodesInfoRequestError }),
+ },
};
} else if (warningNodes.length > 0) {
return {
diff --git a/src/core/server/elasticsearch/types.ts b/src/core/server/elasticsearch/types.ts
index 85678c21f03b0..8bbf665cbc096 100644
--- a/src/core/server/elasticsearch/types.ts
+++ b/src/core/server/elasticsearch/types.ts
@@ -179,6 +179,7 @@ export type InternalElasticsearchServiceStart = ElasticsearchServiceStart;
export interface ElasticsearchStatusMeta {
warningNodes: NodesVersionCompatibility['warningNodes'];
incompatibleNodes: NodesVersionCompatibility['incompatibleNodes'];
+ nodesInfoRequestError?: NodesVersionCompatibility['nodesInfoRequestError'];
}
/**
diff --git a/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts b/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts
index 0e08fd2ddc4c5..70166704679fe 100644
--- a/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts
+++ b/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts
@@ -19,7 +19,8 @@ const mockLogger = mockLoggerFactory.get('mock logger');
const KIBANA_VERSION = '5.1.0';
const createEsSuccess = elasticsearchClientMock.createSuccessTransportRequestPromise;
-const createEsError = elasticsearchClientMock.createErrorTransportRequestPromise;
+const createEsErrorReturn = (err: any) =>
+ elasticsearchClientMock.createErrorTransportRequestPromise(err);
function createNodes(...versions: string[]): NodesInfo {
const nodes = {} as any;
@@ -102,6 +103,28 @@ describe('mapNodesVersionCompatibility', () => {
`"You're running Kibana 5.1.0 with some different versions of Elasticsearch. Update Kibana or Elasticsearch to the same version to prevent compatibility issues: v5.1.1 @ http_address (ip)"`
);
});
+
+ it('returns isCompatible=false without an extended message when a nodesInfoRequestError is not provided', async () => {
+ const result = mapNodesVersionCompatibility({ nodes: {} }, KIBANA_VERSION, false);
+ expect(result.isCompatible).toBe(false);
+ expect(result.nodesInfoRequestError).toBeUndefined();
+ expect(result.message).toMatchInlineSnapshot(
+ `"Unable to retrieve version information from Elasticsearch nodes."`
+ );
+ });
+
+ it('returns isCompatible=false with an extended message when a nodesInfoRequestError is present', async () => {
+ const result = mapNodesVersionCompatibility(
+ { nodes: {}, nodesInfoRequestError: new Error('connection refused') },
+ KIBANA_VERSION,
+ false
+ );
+ expect(result.isCompatible).toBe(false);
+ expect(result.nodesInfoRequestError).toBeTruthy();
+ expect(result.message).toMatchInlineSnapshot(
+ `"Unable to retrieve version information from Elasticsearch nodes. connection refused"`
+ );
+ });
});
describe('pollEsNodesVersion', () => {
@@ -119,10 +142,10 @@ describe('pollEsNodesVersion', () => {
internalClient.nodes.info.mockImplementationOnce(() => createEsSuccess(infos));
};
const nodeInfosErrorOnce = (error: any) => {
- internalClient.nodes.info.mockImplementationOnce(() => createEsError(error));
+ internalClient.nodes.info.mockImplementationOnce(() => createEsErrorReturn(new Error(error)));
};
- it('returns iscCompatible=false and keeps polling when a poll request throws', (done) => {
+ it('returns isCompatible=false and keeps polling when a poll request throws', (done) => {
expect.assertions(3);
const expectedCompatibilityResults = [false, false, true];
jest.clearAllMocks();
@@ -148,6 +171,100 @@ describe('pollEsNodesVersion', () => {
});
});
+ it('returns the error from a failed nodes.info call when a poll request throws', (done) => {
+ expect.assertions(2);
+ const expectedCompatibilityResults = [false];
+ const expectedMessageResults = [
+ 'Unable to retrieve version information from Elasticsearch nodes. mock request error',
+ ];
+ jest.clearAllMocks();
+
+ nodeInfosErrorOnce('mock request error');
+
+ pollEsNodesVersion({
+ internalClient,
+ esVersionCheckInterval: 1,
+ ignoreVersionMismatch: false,
+ kibanaVersion: KIBANA_VERSION,
+ log: mockLogger,
+ })
+ .pipe(take(1))
+ .subscribe({
+ next: (result) => {
+ expect(result.isCompatible).toBe(expectedCompatibilityResults.shift());
+ expect(result.message).toBe(expectedMessageResults.shift());
+ },
+ complete: done,
+ error: done,
+ });
+ });
+
+ it('only emits if the error from a failed nodes.info call changed from the previous poll', (done) => {
+ expect.assertions(4);
+ const expectedCompatibilityResults = [false, false];
+ const expectedMessageResults = [
+ 'Unable to retrieve version information from Elasticsearch nodes. mock request error',
+ 'Unable to retrieve version information from Elasticsearch nodes. mock request error 2',
+ ];
+ jest.clearAllMocks();
+
+ nodeInfosErrorOnce('mock request error'); // emit
+ nodeInfosErrorOnce('mock request error'); // ignore, same error message
+ nodeInfosErrorOnce('mock request error 2'); // emit
+
+ pollEsNodesVersion({
+ internalClient,
+ esVersionCheckInterval: 1,
+ ignoreVersionMismatch: false,
+ kibanaVersion: KIBANA_VERSION,
+ log: mockLogger,
+ })
+ .pipe(take(2))
+ .subscribe({
+ next: (result) => {
+ expect(result.message).toBe(expectedMessageResults.shift());
+ expect(result.isCompatible).toBe(expectedCompatibilityResults.shift());
+ },
+ complete: done,
+ error: done,
+ });
+ });
+
+ it('returns isCompatible=false and keeps polling when a poll request throws, only responding again if the error message has changed', (done) => {
+ expect.assertions(8);
+ const expectedCompatibilityResults = [false, false, true, false];
+ const expectedMessageResults = [
+ 'This version of Kibana (v5.1.0) is incompatible with the following Elasticsearch nodes in your cluster: v5.0.0 @ http_address (ip)',
+ 'Unable to retrieve version information from Elasticsearch nodes. mock request error',
+ "You're running Kibana 5.1.0 with some different versions of Elasticsearch. Update Kibana or Elasticsearch to the same version to prevent compatibility issues: v5.2.0 @ http_address (ip), v5.1.1-Beta1 @ http_address (ip)",
+ 'Unable to retrieve version information from Elasticsearch nodes. mock request error',
+ ];
+ jest.clearAllMocks();
+
+ nodeInfosSuccessOnce(createNodes('5.1.0', '5.2.0', '5.0.0')); // emit
+ nodeInfosErrorOnce('mock request error'); // emit
+ nodeInfosErrorOnce('mock request error'); // ignore
+ nodeInfosSuccessOnce(createNodes('5.1.0', '5.2.0', '5.1.1-Beta1')); // emit
+ nodeInfosErrorOnce('mock request error'); // emit
+
+ pollEsNodesVersion({
+ internalClient,
+ esVersionCheckInterval: 1,
+ ignoreVersionMismatch: false,
+ kibanaVersion: KIBANA_VERSION,
+ log: mockLogger,
+ })
+ .pipe(take(4))
+ .subscribe({
+ next: (result) => {
+ expect(result.isCompatible).toBe(expectedCompatibilityResults.shift());
+ expect(result.message).toBe(expectedMessageResults.shift());
+ },
+ complete: done,
+ error: done,
+ });
+ });
+
it('returns compatibility results', (done) => {
expect.assertions(1);
const nodes = createNodes('5.1.0', '5.2.0', '5.0.0');
diff --git a/src/core/server/elasticsearch/version_check/ensure_es_version.ts b/src/core/server/elasticsearch/version_check/ensure_es_version.ts
index fb7ef0583e4a4..43cd52f1b5721 100644
--- a/src/core/server/elasticsearch/version_check/ensure_es_version.ts
+++ b/src/core/server/elasticsearch/version_check/ensure_es_version.ts
@@ -49,6 +49,7 @@ export interface NodesVersionCompatibility {
incompatibleNodes: NodeInfo[];
warningNodes: NodeInfo[];
kibanaVersion: string;
+ nodesInfoRequestError?: Error;
}
function getHumanizedNodeName(node: NodeInfo) {
@@ -57,22 +58,28 @@ function getHumanizedNodeName(node: NodeInfo) {
}
export function mapNodesVersionCompatibility(
- nodesInfo: NodesInfo,
+ nodesInfoResponse: NodesInfo & { nodesInfoRequestError?: Error },
kibanaVersion: string,
ignoreVersionMismatch: boolean
): NodesVersionCompatibility {
- if (Object.keys(nodesInfo.nodes ?? {}).length === 0) {
+ if (Object.keys(nodesInfoResponse.nodes ?? {}).length === 0) {
+ // Note: If the a nodesInfoRequestError is present, the message contains the nodesInfoRequestError.message as a suffix
+ let message = `Unable to retrieve version information from Elasticsearch nodes.`;
+ if (nodesInfoResponse.nodesInfoRequestError) {
+ message = message + ` ${nodesInfoResponse.nodesInfoRequestError.message}`;
+ }
return {
isCompatible: false,
- message: 'Unable to retrieve version information from Elasticsearch nodes.',
+ message,
incompatibleNodes: [],
warningNodes: [],
kibanaVersion,
+ nodesInfoRequestError: nodesInfoResponse.nodesInfoRequestError,
};
}
- const nodes = Object.keys(nodesInfo.nodes)
+ const nodes = Object.keys(nodesInfoResponse.nodes)
.sort() // Sorting ensures a stable node ordering for comparison
- .map((key) => nodesInfo.nodes[key])
+ .map((key) => nodesInfoResponse.nodes[key])
.map((node) => Object.assign({}, node, { name: getHumanizedNodeName(node) }));
// Aggregate incompatible ES nodes.
@@ -112,7 +119,13 @@ export function mapNodesVersionCompatibility(
kibanaVersion,
};
}
-
+// Returns true if NodesVersionCompatibility nodesInfoRequestError is the same
+function compareNodesInfoErrorMessages(
+ prev: NodesVersionCompatibility,
+ curr: NodesVersionCompatibility
+): boolean {
+ return prev.nodesInfoRequestError?.message === curr.nodesInfoRequestError?.message;
+}
// Returns true if two NodesVersionCompatibility entries match
function compareNodes(prev: NodesVersionCompatibility, curr: NodesVersionCompatibility) {
const nodesEqual = (n: NodeInfo, m: NodeInfo) => n.ip === m.ip && n.version === m.version;
@@ -121,7 +134,8 @@ function compareNodes(prev: NodesVersionCompatibility, curr: NodesVersionCompati
curr.incompatibleNodes.length === prev.incompatibleNodes.length &&
curr.warningNodes.length === prev.warningNodes.length &&
curr.incompatibleNodes.every((node, i) => nodesEqual(node, prev.incompatibleNodes[i])) &&
- curr.warningNodes.every((node, i) => nodesEqual(node, prev.warningNodes[i]))
+ curr.warningNodes.every((node, i) => nodesEqual(node, prev.warningNodes[i])) &&
+ compareNodesInfoErrorMessages(curr, prev)
);
}
@@ -141,14 +155,14 @@ export const pollEsNodesVersion = ({
})
).pipe(
map(({ body }) => body),
- catchError((_err) => {
- return of({ nodes: {} });
+ catchError((nodesInfoRequestError) => {
+ return of({ nodes: {}, nodesInfoRequestError });
})
);
}),
- map((nodesInfo: NodesInfo) =>
- mapNodesVersionCompatibility(nodesInfo, kibanaVersion, ignoreVersionMismatch)
+ map((nodesInfoResponse: NodesInfo & { nodesInfoRequestError?: Error }) =>
+ mapNodesVersionCompatibility(nodesInfoResponse, kibanaVersion, ignoreVersionMismatch)
),
- distinctUntilChanged(compareNodes) // Only emit if there are new nodes or versions
+ distinctUntilChanged(compareNodes) // Only emit if there are new nodes or versions or if we return an error and that error changes
);
};
diff --git a/src/core/server/index.ts b/src/core/server/index.ts
index ca328f17b2ae1..05408d839c0ae 100644
--- a/src/core/server/index.ts
+++ b/src/core/server/index.ts
@@ -320,12 +320,16 @@ export type {
SavedObjectsResolveResponse,
SavedObjectsUpdateOptions,
SavedObjectsUpdateResponse,
- SavedObjectsAddToNamespacesOptions,
- SavedObjectsAddToNamespacesResponse,
- SavedObjectsDeleteFromNamespacesOptions,
- SavedObjectsDeleteFromNamespacesResponse,
SavedObjectsRemoveReferencesToOptions,
SavedObjectsRemoveReferencesToResponse,
+ SavedObjectsCollectMultiNamespaceReferencesObject,
+ SavedObjectsCollectMultiNamespaceReferencesOptions,
+ SavedObjectReferenceWithContext,
+ SavedObjectsCollectMultiNamespaceReferencesResponse,
+ SavedObjectsUpdateObjectsSpacesObject,
+ SavedObjectsUpdateObjectsSpacesOptions,
+ SavedObjectsUpdateObjectsSpacesResponse,
+ SavedObjectsUpdateObjectsSpacesResponseObject,
SavedObjectsServiceStart,
SavedObjectsServiceSetup,
SavedObjectStatusMeta,
diff --git a/src/core/server/saved_objects/export/saved_objects_exporter.test.ts b/src/core/server/saved_objects/export/saved_objects_exporter.test.ts
index 468a761781365..6bdb8003de49d 100644
--- a/src/core/server/saved_objects/export/saved_objects_exporter.test.ts
+++ b/src/core/server/saved_objects/export/saved_objects_exporter.test.ts
@@ -1149,6 +1149,29 @@ describe('getSortedObjectsForExport()', () => {
]);
});
+ test('return results including the `namespaces` attribute when includeNamespaces option is used', async () => {
+ const createSavedObject = (obj: any) => ({ ...obj, attributes: {}, references: [] });
+ const objectResults = [
+ createSavedObject({ type: 'multi', id: '1', namespaces: ['foo'] }),
+ createSavedObject({ type: 'multi', id: '2', namespaces: ['bar'] }),
+ createSavedObject({ type: 'other', id: '3' }),
+ ];
+ savedObjectsClient.bulkGet.mockResolvedValueOnce({
+ saved_objects: objectResults,
+ });
+ const exportStream = await exporter.exportByObjects({
+ request,
+ objects: [
+ { type: 'multi', id: '1' },
+ { type: 'multi', id: '2' },
+ { type: 'other', id: '3' },
+ ],
+ includeNamespaces: true,
+ });
+ const response = await readStreamToCompletion(exportStream);
+ expect(response).toEqual([...objectResults, expect.objectContaining({ exportedCount: 3 })]);
+ });
+
test('includes nested dependencies when passed in', async () => {
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
diff --git a/src/core/server/saved_objects/export/saved_objects_exporter.ts b/src/core/server/saved_objects/export/saved_objects_exporter.ts
index 868efa872d643..8cd6934bf1af9 100644
--- a/src/core/server/saved_objects/export/saved_objects_exporter.ts
+++ b/src/core/server/saved_objects/export/saved_objects_exporter.ts
@@ -77,6 +77,7 @@ export class SavedObjectsExporter {
return this.processObjects(objects, byIdAscComparator, {
request: options.request,
includeReferencesDeep: options.includeReferencesDeep,
+ includeNamespaces: options.includeNamespaces,
excludeExportDetails: options.excludeExportDetails,
namespace: options.namespace,
});
@@ -99,6 +100,7 @@ export class SavedObjectsExporter {
return this.processObjects(objects, comparator, {
request: options.request,
includeReferencesDeep: options.includeReferencesDeep,
+ includeNamespaces: options.includeNamespaces,
excludeExportDetails: options.excludeExportDetails,
namespace: options.namespace,
});
@@ -111,6 +113,7 @@ export class SavedObjectsExporter {
request,
excludeExportDetails = false,
includeReferencesDeep = false,
+ includeNamespaces = false,
namespace,
}: SavedObjectExportBaseOptions
) {
@@ -139,9 +142,9 @@ export class SavedObjectsExporter {
}
// redact attributes that should not be exported
- const redactedObjects = exportedObjects.map>(
- ({ namespaces, ...object }) => object
- );
+ const redactedObjects = includeNamespaces
+ ? exportedObjects
+ : exportedObjects.map>(({ namespaces, ...object }) => object);
const exportDetails: SavedObjectsExportResultDetails = {
exportedCount: exportedObjects.length,
diff --git a/src/core/server/saved_objects/export/types.ts b/src/core/server/saved_objects/export/types.ts
index 4326943bd31ce..7891af6df5b1b 100644
--- a/src/core/server/saved_objects/export/types.ts
+++ b/src/core/server/saved_objects/export/types.ts
@@ -15,6 +15,12 @@ export interface SavedObjectExportBaseOptions {
request: KibanaRequest;
/** flag to also include all related saved objects in the export stream. */
includeReferencesDeep?: 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.
+ */
+ includeNamespaces?: boolean;
/** flag to not append {@link SavedObjectsExportResultDetails | export details} to the end of the export stream. */
excludeExportDetails?: boolean;
/** optional namespace to override the namespace used by the savedObjectsClient. */
diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts
index 45286f158edb1..71e5565ebcbef 100644
--- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts
+++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts
@@ -982,6 +982,7 @@ describe('DocumentMigrator', () => {
id: 'foo-namespace:dog:loud',
type: LEGACY_URL_ALIAS_TYPE,
attributes: {
+ sourceId: 'loud',
targetNamespace: 'foo-namespace',
targetType: 'dog',
targetId: 'uuidv5',
@@ -1046,6 +1047,7 @@ describe('DocumentMigrator', () => {
id: 'foo-namespace:dog:cute',
type: LEGACY_URL_ALIAS_TYPE,
attributes: {
+ sourceId: 'cute',
targetNamespace: 'foo-namespace',
targetType: 'dog',
targetId: 'uuidv5',
@@ -1168,6 +1170,7 @@ describe('DocumentMigrator', () => {
id: 'foo-namespace:dog:hungry',
type: LEGACY_URL_ALIAS_TYPE,
attributes: {
+ sourceId: 'hungry',
targetNamespace: 'foo-namespace',
targetType: 'dog',
targetId: 'uuidv5',
@@ -1240,6 +1243,7 @@ describe('DocumentMigrator', () => {
id: 'foo-namespace:dog:pretty',
type: LEGACY_URL_ALIAS_TYPE,
attributes: {
+ sourceId: 'pretty',
targetNamespace: 'foo-namespace',
targetType: 'dog',
targetId: 'uuidv5',
diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts
index 4f58397866cfb..c96de6ebbfcdd 100644
--- a/src/core/server/saved_objects/migrations/core/document_migrator.ts
+++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts
@@ -560,6 +560,7 @@ function convertNamespaceType(doc: SavedObjectUnsanitizedDoc) {
id: `${namespace}:${type}:${originId}`,
type: LEGACY_URL_ALIAS_TYPE,
attributes: {
+ sourceId: originId,
targetNamespace: namespace,
targetType: type,
targetId: id,
@@ -660,13 +661,14 @@ function wrapWithTry(
migrationFn: SavedObjectMigrationFn,
log: Logger
) {
+ const context = Object.freeze({
+ log: new MigrationLogger(log),
+ migrationVersion: version,
+ convertToMultiNamespaceTypeVersion: type.convertToMultiNamespaceTypeVersion,
+ });
+
return function tryTransformDoc(doc: SavedObjectUnsanitizedDoc) {
try {
- const context = {
- log: new MigrationLogger(log),
- migrationVersion: version,
- convertToMultiNamespaceTypeVersion: type.convertToMultiNamespaceTypeVersion,
- };
const result = migrationFn(doc, context);
// A basic sanity check to help migration authors detect basic errors
diff --git a/src/core/server/saved_objects/migrations/types.ts b/src/core/server/saved_objects/migrations/types.ts
index 619a7f85a327b..570315e780ebe 100644
--- a/src/core/server/saved_objects/migrations/types.ts
+++ b/src/core/server/saved_objects/migrations/types.ts
@@ -56,15 +56,15 @@ export interface SavedObjectMigrationContext {
/**
* logger instance to be used by the migration handler
*/
- log: SavedObjectsMigrationLogger;
+ readonly log: SavedObjectsMigrationLogger;
/**
* The migration version that this migration function is defined for
*/
- migrationVersion: string;
+ readonly migrationVersion: string;
/**
* The version in which this object type is being converted to a multi-namespace type
*/
- convertToMultiNamespaceTypeVersion?: string;
+ readonly convertToMultiNamespaceTypeVersion?: string;
}
/**
diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/rewriting_id.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/rewriting_id.test.ts
index 9f7e32c49ef15..4a1a2b414a642 100644
--- a/src/core/server/saved_objects/migrationsv2/integration_tests/rewriting_id.test.ts
+++ b/src/core/server/saved_objects/migrationsv2/integration_tests/rewriting_id.test.ts
@@ -194,6 +194,7 @@ describe('migration v2', () => {
id: 'legacy-url-alias:spacex:foo:1',
type: 'legacy-url-alias',
'legacy-url-alias': {
+ sourceId: '1',
targetId: newFooId,
targetNamespace: 'spacex',
targetType: 'foo',
@@ -226,6 +227,7 @@ describe('migration v2', () => {
id: 'legacy-url-alias:spacex:bar:1',
type: 'legacy-url-alias',
'legacy-url-alias': {
+ sourceId: '1',
targetId: newBarId,
targetNamespace: 'spacex',
targetType: 'bar',
diff --git a/src/core/server/saved_objects/migrationsv2/model.test.ts b/src/core/server/saved_objects/migrationsv2/model.test.ts
index adeb78e568af3..7a47e58f1947c 100644
--- a/src/core/server/saved_objects/migrationsv2/model.test.ts
+++ b/src/core/server/saved_objects/migrationsv2/model.test.ts
@@ -198,6 +198,31 @@ describe('migrations v2 model', () => {
});
describe('model transitions from', () => {
+ it('transition returns new state', () => {
+ const initState: State = {
+ ...baseState,
+ controlState: 'INIT',
+ currentAlias: '.kibana',
+ versionAlias: '.kibana_7.11.0',
+ versionIndex: '.kibana_7.11.0_001',
+ };
+
+ const res: ResponseType<'INIT'> = Either.right({
+ '.kibana_7.11.0_001': {
+ aliases: {
+ '.kibana': {},
+ '.kibana_7.11.0': {},
+ },
+ mappings: {
+ properties: {},
+ },
+ settings: {},
+ },
+ });
+ const newState = model(initState, res);
+ expect(newState).not.toBe(initState);
+ });
+
describe('INIT', () => {
const initState: State = {
...baseState,
diff --git a/src/core/server/saved_objects/migrationsv2/model.ts b/src/core/server/saved_objects/migrationsv2/model.ts
index 3ef3cb4f83b6f..f4185225ae073 100644
--- a/src/core/server/saved_objects/migrationsv2/model.ts
+++ b/src/core/server/saved_objects/migrationsv2/model.ts
@@ -9,7 +9,7 @@
import { gt, valid } from 'semver';
import * as Either from 'fp-ts/lib/Either';
import * as Option from 'fp-ts/lib/Option';
-import { cloneDeep } from 'lodash';
+
import { AliasAction, FetchIndexResponse, isLeftTypeof, RetryableEsClientError } from './actions';
import { AllActionStates, InitState, State } from './types';
import { IndexMapping } from '../mappings';
@@ -187,7 +187,7 @@ export const model = (currentState: State, resW: ResponseType):
// control state using:
// `const res = resW as ResponseType;`
- let stateP: State = cloneDeep(currentState);
+ let stateP: State = currentState;
// Handle retryable_es_client_errors. Other left values need to be handled
// by the control state specific code below.
diff --git a/src/core/server/saved_objects/migrationsv2/types.ts b/src/core/server/saved_objects/migrationsv2/types.ts
index e3e52212d56cb..adcd2ad32fd24 100644
--- a/src/core/server/saved_objects/migrationsv2/types.ts
+++ b/src/core/server/saved_objects/migrationsv2/types.ts
@@ -381,7 +381,7 @@ export interface LegacyDeleteState extends LegacyBaseState {
readonly controlState: 'LEGACY_DELETE';
}
-export type State =
+export type State = Readonly<
| FatalState
| InitState
| DoneState
@@ -411,7 +411,8 @@ export type State =
| LegacySetWriteBlockState
| LegacyReindexState
| LegacyReindexWaitForTaskState
- | LegacyDeleteState;
+ | LegacyDeleteState
+>;
export type AllControlStates = State['controlState'];
/**
diff --git a/src/core/server/saved_objects/object_types/registration.ts b/src/core/server/saved_objects/object_types/registration.ts
index 149fc09ce401d..2b5f49123b2cf 100644
--- a/src/core/server/saved_objects/object_types/registration.ts
+++ b/src/core/server/saved_objects/object_types/registration.ts
@@ -13,10 +13,15 @@ const legacyUrlAliasType: SavedObjectsType = {
name: LEGACY_URL_ALIAS_TYPE,
namespaceType: 'agnostic',
mappings: {
- dynamic: false, // we aren't querying or aggregating over this data, so we don't need to specify any fields
- properties: {},
+ dynamic: false,
+ properties: {
+ sourceId: { type: 'keyword' },
+ targetType: { type: 'keyword' },
+ disabled: { type: 'boolean' },
+ // other properties exist, but we aren't querying or aggregating on those, so we don't need to specify them (because we use `dynamic: false` above)
+ },
},
- hidden: true,
+ hidden: false,
};
/**
diff --git a/src/core/server/saved_objects/object_types/types.ts b/src/core/server/saved_objects/object_types/types.ts
index 6fca2ed59906b..9038d1a606067 100644
--- a/src/core/server/saved_objects/object_types/types.ts
+++ b/src/core/server/saved_objects/object_types/types.ts
@@ -7,13 +7,49 @@
*/
/**
+ * A legacy URL alias is created for an object when it is converted from a single-namespace type to a multi-namespace type. This enables us
+ * to preserve functionality of existing URLs for objects whose IDs have been changed during the conversion process, by way of the new
+ * `SavedObjectsClient.resolve()` API.
+ *
+ * Legacy URL aliases are only created by the `DocumentMigrator`, and will always have a saved object ID as follows:
+ *
+ * ```
+ * `${targetNamespace}:${targetType}:${sourceId}`
+ * ```
+ *
+ * This predictable object ID allows aliases to be easily looked up during the resolve operation, and ensures that exactly one alias will
+ * exist for a given source per space.
+ *
* @internal
*/
export interface LegacyUrlAlias {
+ /**
+ * The original ID of the object, before it was converted.
+ */
+ sourceId: string;
+ /**
+ * The namespace that the object existed in when it was converted.
+ */
targetNamespace: string;
+ /**
+ * The type of the object when it was converted.
+ */
targetType: string;
+ /**
+ * The new ID of the object when it was converted.
+ */
targetId: string;
+ /**
+ * The last time this alias was used with `SavedObjectsClient.resolve()`.
+ */
lastResolved?: string;
+ /**
+ * How many times this alias was used with `SavedObjectsClient.resolve()`.
+ */
resolveCounter?: number;
+ /**
+ * If true, this alias is disabled and it will be ignored in `SavedObjectsClient.resolve()` and
+ * `SavedObjectsClient.collectMultiNamespaceReferences()`.
+ */
disabled?: boolean;
}
diff --git a/src/core/server/saved_objects/serialization/serializer.ts b/src/core/server/saved_objects/serialization/serializer.ts
index 4b955032939b3..9c91abcfe79c5 100644
--- a/src/core/server/saved_objects/serialization/serializer.ts
+++ b/src/core/server/saved_objects/serialization/serializer.ts
@@ -76,10 +76,10 @@ export class SavedObjectsSerializer {
* @param {SavedObjectsRawDoc} doc - The raw ES document to be converted to saved object format.
* @param {SavedObjectsRawDocParseOptions} options - Options for parsing the raw document.
*/
- public rawToSavedObject(
+ public rawToSavedObject(
doc: SavedObjectsRawDoc,
options: SavedObjectsRawDocParseOptions = {}
- ): SavedObjectSanitizedDoc {
+ ): SavedObjectSanitizedDoc {
this.checkIsRawSavedObject(doc, options); // throws a descriptive error if the document is not a saved object
const { namespaceTreatment = 'strict' } = options;
diff --git a/src/core/server/saved_objects/service/index.ts b/src/core/server/saved_objects/service/index.ts
index 8a66e6176d1f5..7b4ffcf2dd6cf 100644
--- a/src/core/server/saved_objects/service/index.ts
+++ b/src/core/server/saved_objects/service/index.ts
@@ -17,6 +17,14 @@ export type {
SavedObjectsClientWrapperOptions,
SavedObjectsClientFactory,
SavedObjectsClientFactoryProvider,
+ SavedObjectsCollectMultiNamespaceReferencesObject,
+ SavedObjectsCollectMultiNamespaceReferencesOptions,
+ SavedObjectReferenceWithContext,
+ SavedObjectsCollectMultiNamespaceReferencesResponse,
+ SavedObjectsUpdateObjectsSpacesObject,
+ SavedObjectsUpdateObjectsSpacesOptions,
+ SavedObjectsUpdateObjectsSpacesResponse,
+ SavedObjectsUpdateObjectsSpacesResponseObject,
} from './lib';
export * from './saved_objects_client';
diff --git a/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.test.mock.ts b/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.test.mock.ts
new file mode 100644
index 0000000000000..cbd1ac4a8eb8f
--- /dev/null
+++ b/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.test.mock.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import type * as InternalUtils from './internal_utils';
+
+export const mockRawDocExistsInNamespace = jest.fn() as jest.MockedFunction<
+ typeof InternalUtils['rawDocExistsInNamespace']
+>;
+
+jest.mock('./internal_utils', () => {
+ const actual = jest.requireActual('./internal_utils');
+ return {
+ ...actual,
+ rawDocExistsInNamespace: mockRawDocExistsInNamespace,
+ };
+});
diff --git a/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.test.ts b/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.test.ts
new file mode 100644
index 0000000000000..00fc039ff005f
--- /dev/null
+++ b/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.test.ts
@@ -0,0 +1,444 @@
+/*
+ * Copyright 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 { mockRawDocExistsInNamespace } from './collect_multi_namespace_references.test.mock';
+
+import type { DeeplyMockedKeys } from '@kbn/utility-types/target/jest';
+import type { ElasticsearchClient } from 'src/core/server/elasticsearch';
+import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks';
+
+import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../object_types';
+import { typeRegistryMock } from '../../saved_objects_type_registry.mock';
+import { SavedObjectsSerializer } from '../../serialization';
+import type {
+ CollectMultiNamespaceReferencesParams,
+ SavedObjectsCollectMultiNamespaceReferencesObject,
+ SavedObjectsCollectMultiNamespaceReferencesOptions,
+} from './collect_multi_namespace_references';
+import { collectMultiNamespaceReferences } from './collect_multi_namespace_references';
+import { savedObjectsPointInTimeFinderMock } from './point_in_time_finder.mock';
+import { savedObjectsRepositoryMock } from './repository.mock';
+import { PointInTimeFinder } from './point_in_time_finder';
+import { ISavedObjectsRepository } from './repository';
+
+const SPACES = ['default', 'another-space'];
+const VERSION_PROPS = { _seq_no: 1, _primary_term: 1 };
+
+const MULTI_NAMESPACE_OBJ_TYPE_1 = 'type-a';
+const MULTI_NAMESPACE_OBJ_TYPE_2 = 'type-b';
+const NON_MULTI_NAMESPACE_OBJ_TYPE = 'type-c';
+const MULTI_NAMESPACE_HIDDEN_OBJ_TYPE = 'type-d';
+
+beforeEach(() => {
+ mockRawDocExistsInNamespace.mockReset();
+ mockRawDocExistsInNamespace.mockReturnValue(true); // return true by default
+});
+
+describe('collectMultiNamespaceReferences', () => {
+ let client: DeeplyMockedKeys;
+ let savedObjectsMock: jest.Mocked;
+ let createPointInTimeFinder: jest.MockedFunction<
+ CollectMultiNamespaceReferencesParams['createPointInTimeFinder']
+ >;
+ let pointInTimeFinder: DeeplyMockedKeys;
+
+ /** Sets up the type registry, saved objects client, etc. and return the full parameters object to be passed to `collectMultiNamespaceReferences` */
+ function setup(
+ objects: SavedObjectsCollectMultiNamespaceReferencesObject[],
+ options: SavedObjectsCollectMultiNamespaceReferencesOptions = {}
+ ): CollectMultiNamespaceReferencesParams {
+ const registry = typeRegistryMock.create();
+ registry.isMultiNamespace.mockImplementation(
+ (type) =>
+ [
+ MULTI_NAMESPACE_OBJ_TYPE_1,
+ MULTI_NAMESPACE_OBJ_TYPE_2,
+ MULTI_NAMESPACE_HIDDEN_OBJ_TYPE,
+ ].includes(type) // NON_MULTI_NAMESPACE_TYPE is omitted
+ );
+ registry.isShareable.mockImplementation(
+ (type) => [MULTI_NAMESPACE_OBJ_TYPE_1, MULTI_NAMESPACE_HIDDEN_OBJ_TYPE].includes(type) // MULTI_NAMESPACE_OBJ_TYPE_2 and NON_MULTI_NAMESPACE_TYPE are omitted
+ );
+ client = elasticsearchClientMock.createElasticsearchClient();
+
+ const serializer = new SavedObjectsSerializer(registry);
+ savedObjectsMock = savedObjectsRepositoryMock.create();
+ savedObjectsMock.find.mockResolvedValue({
+ pit_id: 'foo',
+ saved_objects: [],
+ // the rest of these fields don't matter but are included for type safety
+ total: 0,
+ page: 1,
+ per_page: 100,
+ });
+ createPointInTimeFinder = jest.fn();
+ createPointInTimeFinder.mockImplementation((params) => {
+ pointInTimeFinder = savedObjectsPointInTimeFinderMock.create({ savedObjectsMock })(params);
+ return pointInTimeFinder;
+ });
+ return {
+ registry,
+ allowedTypes: [
+ MULTI_NAMESPACE_OBJ_TYPE_1,
+ MULTI_NAMESPACE_OBJ_TYPE_2,
+ NON_MULTI_NAMESPACE_OBJ_TYPE,
+ ], // MULTI_NAMESPACE_HIDDEN_TYPE is omitted
+ client,
+ serializer,
+ getIndexForType: (type: string) => `index-for-${type}`,
+ createPointInTimeFinder,
+ objects,
+ options,
+ };
+ }
+
+ /** Mocks the saved objects client so it returns the expected results */
+ function mockMgetResults(
+ ...results: Array<{
+ found: boolean;
+ references?: Array<{ type: string; id: string }>;
+ }>
+ ) {
+ client.mget.mockReturnValueOnce(
+ elasticsearchClientMock.createSuccessTransportRequestPromise({
+ docs: results.map((x) => {
+ const references =
+ x.references?.map(({ type, id }) => ({ type, id, name: 'ref-name' })) ?? [];
+ return x.found
+ ? {
+ _id: 'doesnt-matter',
+ _index: 'doesnt-matter',
+ _source: {
+ namespaces: SPACES,
+ references,
+ },
+ ...VERSION_PROPS,
+ found: true,
+ }
+ : {
+ _id: 'doesnt-matter',
+ _index: 'doesnt-matter',
+ found: false,
+ };
+ }),
+ })
+ );
+ }
+
+ function mockFindResults(...results: LegacyUrlAlias[]) {
+ savedObjectsMock.find.mockResolvedValueOnce({
+ pit_id: 'foo',
+ saved_objects: results.map((attributes) => ({
+ id: 'doesnt-matter',
+ type: LEGACY_URL_ALIAS_TYPE,
+ attributes,
+ references: [],
+ score: 0, // doesn't matter
+ })),
+ // the rest of these fields don't matter but are included for type safety
+ total: 0,
+ page: 1,
+ per_page: 100,
+ });
+ }
+
+ /** Asserts that mget is called for the given objects */
+ function expectMgetArgs(
+ n: number,
+ ...objects: SavedObjectsCollectMultiNamespaceReferencesObject[]
+ ) {
+ const docs = objects.map(({ type, id }) => expect.objectContaining({ _id: `${type}:${id}` }));
+ expect(client.mget).toHaveBeenNthCalledWith(n, { body: { docs } }, expect.anything());
+ }
+
+ it('returns an empty array if no object args are passed in', async () => {
+ const params = setup([]);
+
+ const result = await collectMultiNamespaceReferences(params);
+ expect(client.mget).not.toHaveBeenCalled();
+ expect(result.objects).toEqual([]);
+ });
+
+ it('excludes args that have unsupported types', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const obj2 = { type: NON_MULTI_NAMESPACE_OBJ_TYPE, id: 'id-2' };
+ const obj3 = { type: MULTI_NAMESPACE_HIDDEN_OBJ_TYPE, id: 'id-3' };
+ const params = setup([obj1, obj2, obj3]);
+ mockMgetResults({ found: true }); // results for obj1
+
+ const result = await collectMultiNamespaceReferences(params);
+ expect(client.mget).toHaveBeenCalledTimes(1);
+ expectMgetArgs(1, obj1); // the non-multi-namespace type and the hidden type are excluded
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: SPACES, inboundReferences: [] },
+ // even though they are excluded from the cluster call, obj2 and obj3 are included in the results
+ { ...obj2, spaces: [], inboundReferences: [] },
+ { ...obj3, spaces: [], inboundReferences: [] },
+ ]);
+ });
+
+ it('excludes references that have unsupported types', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const obj2 = { type: NON_MULTI_NAMESPACE_OBJ_TYPE, id: 'id-2' };
+ const obj3 = { type: MULTI_NAMESPACE_HIDDEN_OBJ_TYPE, id: 'id-3' };
+ const params = setup([obj1]);
+ mockMgetResults({ found: true, references: [obj2, obj3] }); // results for obj1
+
+ const result = await collectMultiNamespaceReferences(params);
+ expect(client.mget).toHaveBeenCalledTimes(1);
+ expectMgetArgs(1, obj1);
+ // obj2 and obj3 are not retrieved in a second cluster call
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: SPACES, inboundReferences: [] },
+ // obj2 and obj3 are excluded from the results
+ ]);
+ });
+
+ it('handles circular references', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const params = setup([obj1]);
+ mockMgetResults({ found: true, references: [obj1] }); // results for obj1
+
+ const result = await collectMultiNamespaceReferences(params);
+ expect(params.client.mget).toHaveBeenCalledTimes(1);
+ expectMgetArgs(1, obj1); // obj1 is retrieved once, and it is not retrieved again in a second cluster call
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: SPACES, inboundReferences: [{ ...obj1, name: 'ref-name' }] }, // obj1 reflects the inbound reference to itself
+ ]);
+ });
+
+ it('handles a reference graph more than 20 layers deep (circuit-breaker)', async () => {
+ const type = MULTI_NAMESPACE_OBJ_TYPE_1;
+ const params = setup([{ type, id: 'id-1' }]);
+ for (let i = 1; i < 100; i++) {
+ mockMgetResults({ found: true, references: [{ type, id: `id-${i + 1}` }] });
+ }
+
+ await expect(() => collectMultiNamespaceReferences(params)).rejects.toThrow(
+ /Exceeded maximum reference graph depth/
+ );
+ expect(params.client.mget).toHaveBeenCalledTimes(20);
+ });
+
+ it('handles multiple inbound references', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const obj2 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-2' };
+ const obj3 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-3' };
+ const params = setup([obj1, obj2]);
+ mockMgetResults({ found: true, references: [obj3] }, { found: true, references: [obj3] }); // results for obj1 and obj2
+ mockMgetResults({ found: true }); // results for obj3
+
+ const result = await collectMultiNamespaceReferences(params);
+ expect(params.client.mget).toHaveBeenCalledTimes(2);
+ expectMgetArgs(1, obj1, obj2);
+ expectMgetArgs(2, obj3); // obj3 is retrieved in a second cluster call
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: SPACES, inboundReferences: [] },
+ { ...obj2, spaces: SPACES, inboundReferences: [] },
+ {
+ ...obj3,
+ spaces: SPACES,
+ inboundReferences: [
+ // obj3 reflects both inbound references
+ { ...obj1, name: 'ref-name' },
+ { ...obj2, name: 'ref-name' },
+ ],
+ },
+ ]);
+ });
+
+ it('handles transitive references', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const obj2 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-2' };
+ const obj3 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-3' };
+ const params = setup([obj1]);
+ mockMgetResults({ found: true, references: [obj2] }); // results for obj1
+ mockMgetResults({ found: true, references: [obj3] }); // results for obj2
+ mockMgetResults({ found: true }); // results for obj3
+
+ const result = await collectMultiNamespaceReferences(params);
+ expect(params.client.mget).toHaveBeenCalledTimes(3);
+ expectMgetArgs(1, obj1);
+ expectMgetArgs(2, obj2); // obj2 is retrieved in a second cluster call
+ expectMgetArgs(3, obj3); // obj3 is retrieved in a third cluster call
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: SPACES, inboundReferences: [] },
+ { ...obj2, spaces: SPACES, inboundReferences: [{ ...obj1, name: 'ref-name' }] }, // obj2 reflects the inbound reference
+ { ...obj3, spaces: SPACES, inboundReferences: [{ ...obj2, name: 'ref-name' }] }, // obj3 reflects the inbound reference
+ ]);
+ });
+
+ it('handles missing objects and missing references', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' }; // found, with missing references to obj4 and obj5
+ const obj2 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-2' }; // missing object (found, but doesn't exist in the current space))
+ const obj3 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-3' }; // missing object (not found
+ const obj4 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-4' }; // missing reference (found but doesn't exist in the current space)
+ const obj5 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-5' }; // missing reference (not found)
+ const params = setup([obj1, obj2, obj3]);
+ mockMgetResults({ found: true, references: [obj4, obj5] }, { found: true }, { found: false }); // results for obj1, obj2, and obj3
+ mockMgetResults({ found: true }, { found: false }); // results for obj4 and obj5
+ mockRawDocExistsInNamespace.mockReturnValueOnce(true); // for obj1
+ mockRawDocExistsInNamespace.mockReturnValueOnce(false); // for obj2
+ mockRawDocExistsInNamespace.mockReturnValueOnce(false); // for obj4
+
+ const result = await collectMultiNamespaceReferences(params);
+ expect(params.client.mget).toHaveBeenCalledTimes(2);
+ expectMgetArgs(1, obj1, obj2, obj3);
+ expectMgetArgs(2, obj4, obj5);
+ expect(mockRawDocExistsInNamespace).toHaveBeenCalledTimes(3);
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: SPACES, inboundReferences: [] },
+ { ...obj2, spaces: [], inboundReferences: [], isMissing: true },
+ { ...obj3, spaces: [], inboundReferences: [], isMissing: true },
+ { ...obj4, spaces: [], inboundReferences: [{ ...obj1, name: 'ref-name' }], isMissing: true },
+ { ...obj5, spaces: [], inboundReferences: [{ ...obj1, name: 'ref-name' }], isMissing: true },
+ ]);
+ });
+
+ it('handles the purpose="updateObjectsSpaces" option', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const obj2 = { type: MULTI_NAMESPACE_OBJ_TYPE_2, id: 'id-2' };
+ const obj3 = { type: MULTI_NAMESPACE_OBJ_TYPE_2, id: 'id-3' };
+ const params = setup([obj1, obj2], { purpose: 'updateObjectsSpaces' });
+ mockMgetResults({ found: true, references: [obj3] }); // results for obj1
+
+ const result = await collectMultiNamespaceReferences(params);
+ expect(client.mget).toHaveBeenCalledTimes(1);
+ expectMgetArgs(1, obj1); // obj2 is excluded
+ // obj3 is not retrieved in a second cluster call
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: SPACES, inboundReferences: [] },
+ // even though it is excluded from the cluster call, obj2 is included in the results
+ { ...obj2, spaces: [], inboundReferences: [] },
+ // obj3 is excluded from the results
+ ]);
+ });
+
+ describe('legacy URL aliases', () => {
+ it('uses the PointInTimeFinder to search for legacy URL aliases', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const obj2 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-2' };
+ const obj3 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-3' };
+ const params = setup([obj1, obj2], {});
+ mockMgetResults({ found: true, references: [obj3] }, { found: true, references: [] }); // results for obj1 and obj2
+ mockMgetResults({ found: true, references: [] }); // results for obj3
+ mockFindResults(
+ // mock search results for four aliases for obj1, and none for obj2 or obj3
+ ...[1, 2, 3, 4].map((i) => ({
+ sourceId: obj1.id,
+ targetId: 'doesnt-matter',
+ targetType: obj1.type,
+ targetNamespace: `space-${i}`,
+ }))
+ );
+
+ const result = await collectMultiNamespaceReferences(params);
+ expect(client.mget).toHaveBeenCalledTimes(2);
+ expectMgetArgs(1, obj1, obj2);
+ expectMgetArgs(2, obj3); // obj3 is retrieved in a second cluster call
+ expect(createPointInTimeFinder).toHaveBeenCalledTimes(1);
+ const kueryFilterArgs = createPointInTimeFinder.mock.calls[0][0].filter.arguments;
+ expect(kueryFilterArgs).toHaveLength(2);
+ const typeAndIdFilters = kueryFilterArgs[1].arguments;
+ expect(typeAndIdFilters).toHaveLength(3);
+ [obj1, obj2, obj3].forEach(({ type, id }, i) => {
+ const typeAndIdFilter = typeAndIdFilters[i].arguments;
+ expect(typeAndIdFilter).toEqual([
+ expect.objectContaining({
+ arguments: expect.arrayContaining([{ type: 'literal', value: type }]),
+ }),
+ expect.objectContaining({
+ arguments: expect.arrayContaining([{ type: 'literal', value: id }]),
+ }),
+ ]);
+ });
+ expect(pointInTimeFinder.find).toHaveBeenCalledTimes(1);
+ expect(pointInTimeFinder.close).toHaveBeenCalledTimes(2);
+ expect(result.objects).toEqual([
+ {
+ ...obj1,
+ spaces: SPACES,
+ inboundReferences: [],
+ spacesWithMatchingAliases: ['space-1', 'space-2', 'space-3', 'space-4'],
+ },
+ { ...obj2, spaces: SPACES, inboundReferences: [] },
+ { ...obj3, spaces: SPACES, inboundReferences: [{ ...obj1, name: 'ref-name' }] },
+ ]);
+ });
+
+ it('does not create a PointInTimeFinder if no objects are passed in', async () => {
+ const params = setup([]);
+
+ await collectMultiNamespaceReferences(params);
+ expect(params.createPointInTimeFinder).not.toHaveBeenCalled();
+ });
+
+ it('does not search for objects that have an empty spaces array (the object does not exist, or we are not sure)', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const obj2 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-2' };
+ const params = setup([obj1, obj2]);
+ mockMgetResults({ found: true }, { found: false }); // results for obj1 and obj2
+
+ await collectMultiNamespaceReferences(params);
+ expect(createPointInTimeFinder).toHaveBeenCalledTimes(1);
+
+ const kueryFilterArgs = createPointInTimeFinder.mock.calls[0][0].filter.arguments;
+ expect(kueryFilterArgs).toHaveLength(2);
+ const typeAndIdFilters = kueryFilterArgs[1].arguments;
+ expect(typeAndIdFilters).toHaveLength(1);
+ const typeAndIdFilter = typeAndIdFilters[0].arguments;
+ expect(typeAndIdFilter).toEqual([
+ expect.objectContaining({
+ arguments: expect.arrayContaining([{ type: 'literal', value: obj1.type }]),
+ }),
+ expect.objectContaining({
+ arguments: expect.arrayContaining([{ type: 'literal', value: obj1.id }]),
+ }),
+ ]);
+ expect(pointInTimeFinder.find).toHaveBeenCalledTimes(1);
+ expect(pointInTimeFinder.close).toHaveBeenCalledTimes(2);
+ });
+
+ it('does not search at all if all objects that have an empty spaces array (the object does not exist, or we are not sure)', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const params = setup([obj1]);
+ mockMgetResults({ found: false }); // results for obj1
+
+ await collectMultiNamespaceReferences(params);
+ expect(params.createPointInTimeFinder).not.toHaveBeenCalled();
+ });
+
+ it('handles PointInTimeFinder.find errors', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const params = setup([obj1]);
+ mockMgetResults({ found: true }); // results for obj1
+ savedObjectsMock.find.mockRejectedValue(new Error('Oh no!'));
+
+ await expect(() => collectMultiNamespaceReferences(params)).rejects.toThrow(
+ 'Failed to retrieve legacy URL aliases: Oh no!'
+ );
+ expect(createPointInTimeFinder).toHaveBeenCalledTimes(1);
+ expect(pointInTimeFinder.find).toHaveBeenCalledTimes(1);
+ expect(pointInTimeFinder.close).toHaveBeenCalledTimes(2); // we still close the point-in-time, even though the search failed
+ });
+
+ it('handles PointInTimeFinder.close errors', async () => {
+ const obj1 = { type: MULTI_NAMESPACE_OBJ_TYPE_1, id: 'id-1' };
+ const params = setup([obj1]);
+ mockMgetResults({ found: true }); // results for obj1
+ savedObjectsMock.closePointInTime.mockRejectedValue(new Error('Oh no!'));
+
+ await expect(() => collectMultiNamespaceReferences(params)).rejects.toThrow(
+ 'Failed to retrieve legacy URL aliases: Oh no!'
+ );
+ expect(createPointInTimeFinder).toHaveBeenCalledTimes(1);
+ expect(pointInTimeFinder.find).toHaveBeenCalledTimes(1);
+ expect(pointInTimeFinder.close).toHaveBeenCalledTimes(2);
+ });
+ });
+});
diff --git a/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts b/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts
new file mode 100644
index 0000000000000..43923695f6548
--- /dev/null
+++ b/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts
@@ -0,0 +1,310 @@
+/*
+ * Copyright 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.
+ */
+
+// @ts-expect-error no ts
+import { esKuery } from '../../es_query';
+
+import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../object_types';
+import type { ISavedObjectTypeRegistry } from '../../saved_objects_type_registry';
+import type { SavedObjectsSerializer } from '../../serialization';
+import type { SavedObject, SavedObjectsBaseOptions } from '../../types';
+import { getRootFields } from './included_fields';
+import { getSavedObjectFromSource, rawDocExistsInNamespace } from './internal_utils';
+import type {
+ ISavedObjectsPointInTimeFinder,
+ SavedObjectsCreatePointInTimeFinderOptions,
+} from './point_in_time_finder';
+import type { RepositoryEsClient } from './repository_es_client';
+
+/**
+ * When we collect an object's outbound references, we will only go a maximum of this many levels deep before we throw an error.
+ */
+const MAX_REFERENCE_GRAPH_DEPTH = 20;
+
+/**
+ * How many aliases to search for per page. This is smaller than the PointInTimeFinder's default of 1000. We specify 100 for the page count
+ * because this is a relatively unimportant operation, and we want to avoid blocking the Elasticsearch thread pool for longer than
+ * necessary.
+ */
+const ALIAS_SEARCH_PER_PAGE = 100;
+
+/**
+ * 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: 'multiple'` or `namespaceType: 'multiple-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: 'multiple'`).
+ *
+ * @public
+ */
+export interface SavedObjectsCollectMultiNamespaceReferencesObject {
+ id: string;
+ type: string;
+}
+
+/**
+ * Options for collecting references.
+ *
+ * @public
+ */
+export interface SavedObjectsCollectMultiNamespaceReferencesOptions
+ extends SavedObjectsBaseOptions {
+ /** Optional purpose used to determine filtering and authorization checks; default is 'collectMultiNamespaceReferences' */
+ purpose?: 'collectMultiNamespaceReferences' | 'updateObjectsSpaces';
+}
+
+/**
+ * A returned input object or one of its references, with additional context.
+ *
+ * @public
+ */
+export interface SavedObjectReferenceWithContext {
+ /** The type of the referenced object */
+ type: string;
+ /** The ID of the referenced object */
+ id: string;
+ /** The space(s) that the referenced object exists in */
+ spaces: 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
+ */
+ inboundReferences: Array<{
+ /** The type of the object that has the inbound reference */
+ type: string;
+ /** The ID of the object that has the inbound reference */
+ id: string;
+ /** The name of the inbound reference */
+ name: string;
+ }>;
+ /** Whether or not this object or reference is missing */
+ isMissing?: boolean;
+ /** The space(s) that legacy URL aliases matching this type/id exist in */
+ spacesWithMatchingAliases?: string[];
+}
+
+/**
+ * The response when object references are collected.
+ *
+ * @public
+ */
+export interface SavedObjectsCollectMultiNamespaceReferencesResponse {
+ objects: SavedObjectReferenceWithContext[];
+}
+
+/**
+ * Parameters for the collectMultiNamespaceReferences function.
+ *
+ * @internal
+ */
+export interface CollectMultiNamespaceReferencesParams {
+ registry: ISavedObjectTypeRegistry;
+ allowedTypes: string[];
+ client: RepositoryEsClient;
+ serializer: SavedObjectsSerializer;
+ getIndexForType: (type: string) => string;
+ createPointInTimeFinder: (
+ findOptions: SavedObjectsCreatePointInTimeFinderOptions
+ ) => ISavedObjectsPointInTimeFinder;
+ objects: SavedObjectsCollectMultiNamespaceReferencesObject[];
+ options?: SavedObjectsCollectMultiNamespaceReferencesOptions;
+}
+
+/**
+ * Gets all references and transitive references of the given objects. Ignores any object and/or reference that is not a multi-namespace
+ * type.
+ */
+export async function collectMultiNamespaceReferences(
+ params: CollectMultiNamespaceReferencesParams
+): Promise {
+ const { createPointInTimeFinder, objects } = params;
+ if (!objects.length) {
+ return { objects: [] };
+ }
+
+ const { objectMap, inboundReferencesMap } = await getObjectsAndReferences(params);
+ const objectsWithContext = Array.from(
+ inboundReferencesMap.entries()
+ ).map(([referenceKey, referenceVal]) => {
+ const inboundReferences = Array.from(referenceVal.entries()).map(([objectKey, name]) => {
+ const { type, id } = parseKey(objectKey);
+ return { type, id, name };
+ });
+ const { type, id } = parseKey(referenceKey);
+ const object = objectMap.get(referenceKey);
+ const spaces = object?.namespaces ?? [];
+ return { type, id, spaces, inboundReferences, ...(object === null && { isMissing: true }) };
+ });
+
+ const aliasesMap = await checkLegacyUrlAliases(createPointInTimeFinder, objectsWithContext);
+ const results = objectsWithContext.map((obj) => {
+ const key = getKey(obj);
+ const val = aliasesMap.get(key);
+ const spacesWithMatchingAliases = val && Array.from(val);
+ return { ...obj, spacesWithMatchingAliases };
+ });
+
+ return {
+ objects: results,
+ };
+}
+
+/**
+ * Recursively fetches objects and their references, returning a map of the retrieved objects and a map of all inbound references.
+ */
+async function getObjectsAndReferences({
+ registry,
+ allowedTypes,
+ client,
+ serializer,
+ getIndexForType,
+ objects,
+ options = {},
+}: CollectMultiNamespaceReferencesParams) {
+ const { namespace, purpose } = options;
+ const inboundReferencesMap = objects.reduce(
+ // Add the input objects to the references map so they are returned with the results, even if they have no inbound references
+ (acc, cur) => acc.set(getKey(cur), new Map()),
+ new Map>()
+ );
+ const objectMap = new Map();
+
+ const rootFields = getRootFields();
+ const makeBulkGetDocs = (objectsToGet: SavedObjectsCollectMultiNamespaceReferencesObject[]) =>
+ objectsToGet.map(({ type, id }) => ({
+ _id: serializer.generateRawId(undefined, type, id),
+ _index: getIndexForType(type),
+ _source: rootFields, // Optimized to only retrieve root fields (ignoring type-specific fields)
+ }));
+ const validObjectTypesFilter = ({ type }: SavedObjectsCollectMultiNamespaceReferencesObject) =>
+ allowedTypes.includes(type) &&
+ (purpose === 'updateObjectsSpaces'
+ ? registry.isShareable(type)
+ : registry.isMultiNamespace(type));
+
+ let bulkGetObjects = objects.filter(validObjectTypesFilter);
+ let count = 0; // this is a circuit-breaker to ensure we don't hog too many resources; we should never have an object graph this deep
+ while (bulkGetObjects.length) {
+ if (count >= MAX_REFERENCE_GRAPH_DEPTH) {
+ throw new Error(
+ `Exceeded maximum reference graph depth of ${MAX_REFERENCE_GRAPH_DEPTH} objects!`
+ );
+ }
+ const bulkGetResponse = await client.mget(
+ { body: { docs: makeBulkGetDocs(bulkGetObjects) } },
+ { ignore: [404] }
+ );
+ const newObjectsToGet = new Set();
+ for (let i = 0; i < bulkGetObjects.length; i++) {
+ // For every element in bulkGetObjects, there should be a matching element in bulkGetResponse.body.docs
+ const { type, id } = bulkGetObjects[i];
+ const objectKey = getKey({ type, id });
+ const doc = bulkGetResponse.body.docs[i];
+ // @ts-expect-error MultiGetHit._source is optional
+ if (!doc.found || !rawDocExistsInNamespace(registry, doc, namespace)) {
+ objectMap.set(objectKey, null);
+ continue;
+ }
+ // @ts-expect-error MultiGetHit._source is optional
+ const object = getSavedObjectFromSource(registry, type, id, doc);
+ objectMap.set(objectKey, object);
+ for (const reference of object.references) {
+ if (!validObjectTypesFilter(reference)) {
+ continue;
+ }
+ const referenceKey = getKey(reference);
+ const referenceVal = inboundReferencesMap.get(referenceKey) ?? new Map();
+ if (!referenceVal.has(objectKey)) {
+ inboundReferencesMap.set(referenceKey, referenceVal.set(objectKey, reference.name));
+ }
+ if (!objectMap.has(referenceKey)) {
+ newObjectsToGet.add(referenceKey);
+ }
+ }
+ }
+ bulkGetObjects = Array.from(newObjectsToGet).map((key) => parseKey(key));
+ count++;
+ }
+
+ return { objectMap, inboundReferencesMap };
+}
+
+/**
+ * Fetches all legacy URL aliases that match the given objects, returning a map of the matching aliases and what space(s) they exist in.
+ */
+async function checkLegacyUrlAliases(
+ createPointInTimeFinder: (
+ findOptions: SavedObjectsCreatePointInTimeFinderOptions
+ ) => ISavedObjectsPointInTimeFinder,
+ objects: SavedObjectReferenceWithContext[]
+) {
+ const filteredObjects = objects.filter(({ spaces }) => spaces.length !== 0);
+ if (!filteredObjects.length) {
+ return new Map>();
+ }
+ const filter = createAliasKueryFilter(filteredObjects);
+ const finder = createPointInTimeFinder({
+ type: LEGACY_URL_ALIAS_TYPE,
+ perPage: ALIAS_SEARCH_PER_PAGE,
+ filter,
+ });
+ const aliasesMap = new Map>();
+ let error: Error | undefined;
+ try {
+ for await (const { saved_objects: savedObjects } of finder.find()) {
+ for (const alias of savedObjects) {
+ const { sourceId, targetType, targetNamespace } = alias.attributes;
+ const key = getKey({ type: targetType, id: sourceId });
+ const val = aliasesMap.get(key) ?? new Set();
+ val.add(targetNamespace);
+ aliasesMap.set(key, val);
+ }
+ }
+ } catch (e) {
+ error = e;
+ }
+
+ try {
+ await finder.close();
+ } catch (e) {
+ if (!error) {
+ error = e;
+ }
+ }
+
+ if (error) {
+ throw new Error(`Failed to retrieve legacy URL aliases: ${error.message}`);
+ }
+ return aliasesMap;
+}
+
+function createAliasKueryFilter(objects: SavedObjectReferenceWithContext[]) {
+ const { buildNode } = esKuery.nodeTypes.function;
+ const kueryNodes = objects.reduce((acc, { type, id }) => {
+ const match1 = buildNode('is', `${LEGACY_URL_ALIAS_TYPE}.attributes.targetType`, type);
+ const match2 = buildNode('is', `${LEGACY_URL_ALIAS_TYPE}.attributes.sourceId`, id);
+ acc.push(buildNode('and', [match1, match2]));
+ return acc;
+ }, []);
+ return buildNode('and', [
+ buildNode('not', buildNode('is', `${LEGACY_URL_ALIAS_TYPE}.attributes.disabled`, true)), // ignore aliases that have been disabled
+ buildNode('or', kueryNodes),
+ ]);
+}
+
+/** Takes an object with a `type` and `id` field and returns a key string */
+function getKey({ type, id }: { type: string; id: string }) {
+ return `${type}:${id}`;
+}
+
+/** Parses a 'type:id' key string and returns an object with a `type` field and an `id` field */
+function parseKey(key: string) {
+ const type = key.slice(0, key.indexOf(':'));
+ const id = key.slice(type.length + 1);
+ return { type, id };
+}
diff --git a/src/core/server/saved_objects/service/lib/included_fields.test.ts b/src/core/server/saved_objects/service/lib/included_fields.test.ts
index 334cda91129f3..51c431b1c6b3b 100644
--- a/src/core/server/saved_objects/service/lib/included_fields.test.ts
+++ b/src/core/server/saved_objects/service/lib/included_fields.test.ts
@@ -6,125 +6,63 @@
* Side Public License, v 1.
*/
-import { includedFields } from './included_fields';
+import { getRootFields, includedFields } from './included_fields';
-const BASE_FIELD_COUNT = 10;
+describe('getRootFields', () => {
+ it('returns copy of root fields', () => {
+ const fields = getRootFields();
+ expect(fields).toMatchInlineSnapshot(`
+ Array [
+ "namespace",
+ "namespaces",
+ "type",
+ "references",
+ "migrationVersion",
+ "coreMigrationVersion",
+ "updated_at",
+ "originId",
+ ]
+ `);
+ });
+});
describe('includedFields', () => {
+ const rootFields = getRootFields();
+
it('returns undefined if fields are not provided', () => {
expect(includedFields()).toBe(undefined);
});
- it('accepts type string', () => {
+ it('accepts type and field as string', () => {
const fields = includedFields('config', 'foo');
- expect(fields).toHaveLength(BASE_FIELD_COUNT);
- expect(fields).toContain('type');
+ expect(fields).toEqual(['config.foo', ...rootFields, 'foo']);
});
- it('accepts type as string array', () => {
+ it('accepts type as array and field as string', () => {
const fields = includedFields(['config', 'secret'], 'foo');
- expect(fields).toMatchInlineSnapshot(`
-Array [
- "config.foo",
- "secret.foo",
- "namespace",
- "namespaces",
- "type",
- "references",
- "migrationVersion",
- "coreMigrationVersion",
- "updated_at",
- "originId",
- "foo",
-]
-`);
- });
-
- it('accepts field as string', () => {
- const fields = includedFields('config', 'foo');
- expect(fields).toHaveLength(BASE_FIELD_COUNT);
- expect(fields).toContain('config.foo');
+ expect(fields).toEqual(['config.foo', 'secret.foo', ...rootFields, 'foo']);
});
- it('accepts fields as an array', () => {
+ it('accepts type as string and field as array', () => {
const fields = includedFields('config', ['foo', 'bar']);
-
- expect(fields).toHaveLength(BASE_FIELD_COUNT + 2);
- expect(fields).toContain('config.foo');
- expect(fields).toContain('config.bar');
+ expect(fields).toEqual(['config.foo', 'config.bar', ...rootFields, 'foo', 'bar']);
});
- it('accepts type as string array and fields as string array', () => {
+ it('accepts type as array and field as array', () => {
const fields = includedFields(['config', 'secret'], ['foo', 'bar']);
- expect(fields).toMatchInlineSnapshot(`
-Array [
- "config.foo",
- "config.bar",
- "secret.foo",
- "secret.bar",
- "namespace",
- "namespaces",
- "type",
- "references",
- "migrationVersion",
- "coreMigrationVersion",
- "updated_at",
- "originId",
- "foo",
- "bar",
-]
-`);
- });
-
- it('includes namespace', () => {
- const fields = includedFields('config', 'foo');
- expect(fields).toHaveLength(BASE_FIELD_COUNT);
- expect(fields).toContain('namespace');
- });
-
- it('includes namespaces', () => {
- const fields = includedFields('config', 'foo');
- expect(fields).toHaveLength(BASE_FIELD_COUNT);
- expect(fields).toContain('namespaces');
- });
-
- it('includes references', () => {
- const fields = includedFields('config', 'foo');
- expect(fields).toHaveLength(BASE_FIELD_COUNT);
- expect(fields).toContain('references');
- });
-
- it('includes migrationVersion', () => {
- const fields = includedFields('config', 'foo');
- expect(fields).toHaveLength(BASE_FIELD_COUNT);
- expect(fields).toContain('migrationVersion');
- });
-
- it('includes updated_at', () => {
- const fields = includedFields('config', 'foo');
- expect(fields).toHaveLength(BASE_FIELD_COUNT);
- expect(fields).toContain('updated_at');
- });
-
- it('includes originId', () => {
- const fields = includedFields('config', 'foo');
- expect(fields).toHaveLength(BASE_FIELD_COUNT);
- expect(fields).toContain('originId');
+ expect(fields).toEqual([
+ 'config.foo',
+ 'config.bar',
+ 'secret.foo',
+ 'secret.bar',
+ ...rootFields,
+ 'foo',
+ 'bar',
+ ]);
});
it('uses wildcard when type is not provided', () => {
const fields = includedFields(undefined, 'foo');
- expect(fields).toHaveLength(BASE_FIELD_COUNT);
- expect(fields).toContain('*.foo');
- });
-
- describe('v5 compatibility', () => {
- it('includes legacy field path', () => {
- const fields = includedFields('config', ['foo', 'bar']);
-
- expect(fields).toHaveLength(BASE_FIELD_COUNT + 2);
- expect(fields).toContain('foo');
- expect(fields).toContain('bar');
- });
+ expect(fields).toEqual(['*.foo', ...rootFields, 'foo']);
});
});
diff --git a/src/core/server/saved_objects/service/lib/included_fields.ts b/src/core/server/saved_objects/service/lib/included_fields.ts
index cef83f103ec53..9613d8f6a4a41 100644
--- a/src/core/server/saved_objects/service/lib/included_fields.ts
+++ b/src/core/server/saved_objects/service/lib/included_fields.ts
@@ -9,6 +9,22 @@
function toArray(value: string | string[]): string[] {
return typeof value === 'string' ? [value] : value;
}
+
+const ROOT_FIELDS = [
+ 'namespace',
+ 'namespaces',
+ 'type',
+ 'references',
+ 'migrationVersion',
+ 'coreMigrationVersion',
+ 'updated_at',
+ 'originId',
+];
+
+export function getRootFields() {
+ return [...ROOT_FIELDS];
+}
+
/**
* Provides an array of paths for ES source filtering
*/
@@ -28,13 +44,6 @@ export function includedFields(
.reduce((acc: string[], t) => {
return [...acc, ...sourceFields.map((f) => `${t}.${f}`)];
}, [])
- .concat('namespace')
- .concat('namespaces')
- .concat('type')
- .concat('references')
- .concat('migrationVersion')
- .concat('coreMigrationVersion')
- .concat('updated_at')
- .concat('originId')
+ .concat(ROOT_FIELDS)
.concat(fields); // v5 compatibility
}
diff --git a/src/core/server/saved_objects/service/lib/index.ts b/src/core/server/saved_objects/service/lib/index.ts
index 09bce81b14c39..661d04b8a0b2a 100644
--- a/src/core/server/saved_objects/service/lib/index.ts
+++ b/src/core/server/saved_objects/service/lib/index.ts
@@ -27,3 +27,17 @@ export type {
export { SavedObjectsErrorHelpers } from './errors';
export { SavedObjectsUtils } from './utils';
+
+export type {
+ SavedObjectsCollectMultiNamespaceReferencesObject,
+ SavedObjectsCollectMultiNamespaceReferencesOptions,
+ SavedObjectReferenceWithContext,
+ SavedObjectsCollectMultiNamespaceReferencesResponse,
+} from './collect_multi_namespace_references';
+
+export type {
+ SavedObjectsUpdateObjectsSpacesObject,
+ SavedObjectsUpdateObjectsSpacesOptions,
+ SavedObjectsUpdateObjectsSpacesResponse,
+ SavedObjectsUpdateObjectsSpacesResponseObject,
+} from './update_objects_spaces';
diff --git a/src/core/server/saved_objects/service/lib/internal_utils.test.ts b/src/core/server/saved_objects/service/lib/internal_utils.test.ts
new file mode 100644
index 0000000000000..d1fd067990f07
--- /dev/null
+++ b/src/core/server/saved_objects/service/lib/internal_utils.test.ts
@@ -0,0 +1,243 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { typeRegistryMock } from '../../saved_objects_type_registry.mock';
+import type { SavedObjectsRawDoc } from '../../serialization';
+import { encodeHitVersion } from '../../version';
+import {
+ getBulkOperationError,
+ getSavedObjectFromSource,
+ rawDocExistsInNamespace,
+} from './internal_utils';
+import { ALL_NAMESPACES_STRING } from './utils';
+
+describe('#getBulkOperationError', () => {
+ const type = 'obj-type';
+ const id = 'obj-id';
+
+ it('returns index not found error', () => {
+ const rawResponse = {
+ status: 404,
+ error: { type: 'index_not_found_exception', reason: 'some-reason', index: 'some-index' },
+ };
+
+ const result = getBulkOperationError(type, id, rawResponse);
+ expect(result).toEqual({
+ error: 'Internal Server Error',
+ message: 'An internal server error occurred', // TODO: this error payload is not very helpful to consumers, can we change it?
+ statusCode: 500,
+ });
+ });
+
+ it('returns generic not found error', () => {
+ const rawResponse = {
+ status: 404,
+ error: { type: 'anything', reason: 'some-reason', index: 'some-index' },
+ };
+
+ const result = getBulkOperationError(type, id, rawResponse);
+ expect(result).toEqual({
+ error: 'Not Found',
+ message: `Saved object [${type}/${id}] not found`,
+ statusCode: 404,
+ });
+ });
+
+ it('returns conflict error', () => {
+ const rawResponse = {
+ status: 409,
+ error: { type: 'anything', reason: 'some-reason', index: 'some-index' },
+ };
+
+ const result = getBulkOperationError(type, id, rawResponse);
+ expect(result).toEqual({
+ error: 'Conflict',
+ message: `Saved object [${type}/${id}] conflict`,
+ statusCode: 409,
+ });
+ });
+
+ it('returns an unexpected result error', () => {
+ const rawResponse = {
+ status: 123, // any status
+ error: { type: 'anything', reason: 'some-reason', index: 'some-index' },
+ };
+
+ const result = getBulkOperationError(type, id, rawResponse);
+ expect(result).toEqual({
+ error: 'Internal Server Error',
+ message: `Unexpected bulk response [${rawResponse.status}] ${rawResponse.error.type}: ${rawResponse.error.reason}`,
+ statusCode: 500,
+ });
+ });
+});
+
+describe('#getSavedObjectFromSource', () => {
+ const NAMESPACE_AGNOSTIC_TYPE = 'agnostic-type';
+ const NON_NAMESPACE_AGNOSTIC_TYPE = 'other-type';
+
+ const registry = typeRegistryMock.create();
+ registry.isNamespaceAgnostic.mockImplementation((type) => type === NAMESPACE_AGNOSTIC_TYPE);
+
+ const id = 'obj-id';
+ const _seq_no = 1;
+ const _primary_term = 1;
+ const attributes = { foo: 'bar' };
+ const references = [{ type: 'ref-type', id: 'ref-id', name: 'ref-name' }];
+ const migrationVersion = { foo: 'migrationVersion' };
+ const coreMigrationVersion = 'coreMigrationVersion';
+ const originId = 'originId';
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const updated_at = 'updatedAt';
+
+ function createRawDoc(
+ type: string,
+ namespaceAttrs?: { namespace?: string; namespaces?: string[] }
+ ) {
+ return {
+ // other fields exist on the raw document, but they are not relevant to these test cases
+ _seq_no,
+ _primary_term,
+ _source: {
+ type,
+ [type]: attributes,
+ references,
+ migrationVersion,
+ coreMigrationVersion,
+ originId,
+ updated_at,
+ ...namespaceAttrs,
+ },
+ };
+ }
+
+ it('returns object with expected attributes', () => {
+ const type = 'any-type';
+ const doc = createRawDoc(type);
+
+ const result = getSavedObjectFromSource(registry, type, id, doc);
+ expect(result).toEqual({
+ attributes,
+ coreMigrationVersion,
+ id,
+ migrationVersion,
+ namespaces: expect.anything(), // see specific test cases below
+ originId,
+ references,
+ type,
+ updated_at,
+ version: encodeHitVersion(doc),
+ });
+ });
+
+ it('returns object with empty namespaces array when type is namespace-agnostic', () => {
+ const type = NAMESPACE_AGNOSTIC_TYPE;
+ const doc = createRawDoc(type);
+
+ const result = getSavedObjectFromSource(registry, type, id, doc);
+ expect(result).toEqual(expect.objectContaining({ namespaces: [] }));
+ });
+
+ it('returns object with namespaces when type is not namespace-agnostic and namespaces array is defined', () => {
+ const type = NON_NAMESPACE_AGNOSTIC_TYPE;
+ const namespaces = ['foo-ns', 'bar-ns'];
+ const doc = createRawDoc(type, { namespaces });
+
+ const result = getSavedObjectFromSource(registry, type, id, doc);
+ expect(result).toEqual(expect.objectContaining({ namespaces }));
+ });
+
+ it('derives namespaces from namespace attribute when type is not namespace-agnostic and namespaces array is not defined', () => {
+ // Deriving namespaces from the namespace attribute is an implementation detail of SavedObjectsUtils.namespaceIdToString().
+ // However, these test cases assertions are written out anyway for clarity.
+ const type = NON_NAMESPACE_AGNOSTIC_TYPE;
+ const doc1 = createRawDoc(type, { namespace: undefined });
+ const doc2 = createRawDoc(type, { namespace: 'foo-ns' });
+
+ const result1 = getSavedObjectFromSource(registry, type, id, doc1);
+ const result2 = getSavedObjectFromSource(registry, type, id, doc2);
+ expect(result1).toEqual(expect.objectContaining({ namespaces: ['default'] }));
+ expect(result2).toEqual(expect.objectContaining({ namespaces: ['foo-ns'] }));
+ });
+});
+
+describe('#rawDocExistsInNamespace', () => {
+ const SINGLE_NAMESPACE_TYPE = 'single-type';
+ const MULTI_NAMESPACE_TYPE = 'multi-type';
+ const NAMESPACE_AGNOSTIC_TYPE = 'agnostic-type';
+
+ const registry = typeRegistryMock.create();
+ registry.isSingleNamespace.mockImplementation((type) => type === SINGLE_NAMESPACE_TYPE);
+ registry.isMultiNamespace.mockImplementation((type) => type === MULTI_NAMESPACE_TYPE);
+ registry.isNamespaceAgnostic.mockImplementation((type) => type === NAMESPACE_AGNOSTIC_TYPE);
+
+ function createRawDoc(
+ type: string,
+ namespaceAttrs: { namespace?: string; namespaces?: string[] }
+ ) {
+ return {
+ // other fields exist on the raw document, but they are not relevant to these test cases
+ _source: {
+ type,
+ ...namespaceAttrs,
+ },
+ } as SavedObjectsRawDoc;
+ }
+
+ describe('single-namespace type', () => {
+ it('returns true regardless of namespace or namespaces fields', () => {
+ // Technically, a single-namespace type does not exist in a space unless it has a namespace prefix in its raw ID and a matching
+ // 'namespace' field. However, historically we have not enforced the latter, we have just relied on searching for and deserializing
+ // documents with the correct namespace prefix. We may revisit this in the future.
+ const doc1 = createRawDoc(SINGLE_NAMESPACE_TYPE, { namespace: 'some-space' }); // the namespace field is ignored
+ const doc2 = createRawDoc(SINGLE_NAMESPACE_TYPE, { namespaces: ['some-space'] }); // the namespaces field is ignored
+ expect(rawDocExistsInNamespace(registry, doc1, undefined)).toBe(true);
+ expect(rawDocExistsInNamespace(registry, doc1, 'some-space')).toBe(true);
+ expect(rawDocExistsInNamespace(registry, doc1, 'other-space')).toBe(true);
+ expect(rawDocExistsInNamespace(registry, doc2, undefined)).toBe(true);
+ expect(rawDocExistsInNamespace(registry, doc2, 'some-space')).toBe(true);
+ expect(rawDocExistsInNamespace(registry, doc2, 'other-space')).toBe(true);
+ });
+ });
+
+ describe('multi-namespace type', () => {
+ const docInDefaultSpace = createRawDoc(MULTI_NAMESPACE_TYPE, { namespaces: ['default'] });
+ const docInSomeSpace = createRawDoc(MULTI_NAMESPACE_TYPE, { namespaces: ['some-space'] });
+ const docInAllSpaces = createRawDoc(MULTI_NAMESPACE_TYPE, {
+ namespaces: [ALL_NAMESPACES_STRING],
+ });
+ const docInNoSpace = createRawDoc(MULTI_NAMESPACE_TYPE, { namespaces: [] });
+
+ it('returns true when the document namespaces matches', () => {
+ expect(rawDocExistsInNamespace(registry, docInDefaultSpace, undefined)).toBe(true);
+ expect(rawDocExistsInNamespace(registry, docInAllSpaces, undefined)).toBe(true);
+ expect(rawDocExistsInNamespace(registry, docInSomeSpace, 'some-space')).toBe(true);
+ expect(rawDocExistsInNamespace(registry, docInAllSpaces, 'some-space')).toBe(true);
+ expect(rawDocExistsInNamespace(registry, docInAllSpaces, 'other-space')).toBe(true);
+ });
+
+ it('returns false when the document namespace does not match', () => {
+ expect(rawDocExistsInNamespace(registry, docInDefaultSpace, 'other-space')).toBe(false);
+ expect(rawDocExistsInNamespace(registry, docInSomeSpace, 'other-space')).toBe(false);
+ expect(rawDocExistsInNamespace(registry, docInNoSpace, 'other-space')).toBe(false);
+ });
+ });
+
+ describe('namespace-agnostic type', () => {
+ it('returns true regardless of namespace or namespaces fields', () => {
+ const doc1 = createRawDoc(NAMESPACE_AGNOSTIC_TYPE, { namespace: 'some-space' }); // the namespace field is ignored
+ const doc2 = createRawDoc(NAMESPACE_AGNOSTIC_TYPE, { namespaces: ['some-space'] }); // the namespaces field is ignored
+ expect(rawDocExistsInNamespace(registry, doc1, undefined)).toBe(true);
+ expect(rawDocExistsInNamespace(registry, doc1, 'some-space')).toBe(true);
+ expect(rawDocExistsInNamespace(registry, doc1, 'other-space')).toBe(true);
+ expect(rawDocExistsInNamespace(registry, doc2, undefined)).toBe(true);
+ expect(rawDocExistsInNamespace(registry, doc2, 'some-space')).toBe(true);
+ expect(rawDocExistsInNamespace(registry, doc2, 'other-space')).toBe(true);
+ });
+ });
+});
diff --git a/src/core/server/saved_objects/service/lib/internal_utils.ts b/src/core/server/saved_objects/service/lib/internal_utils.ts
new file mode 100644
index 0000000000000..feaaea15649c7
--- /dev/null
+++ b/src/core/server/saved_objects/service/lib/internal_utils.ts
@@ -0,0 +1,143 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import type { Payload } from '@hapi/boom';
+import type { ISavedObjectTypeRegistry } from '../../saved_objects_type_registry';
+import type { SavedObjectsRawDoc, SavedObjectsRawDocSource } from '../../serialization';
+import type { SavedObject } from '../../types';
+import { decodeRequestVersion, encodeHitVersion } from '../../version';
+import { SavedObjectsErrorHelpers } from './errors';
+import { ALL_NAMESPACES_STRING, SavedObjectsUtils } from './utils';
+
+/**
+ * Checks the raw response of a bulk operation and returns an error if necessary.
+ *
+ * @param type
+ * @param id
+ * @param rawResponse
+ *
+ * @internal
+ */
+export function getBulkOperationError(
+ type: string,
+ id: string,
+ rawResponse: {
+ status: number;
+ error?: { type: string; reason: string; index: string };
+ // Other fields are present on a bulk operation result but they are irrelevant for this function
+ }
+): Payload | undefined {
+ const { status, error } = rawResponse;
+ if (error) {
+ switch (status) {
+ case 404:
+ return error.type === 'index_not_found_exception'
+ ? SavedObjectsErrorHelpers.createIndexAliasNotFoundError(error.index).output.payload
+ : SavedObjectsErrorHelpers.createGenericNotFoundError(type, id).output.payload;
+ case 409:
+ return SavedObjectsErrorHelpers.createConflictError(type, id).output.payload;
+ default:
+ return {
+ error: 'Internal Server Error',
+ message: `Unexpected bulk response [${status}] ${error.type}: ${error.reason}`,
+ statusCode: 500,
+ };
+ }
+ }
+}
+
+/**
+ * Returns an object with the expected version properties. This facilitates Elasticsearch's Optimistic Concurrency Control.
+ *
+ * @param version Optional version specified by the consumer.
+ * @param document Optional existing document that was obtained in a preflight operation.
+ *
+ * @internal
+ */
+export function getExpectedVersionProperties(version?: string, document?: SavedObjectsRawDoc) {
+ if (version) {
+ return decodeRequestVersion(version);
+ } else if (document) {
+ return {
+ if_seq_no: document._seq_no,
+ if_primary_term: document._primary_term,
+ };
+ }
+ return {};
+}
+
+/**
+ * Gets a saved object from a raw ES document.
+ *
+ * @param registry
+ * @param type
+ * @param id
+ * @param doc
+ *
+ * @internal
+ */
+export function getSavedObjectFromSource(
+ registry: ISavedObjectTypeRegistry,
+ type: string,
+ id: string,
+ doc: { _seq_no?: number; _primary_term?: number; _source: SavedObjectsRawDocSource }
+): SavedObject {
+ const { originId, updated_at: updatedAt } = doc._source;
+
+ let namespaces: string[] = [];
+ if (!registry.isNamespaceAgnostic(type)) {
+ namespaces = doc._source.namespaces ?? [
+ SavedObjectsUtils.namespaceIdToString(doc._source.namespace),
+ ];
+ }
+
+ return {
+ id,
+ type,
+ namespaces,
+ ...(originId && { originId }),
+ ...(updatedAt && { updated_at: updatedAt }),
+ version: encodeHitVersion(doc),
+ attributes: doc._source[type],
+ references: doc._source.references || [],
+ migrationVersion: doc._source.migrationVersion,
+ coreMigrationVersion: doc._source.coreMigrationVersion,
+ };
+}
+
+/**
+ * Check to ensure that a raw document exists in a namespace. If the document is not a multi-namespace type, then this returns `true` as
+ * we rely on the guarantees of the document ID format. If the document is a multi-namespace type, this checks to ensure that the
+ * document's `namespaces` value includes the string representation of the given namespace.
+ *
+ * WARNING: This should only be used for documents that were retrieved from Elasticsearch. Otherwise, the guarantees of the document ID
+ * format mentioned above do not apply.
+ *
+ * @param registry
+ * @param raw
+ * @param namespace
+ */
+export function rawDocExistsInNamespace(
+ registry: ISavedObjectTypeRegistry,
+ raw: SavedObjectsRawDoc,
+ namespace: string | undefined
+) {
+ const rawDocType = raw._source.type;
+
+ // if the type is namespace isolated, or namespace agnostic, we can continue to rely on the guarantees
+ // of the document ID format and don't need to check this
+ if (!registry.isMultiNamespace(rawDocType)) {
+ return true;
+ }
+
+ const namespaces = raw._source.namespaces;
+ const existsInNamespace =
+ namespaces?.includes(SavedObjectsUtils.namespaceIdToString(namespace)) ||
+ namespaces?.includes(ALL_NAMESPACES_STRING);
+ return existsInNamespace ?? false;
+}
diff --git a/src/core/server/saved_objects/service/lib/point_in_time_finder.ts b/src/core/server/saved_objects/service/lib/point_in_time_finder.ts
index 9a8dcceafebb2..f0ed943c585e5 100644
--- a/src/core/server/saved_objects/service/lib/point_in_time_finder.ts
+++ b/src/core/server/saved_objects/service/lib/point_in_time_finder.ts
@@ -39,14 +39,14 @@ export interface PointInTimeFinderDependencies
}
/** @public */
-export interface ISavedObjectsPointInTimeFinder {
+export interface ISavedObjectsPointInTimeFinder {
/**
* 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: () => AsyncGenerator;
+ find: () => AsyncGenerator>;
/**
* Closes the Point-In-Time associated with this finder instance.
*
@@ -63,7 +63,8 @@ export interface ISavedObjectsPointInTimeFinder {
/**
* @internal
*/
-export class PointInTimeFinder implements ISavedObjectsPointInTimeFinder {
+export class PointInTimeFinder
+ implements ISavedObjectsPointInTimeFinder {
readonly #log: Logger;
readonly #client: PointInTimeFinderClient;
readonly #findOptions: SavedObjectsFindOptions;
@@ -162,7 +163,7 @@ export class PointInTimeFinder implements ISavedObjectsPointInTimeFinder {
searchAfter?: estypes.Id[];
}) {
try {
- return await this.#client.find({
+ return await this.#client.find({
// Sort fields are required to use searchAfter, so we set some defaults here
sortField: 'updated_at',
sortOrder: 'desc',
diff --git a/src/core/server/saved_objects/service/lib/repository.mock.ts b/src/core/server/saved_objects/service/lib/repository.mock.ts
index a2092e0571808..0e1426a58f8ae 100644
--- a/src/core/server/saved_objects/service/lib/repository.mock.ts
+++ b/src/core/server/saved_objects/service/lib/repository.mock.ts
@@ -24,11 +24,11 @@ const create = () => {
openPointInTimeForType: jest.fn().mockResolvedValue({ id: 'some_pit_id' }),
resolve: jest.fn(),
update: jest.fn(),
- addToNamespaces: jest.fn(),
- deleteFromNamespaces: jest.fn(),
deleteByNamespace: jest.fn(),
incrementCounter: jest.fn(),
removeReferencesTo: jest.fn(),
+ collectMultiNamespaceReferences: jest.fn(),
+ updateObjectsSpaces: jest.fn(),
};
mock.createPointInTimeFinder = savedObjectsPointInTimeFinderMock.create({
diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js
index 33754d0ad9661..22c40a547f419 100644
--- a/src/core/server/saved_objects/service/lib/repository.test.js
+++ b/src/core/server/saved_objects/service/lib/repository.test.js
@@ -6,7 +6,12 @@
* Side Public License, v 1.
*/
-import { pointInTimeFinderMock } from './repository.test.mock';
+import {
+ pointInTimeFinderMock,
+ mockCollectMultiNamespaceReferences,
+ mockGetBulkOperationError,
+ mockUpdateObjectsSpaces,
+} from './repository.test.mock';
import { SavedObjectsRepository } from './repository';
import * as getSearchDslNS from './search_dsl/search_dsl';
@@ -67,9 +72,9 @@ describe('SavedObjectsRepository', () => {
* This type has namespaceType: 'multiple-isolated'.
*
* That means that the object is serialized with a globally unique ID across namespaces. It also means that the object is NOT shareable
- * across namespaces. This distinction only matters when using the `addToNamespaces` and `deleteFromNamespaces` APIs, or when using the
- * `initialNamespaces` argument with the `create` and `bulkCreate` APIs. Those allow you to define or change what namespaces an object
- * exists in.
+ * across namespaces. This distinction only matters when using the `collectMultiNamespaceReferences` or `updateObjectsSpaces` APIs, or
+ * when using the `initialNamespaces` argument with the `create` and `bulkCreate` APIs. Those allow you to define or change what
+ * namespaces an object exists in.
*
* In a nutshell, this type is more restrictive than `MULTI_NAMESPACE_TYPE`, so we use `MULTI_NAMESPACE_ISOLATED_TYPE` for any test cases
* where `MULTI_NAMESPACE_TYPE` would also satisfy the test case.
@@ -295,164 +300,6 @@ describe('SavedObjectsRepository', () => {
references: [{ name: 'search_0', type: 'search', id: '123' }],
});
- describe('#addToNamespaces', () => {
- const id = 'some-id';
- const type = MULTI_NAMESPACE_TYPE;
- const currentNs1 = 'default';
- const currentNs2 = 'foo-namespace';
- const newNs1 = 'bar-namespace';
- const newNs2 = 'baz-namespace';
-
- const mockGetResponse = (type, id) => {
- // mock a document that exists in two namespaces
- const mockResponse = getMockGetResponse({ type, id });
- mockResponse._source.namespaces = [currentNs1, currentNs2];
- client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise(mockResponse)
- );
- };
-
- const addToNamespacesSuccess = async (type, id, namespaces, options) => {
- mockGetResponse(type, id);
- client.update.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({
- _id: `${type}:${id}`,
- ...mockVersionProps,
- result: 'updated',
- })
- );
- const result = await savedObjectsRepository.addToNamespaces(type, id, namespaces, options);
- expect(client.get).toHaveBeenCalledTimes(1);
- expect(client.update).toHaveBeenCalledTimes(1);
- return result;
- };
-
- describe('client calls', () => {
- it(`should use ES get action then update action`, async () => {
- await addToNamespacesSuccess(type, id, [newNs1, newNs2]);
- });
-
- it(`defaults to the version of the existing document`, async () => {
- await addToNamespacesSuccess(type, id, [newNs1, newNs2]);
- const versionProperties = {
- if_seq_no: mockVersionProps._seq_no,
- if_primary_term: mockVersionProps._primary_term,
- };
- expect(client.update).toHaveBeenCalledWith(
- expect.objectContaining(versionProperties),
- expect.anything()
- );
- });
-
- it(`accepts version`, async () => {
- await addToNamespacesSuccess(type, id, [newNs1, newNs2], {
- version: encodeHitVersion({ _seq_no: 100, _primary_term: 200 }),
- });
- expect(client.update).toHaveBeenCalledWith(
- expect.objectContaining({ if_seq_no: 100, if_primary_term: 200 }),
- expect.anything()
- );
- });
-
- it(`defaults to a refresh setting of wait_for`, async () => {
- await addToNamespacesSuccess(type, id, [newNs1, newNs2]);
- expect(client.update).toHaveBeenCalledWith(
- expect.objectContaining({ refresh: 'wait_for' }),
- expect.anything()
- );
- });
- });
-
- describe('errors', () => {
- const expectNotFoundError = async (type, id, namespaces, options) => {
- await expect(
- savedObjectsRepository.addToNamespaces(type, id, namespaces, options)
- ).rejects.toThrowError(createGenericNotFoundError(type, id));
- };
- const expectBadRequestError = async (type, id, namespaces, message) => {
- await expect(
- savedObjectsRepository.addToNamespaces(type, id, namespaces)
- ).rejects.toThrowError(createBadRequestError(message));
- };
-
- it(`throws when type is invalid`, async () => {
- await expectNotFoundError('unknownType', id, [newNs1, newNs2]);
- expect(client.update).not.toHaveBeenCalled();
- });
-
- it(`throws when type is hidden`, async () => {
- await expectNotFoundError(HIDDEN_TYPE, id, [newNs1, newNs2]);
- expect(client.update).not.toHaveBeenCalled();
- });
-
- it(`throws when type is not shareable`, async () => {
- const test = async (type) => {
- const message = `${type} doesn't support multiple namespaces`;
- await expectBadRequestError(type, id, [newNs1, newNs2], message);
- expect(client.update).not.toHaveBeenCalled();
- };
- await test('index-pattern');
- await test(MULTI_NAMESPACE_ISOLATED_TYPE);
- await test(NAMESPACE_AGNOSTIC_TYPE);
- });
-
- it(`throws when namespaces is an empty array`, async () => {
- const test = async (namespaces) => {
- const message = 'namespaces must be a non-empty array of strings';
- await expectBadRequestError(type, id, namespaces, message);
- expect(client.update).not.toHaveBeenCalled();
- };
- await test([]);
- });
-
- it(`throws when ES is unable to find the document during get`, async () => {
- client.get.mockResolvedValue(
- elasticsearchClientMock.createSuccessTransportRequestPromise({ found: false })
- );
- await expectNotFoundError(type, id, [newNs1, newNs2]);
- expect(client.get).toHaveBeenCalledTimes(1);
- });
-
- it(`throws when ES is unable to find the index during get`, async () => {
- client.get.mockResolvedValue(
- elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
- );
- await expectNotFoundError(type, id, [newNs1, newNs2]);
- expect(client.get).toHaveBeenCalledTimes(1);
- });
-
- it(`throws when the document exists, but not in this namespace`, async () => {
- mockGetResponse(type, id);
- await expectNotFoundError(type, id, [newNs1, newNs2], {
- namespace: 'some-other-namespace',
- });
- expect(client.get).toHaveBeenCalledTimes(1);
- });
-
- it(`throws when ES is unable to find the document during update`, async () => {
- mockGetResponse(type, id);
- client.update.mockResolvedValue(
- elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
- );
- await expectNotFoundError(type, id, [newNs1, newNs2]);
- expect(client.get).toHaveBeenCalledTimes(1);
- expect(client.update).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('returns', () => {
- it(`returns all existing and new namespaces on success`, async () => {
- const result = await addToNamespacesSuccess(type, id, [newNs1, newNs2]);
- expect(result).toEqual({ namespaces: [currentNs1, currentNs2, newNs1, newNs2] });
- });
-
- it(`succeeds when adding existing namespaces`, async () => {
- const result = await addToNamespacesSuccess(type, id, [currentNs1]);
- expect(result).toEqual({ namespaces: [currentNs1, currentNs2] });
- });
- });
- });
-
describe('#bulkCreate', () => {
const obj1 = {
type: 'config',
@@ -757,6 +604,10 @@ describe('SavedObjectsRepository', () => {
});
describe('errors', () => {
+ afterEach(() => {
+ mockGetBulkOperationError.mockReset();
+ });
+
const obj3 = {
type: 'dashboard',
id: 'three',
@@ -764,11 +615,13 @@ describe('SavedObjectsRepository', () => {
references: [{ name: 'ref_0', type: 'test', id: '2' }],
};
- const bulkCreateError = async (obj, esError, expectedError) => {
+ const bulkCreateError = async (obj, isBulkError, expectedErrorResult) => {
let response;
- if (esError) {
+ if (isBulkError) {
+ // mock the bulk error for only the second object
+ mockGetBulkOperationError.mockReturnValueOnce(undefined);
+ mockGetBulkOperationError.mockReturnValueOnce(expectedErrorResult.error);
response = getMockBulkCreateResponse([obj1, obj, obj2]);
- response.items[1].create = { error: esError };
} else {
response = getMockBulkCreateResponse([obj1, obj2]);
}
@@ -779,14 +632,14 @@ describe('SavedObjectsRepository', () => {
const objects = [obj1, obj, obj2];
const result = await savedObjectsRepository.bulkCreate(objects);
expect(client.bulk).toHaveBeenCalled();
- const objCall = esError ? expectObjArgs(obj) : [];
+ const objCall = isBulkError ? expectObjArgs(obj) : [];
const body = [...expectObjArgs(obj1), ...objCall, ...expectObjArgs(obj2)];
expect(client.bulk).toHaveBeenCalledWith(
expect.objectContaining({ body }),
expect.anything()
);
expect(result).toEqual({
- saved_objects: [expectSuccess(obj1), expectedError, expectSuccess(obj2)],
+ saved_objects: [expectSuccess(obj1), expectedErrorResult, expectSuccess(obj2)],
});
};
@@ -878,25 +731,9 @@ describe('SavedObjectsRepository', () => {
});
});
- it(`returns error when there is a version conflict (bulk)`, async () => {
- const esError = { type: 'version_conflict_engine_exception' };
- await bulkCreateError(obj3, esError, expectErrorConflict(obj3));
- });
-
- it(`returns error when document is missing`, async () => {
- const esError = { type: 'document_missing_exception' };
- await bulkCreateError(obj3, esError, expectErrorNotFound(obj3));
- });
-
- it(`returns error reason for other errors`, async () => {
- const esError = { reason: 'some_other_error' };
- await bulkCreateError(obj3, esError, expectErrorResult(obj3, { message: esError.reason }));
- });
-
- it(`returns error string for other errors if no reason is defined`, async () => {
- const esError = { foo: 'some_other_error' };
- const expectedError = expectErrorResult(obj3, { message: JSON.stringify(esError) });
- await bulkCreateError(obj3, esError, expectedError);
+ it(`returns bulk error`, async () => {
+ const expectedErrorResult = { type: obj3.type, id: obj3.id, error: 'Oh no, a bulk error!' };
+ await bulkCreateError(obj3, true, expectedErrorResult);
});
});
@@ -1530,16 +1367,22 @@ describe('SavedObjectsRepository', () => {
});
describe('errors', () => {
+ afterEach(() => {
+ mockGetBulkOperationError.mockReset();
+ });
+
const obj = {
type: 'dashboard',
id: 'three',
};
- const bulkUpdateError = async (obj, esError, expectedError) => {
+ const bulkUpdateError = async (obj, isBulkError, expectedErrorResult) => {
const objects = [obj1, obj, obj2];
const mockResponse = getMockBulkUpdateResponse(objects);
- if (esError) {
- mockResponse.items[1].update = { error: esError };
+ if (isBulkError) {
+ // mock the bulk error for only the second object
+ mockGetBulkOperationError.mockReturnValueOnce(undefined);
+ mockGetBulkOperationError.mockReturnValueOnce(expectedErrorResult.error);
}
client.bulk.mockResolvedValueOnce(
elasticsearchClientMock.createSuccessTransportRequestPromise(mockResponse)
@@ -1547,14 +1390,14 @@ describe('SavedObjectsRepository', () => {
const result = await savedObjectsRepository.bulkUpdate(objects);
expect(client.bulk).toHaveBeenCalled();
- const objCall = esError ? expectObjArgs(obj) : [];
+ const objCall = isBulkError ? expectObjArgs(obj) : [];
const body = [...expectObjArgs(obj1), ...objCall, ...expectObjArgs(obj2)];
expect(client.bulk).toHaveBeenCalledWith(
expect.objectContaining({ body }),
expect.anything()
);
expect(result).toEqual({
- saved_objects: [expectSuccess(obj1), expectedError, expectSuccess(obj2)],
+ saved_objects: [expectSuccess(obj1), expectedErrorResult, expectSuccess(obj2)],
});
};
@@ -1592,19 +1435,19 @@ describe('SavedObjectsRepository', () => {
it(`returns error when type is invalid`, async () => {
const _obj = { ...obj, type: 'unknownType' };
- await bulkUpdateError(_obj, undefined, expectErrorNotFound(_obj));
+ await bulkUpdateError(_obj, false, expectErrorNotFound(_obj));
});
it(`returns error when type is hidden`, async () => {
const _obj = { ...obj, type: HIDDEN_TYPE };
- await bulkUpdateError(_obj, undefined, expectErrorNotFound(_obj));
+ await bulkUpdateError(_obj, false, expectErrorNotFound(_obj));
});
it(`returns error when object namespace is '*'`, async () => {
const _obj = { ...obj, namespace: '*' };
await bulkUpdateError(
_obj,
- undefined,
+ false,
expectErrorResult(obj, createBadRequestError('"namespace" cannot be "*"'))
);
});
@@ -1627,25 +1470,9 @@ describe('SavedObjectsRepository', () => {
await bulkUpdateMultiError([obj1, _obj, obj2], { namespace }, mgetResponse);
});
- it(`returns error when there is a version conflict (bulk)`, async () => {
- const esError = { type: 'version_conflict_engine_exception' };
- await bulkUpdateError(obj, esError, expectErrorConflict(obj));
- });
-
- it(`returns error when document is missing (bulk)`, async () => {
- const esError = { type: 'document_missing_exception' };
- await bulkUpdateError(obj, esError, expectErrorNotFound(obj));
- });
-
- it(`returns error reason for other errors (bulk)`, async () => {
- const esError = { reason: 'some_other_error' };
- await bulkUpdateError(obj, esError, expectErrorResult(obj, { message: esError.reason }));
- });
-
- it(`returns error string for other errors if no reason is defined (bulk)`, async () => {
- const esError = { foo: 'some_other_error' };
- const expectedError = expectErrorResult(obj, { message: JSON.stringify(esError) });
- await bulkUpdateError(obj, esError, expectedError);
+ it(`returns bulk error`, async () => {
+ const expectedErrorResult = { type: obj.type, id: obj.id, error: 'Oh no, a bulk error!' };
+ await bulkUpdateError(obj, true, expectedErrorResult);
});
});
@@ -3898,352 +3725,6 @@ describe('SavedObjectsRepository', () => {
});
});
- describe('#deleteFromNamespaces', () => {
- const id = 'some-id';
- const type = MULTI_NAMESPACE_TYPE;
- const namespace1 = 'default';
- const namespace2 = 'foo-namespace';
- const namespace3 = 'bar-namespace';
-
- const mockGetResponse = (type, id, namespaces) => {
- // mock a document that exists in two namespaces
- const mockResponse = getMockGetResponse({ type, id });
- mockResponse._source.namespaces = namespaces;
- client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise(mockResponse)
- );
- };
-
- const deleteFromNamespacesSuccess = async (
- type,
- id,
- namespaces,
- currentNamespaces,
- options
- ) => {
- mockGetResponse(type, id, currentNamespaces);
- client.delete.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({
- _id: `${type}:${id}`,
- ...mockVersionProps,
- result: 'deleted',
- })
- );
- client.update.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({
- _id: `${type}:${id}`,
- ...mockVersionProps,
- result: 'updated',
- })
- );
-
- return await savedObjectsRepository.deleteFromNamespaces(type, id, namespaces, options);
- };
-
- describe('client calls', () => {
- describe('delete action', () => {
- const deleteFromNamespacesSuccessDelete = async (expectFn, options, _type = type) => {
- const test = async (namespaces) => {
- await deleteFromNamespacesSuccess(_type, id, namespaces, namespaces, options);
- expectFn();
- client.delete.mockClear();
- client.get.mockClear();
- };
- await test([namespace1]);
- await test([namespace1, namespace2]);
- };
-
- it(`should use ES get action then delete action if the object has no namespaces remaining`, async () => {
- const expectFn = () => {
- expect(client.delete).toHaveBeenCalledTimes(1);
- expect(client.get).toHaveBeenCalledTimes(1);
- };
- await deleteFromNamespacesSuccessDelete(expectFn);
- });
-
- it(`formats the ES requests`, async () => {
- const expectFn = () => {
- expect(client.delete).toHaveBeenCalledWith(
- expect.objectContaining({
- id: `${type}:${id}`,
- }),
- expect.anything()
- );
-
- const versionProperties = {
- if_seq_no: mockVersionProps._seq_no,
- if_primary_term: mockVersionProps._primary_term,
- };
- expect(client.delete).toHaveBeenCalledWith(
- expect.objectContaining({
- id: `${type}:${id}`,
- ...versionProperties,
- }),
- expect.anything()
- );
- };
- await deleteFromNamespacesSuccessDelete(expectFn);
- });
-
- it(`defaults to a refresh setting of wait_for`, async () => {
- await deleteFromNamespacesSuccessDelete(() =>
- expect(client.delete).toHaveBeenCalledWith(
- expect.objectContaining({
- refresh: 'wait_for',
- }),
- expect.anything()
- )
- );
- });
-
- it(`should use default index`, async () => {
- const expectFn = () =>
- expect(client.delete).toHaveBeenCalledWith(
- expect.objectContaining({ index: '.kibana-test' }),
- expect.anything()
- );
- await deleteFromNamespacesSuccessDelete(expectFn);
- });
-
- it(`should use custom index`, async () => {
- const expectFn = () =>
- expect(client.delete).toHaveBeenCalledWith(
- expect.objectContaining({ index: 'custom' }),
- expect.anything()
- );
- await deleteFromNamespacesSuccessDelete(expectFn, {}, MULTI_NAMESPACE_CUSTOM_INDEX_TYPE);
- });
- });
-
- describe('update action', () => {
- const deleteFromNamespacesSuccessUpdate = async (expectFn, options, _type = type) => {
- const test = async (remaining) => {
- const currentNamespaces = [namespace1].concat(remaining);
- await deleteFromNamespacesSuccess(_type, id, [namespace1], currentNamespaces, options);
- expectFn();
- client.get.mockClear();
- client.update.mockClear();
- };
- await test([namespace2]);
- await test([namespace2, namespace3]);
- };
-
- it(`should use ES get action then update action if the object has one or more namespaces remaining`, async () => {
- const expectFn = () => {
- expect(client.update).toHaveBeenCalledTimes(1);
- expect(client.get).toHaveBeenCalledTimes(1);
- };
- await deleteFromNamespacesSuccessUpdate(expectFn);
- });
-
- it(`formats the ES requests`, async () => {
- let ctr = 0;
- const expectFn = () => {
- expect(client.update).toHaveBeenCalledWith(
- expect.objectContaining({
- id: `${type}:${id}`,
- }),
- expect.anything()
- );
- const namespaces = ctr++ === 0 ? [namespace2] : [namespace2, namespace3];
- const versionProperties = {
- if_seq_no: mockVersionProps._seq_no,
- if_primary_term: mockVersionProps._primary_term,
- };
- expect(client.update).toHaveBeenCalledWith(
- expect.objectContaining({
- id: `${type}:${id}`,
- ...versionProperties,
- body: { doc: { ...mockTimestampFields, namespaces } },
- }),
- expect.anything()
- );
- };
- await deleteFromNamespacesSuccessUpdate(expectFn);
- });
-
- it(`defaults to a refresh setting of wait_for`, async () => {
- const expectFn = () =>
- expect(client.update).toHaveBeenCalledWith(
- expect.objectContaining({
- refresh: 'wait_for',
- }),
- expect.anything()
- );
- await deleteFromNamespacesSuccessUpdate(expectFn);
- });
-
- it(`should use default index`, async () => {
- const expectFn = () =>
- expect(client.update).toHaveBeenCalledWith(
- expect.objectContaining({ index: '.kibana-test' }),
- expect.anything()
- );
- await deleteFromNamespacesSuccessUpdate(expectFn);
- });
-
- it(`should use custom index`, async () => {
- const expectFn = () =>
- expect(client.update).toHaveBeenCalledWith(
- expect.objectContaining({ index: 'custom' }),
- expect.anything()
- );
- await deleteFromNamespacesSuccessUpdate(expectFn, {}, MULTI_NAMESPACE_CUSTOM_INDEX_TYPE);
- });
- });
- });
-
- describe('errors', () => {
- const expectNotFoundError = async (type, id, namespaces, options) => {
- await expect(
- savedObjectsRepository.deleteFromNamespaces(type, id, namespaces, options)
- ).rejects.toThrowError(createGenericNotFoundError(type, id));
- };
- const expectBadRequestError = async (type, id, namespaces, message) => {
- await expect(
- savedObjectsRepository.deleteFromNamespaces(type, id, namespaces)
- ).rejects.toThrowError(createBadRequestError(message));
- };
-
- it(`throws when type is invalid`, async () => {
- await expectNotFoundError('unknownType', id, [namespace1, namespace2]);
- expect(client.delete).not.toHaveBeenCalled();
- expect(client.update).not.toHaveBeenCalled();
- });
-
- it(`throws when type is hidden`, async () => {
- await expectNotFoundError(HIDDEN_TYPE, id, [namespace1, namespace2]);
- expect(client.delete).not.toHaveBeenCalled();
- expect(client.update).not.toHaveBeenCalled();
- });
-
- it(`throws when type is not shareable`, async () => {
- const test = async (type) => {
- const message = `${type} doesn't support multiple namespaces`;
- await expectBadRequestError(type, id, [namespace1, namespace2], message);
- expect(client.delete).not.toHaveBeenCalled();
- expect(client.update).not.toHaveBeenCalled();
- };
- await test('index-pattern');
- await test(MULTI_NAMESPACE_ISOLATED_TYPE);
- await test(NAMESPACE_AGNOSTIC_TYPE);
- });
-
- it(`throws when namespaces is an empty array`, async () => {
- const test = async (namespaces) => {
- const message = 'namespaces must be a non-empty array of strings';
- await expectBadRequestError(type, id, namespaces, message);
- expect(client.delete).not.toHaveBeenCalled();
- expect(client.update).not.toHaveBeenCalled();
- };
- await test([]);
- });
-
- it(`throws when ES is unable to find the document during get`, async () => {
- client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({ found: false })
- );
- await expectNotFoundError(type, id, [namespace1, namespace2]);
- expect(client.get).toHaveBeenCalledTimes(1);
- });
-
- it(`throws when ES is unable to find the index during get`, async () => {
- client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
- );
- await expectNotFoundError(type, id, [namespace1, namespace2]);
- expect(client.get).toHaveBeenCalledTimes(1);
- });
-
- it(`throws when the document exists, but not in this namespace`, async () => {
- mockGetResponse(type, id, [namespace1]);
- await expectNotFoundError(type, id, [namespace1], { namespace: 'some-other-namespace' });
- expect(client.get).toHaveBeenCalledTimes(1);
- });
-
- it(`throws when ES is unable to find the document during delete`, async () => {
- mockGetResponse(type, id, [namespace1]);
- client.delete.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({ result: 'not_found' })
- );
- await expectNotFoundError(type, id, [namespace1]);
- expect(client.get).toHaveBeenCalledTimes(1);
- expect(client.delete).toHaveBeenCalledTimes(1);
- });
-
- it(`throws when ES is unable to find the index during delete`, async () => {
- mockGetResponse(type, id, [namespace1]);
- client.delete.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({
- error: { type: 'index_not_found_exception' },
- })
- );
- await expectNotFoundError(type, id, [namespace1]);
- expect(client.get).toHaveBeenCalledTimes(1);
- expect(client.delete).toHaveBeenCalledTimes(1);
- });
-
- it(`throws when ES returns an unexpected response`, async () => {
- mockGetResponse(type, id, [namespace1]);
- client.delete.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({
- result: 'something unexpected',
- })
- );
- await expect(
- savedObjectsRepository.deleteFromNamespaces(type, id, [namespace1])
- ).rejects.toThrowError('Unexpected Elasticsearch DELETE response');
- expect(client.get).toHaveBeenCalledTimes(1);
- expect(client.delete).toHaveBeenCalledTimes(1);
- });
-
- it(`throws when ES is unable to find the document during update`, async () => {
- mockGetResponse(type, id, [namespace1, namespace2]);
- client.update.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
- );
- await expectNotFoundError(type, id, [namespace1]);
- expect(client.get).toHaveBeenCalledTimes(1);
- expect(client.update).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('returns', () => {
- it(`returns an empty namespaces array on success (delete)`, async () => {
- const test = async (namespaces) => {
- const result = await deleteFromNamespacesSuccess(type, id, namespaces, namespaces);
- expect(result).toEqual({ namespaces: [] });
- client.delete.mockClear();
- };
- await test([namespace1]);
- await test([namespace1, namespace2]);
- });
-
- it(`returns remaining namespaces on success (update)`, async () => {
- const test = async (remaining) => {
- const currentNamespaces = [namespace1].concat(remaining);
- const result = await deleteFromNamespacesSuccess(
- type,
- id,
- [namespace1],
- currentNamespaces
- );
- expect(result).toEqual({ namespaces: remaining });
- client.delete.mockClear();
- };
- await test([namespace2]);
- await test([namespace2, namespace3]);
- });
-
- it(`succeeds when the document doesn't exist in all of the targeted namespaces`, async () => {
- const namespaces = [namespace2];
- const currentNamespaces = [namespace1];
- const result = await deleteFromNamespacesSuccess(type, id, namespaces, currentNamespaces);
- expect(result).toEqual({ namespaces: currentNamespaces });
- });
- });
- });
-
describe('#update', () => {
const id = 'logstash-*';
const type = 'index-pattern';
@@ -4722,4 +4203,65 @@ describe('SavedObjectsRepository', () => {
);
});
});
+
+ describe('#collectMultiNamespaceReferences', () => {
+ afterEach(() => {
+ mockCollectMultiNamespaceReferences.mockReset();
+ });
+
+ it('passes arguments to the collectMultiNamespaceReferences module and returns the result', async () => {
+ const objects = Symbol();
+ const expectedResult = Symbol();
+ mockCollectMultiNamespaceReferences.mockResolvedValue(expectedResult);
+
+ await expect(
+ savedObjectsRepository.collectMultiNamespaceReferences(objects)
+ ).resolves.toEqual(expectedResult);
+ expect(mockCollectMultiNamespaceReferences).toHaveBeenCalledTimes(1);
+ expect(mockCollectMultiNamespaceReferences).toHaveBeenCalledWith(
+ expect.objectContaining({ objects })
+ );
+ });
+
+ it('returns an error from the collectMultiNamespaceReferences module', async () => {
+ const expectedResult = new Error('Oh no!');
+ mockCollectMultiNamespaceReferences.mockRejectedValue(expectedResult);
+
+ await expect(savedObjectsRepository.collectMultiNamespaceReferences([])).rejects.toEqual(
+ expectedResult
+ );
+ });
+ });
+
+ describe('#updateObjectsSpaces', () => {
+ afterEach(() => {
+ mockUpdateObjectsSpaces.mockReset();
+ });
+
+ it('passes arguments to the updateObjectsSpaces module and returns the result', async () => {
+ const objects = Symbol();
+ const spacesToAdd = Symbol();
+ const spacesToRemove = Symbol();
+ const options = Symbol();
+ const expectedResult = Symbol();
+ mockUpdateObjectsSpaces.mockResolvedValue(expectedResult);
+
+ await expect(
+ savedObjectsRepository.updateObjectsSpaces(objects, spacesToAdd, spacesToRemove, options)
+ ).resolves.toEqual(expectedResult);
+ expect(mockUpdateObjectsSpaces).toHaveBeenCalledTimes(1);
+ expect(mockUpdateObjectsSpaces).toHaveBeenCalledWith(
+ expect.objectContaining({ objects, spacesToAdd, spacesToRemove, options })
+ );
+ });
+
+ it('returns an error from the updateObjectsSpaces module', async () => {
+ const expectedResult = new Error('Oh no!');
+ mockUpdateObjectsSpaces.mockRejectedValue(expectedResult);
+
+ await expect(savedObjectsRepository.updateObjectsSpaces([], [], [])).rejects.toEqual(
+ expectedResult
+ );
+ });
+ });
});
diff --git a/src/core/server/saved_objects/service/lib/repository.test.mock.ts b/src/core/server/saved_objects/service/lib/repository.test.mock.ts
index 3eba77b465819..f044fe9279fbf 100644
--- a/src/core/server/saved_objects/service/lib/repository.test.mock.ts
+++ b/src/core/server/saved_objects/service/lib/repository.test.mock.ts
@@ -6,6 +6,36 @@
* Side Public License, v 1.
*/
+import type { collectMultiNamespaceReferences } from './collect_multi_namespace_references';
+import type * as InternalUtils from './internal_utils';
+import type { updateObjectsSpaces } from './update_objects_spaces';
+
+export const mockCollectMultiNamespaceReferences = jest.fn() as jest.MockedFunction<
+ typeof collectMultiNamespaceReferences
+>;
+
+jest.mock('./collect_multi_namespace_references', () => ({
+ collectMultiNamespaceReferences: mockCollectMultiNamespaceReferences,
+}));
+
+export const mockGetBulkOperationError = jest.fn() as jest.MockedFunction<
+ typeof InternalUtils['getBulkOperationError']
+>;
+
+jest.mock('./internal_utils', () => {
+ const actual = jest.requireActual('./internal_utils');
+ return {
+ ...actual,
+ getBulkOperationError: mockGetBulkOperationError,
+ };
+});
+
+export const mockUpdateObjectsSpaces = jest.fn() as jest.MockedFunction;
+
+jest.mock('./update_objects_spaces', () => ({
+ updateObjectsSpaces: mockUpdateObjectsSpaces,
+}));
+
export const pointInTimeFinderMock = jest.fn();
jest.doMock('./point_in_time_finder', () => ({
PointInTimeFinder: pointInTimeFinderMock,
diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts
index 2ef3be71407b0..c626a2b2acfb5 100644
--- a/src/core/server/saved_objects/service/lib/repository.ts
+++ b/src/core/server/saved_objects/service/lib/repository.ts
@@ -48,10 +48,6 @@ import {
SavedObjectsBulkUpdateObject,
SavedObjectsBulkUpdateOptions,
SavedObjectsDeleteOptions,
- SavedObjectsAddToNamespacesOptions,
- SavedObjectsAddToNamespacesResponse,
- SavedObjectsDeleteFromNamespacesOptions,
- SavedObjectsDeleteFromNamespacesResponse,
SavedObjectsRemoveReferencesToOptions,
SavedObjectsRemoveReferencesToResponse,
SavedObjectsResolveResponse,
@@ -64,15 +60,31 @@ import {
MutatingOperationRefreshSetting,
} from '../../types';
import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../object_types';
-import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry';
+import { ISavedObjectTypeRegistry } from '../../saved_objects_type_registry';
import { validateConvertFilterToKueryNode } from './filter_utils';
import { validateAndConvertAggregations } from './aggregations';
+import {
+ getBulkOperationError,
+ getExpectedVersionProperties,
+ getSavedObjectFromSource,
+ rawDocExistsInNamespace,
+} from './internal_utils';
import {
ALL_NAMESPACES_STRING,
FIND_DEFAULT_PAGE,
FIND_DEFAULT_PER_PAGE,
SavedObjectsUtils,
} from './utils';
+import {
+ collectMultiNamespaceReferences,
+ SavedObjectsCollectMultiNamespaceReferencesObject,
+ SavedObjectsCollectMultiNamespaceReferencesOptions,
+} from './collect_multi_namespace_references';
+import {
+ updateObjectsSpaces,
+ SavedObjectsUpdateObjectsSpacesObject,
+ SavedObjectsUpdateObjectsSpacesOptions,
+} from './update_objects_spaces';
// BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository
// so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient.
@@ -95,7 +107,7 @@ export interface SavedObjectsRepositoryOptions {
index: string;
mappings: IndexMapping;
client: ElasticsearchClient;
- typeRegistry: SavedObjectTypeRegistry;
+ typeRegistry: ISavedObjectTypeRegistry;
serializer: SavedObjectsSerializer;
migrator: IKibanaMigrator;
allowedTypes: string[];
@@ -134,7 +146,7 @@ export interface SavedObjectsDeleteByNamespaceOptions extends SavedObjectsBaseOp
refresh?: boolean;
}
-const DEFAULT_REFRESH_SETTING = 'wait_for';
+export const DEFAULT_REFRESH_SETTING = 'wait_for';
/**
* See {@link SavedObjectsRepository}
@@ -160,7 +172,7 @@ export class SavedObjectsRepository {
private _migrator: IKibanaMigrator;
private _index: string;
private _mappings: IndexMapping;
- private _registry: SavedObjectTypeRegistry;
+ private _registry: ISavedObjectTypeRegistry;
private _allowedTypes: string[];
private readonly client: RepositoryEsClient;
private _serializer: SavedObjectsSerializer;
@@ -176,7 +188,7 @@ export class SavedObjectsRepository {
*/
public static createRepository(
migrator: IKibanaMigrator,
- typeRegistry: SavedObjectTypeRegistry,
+ typeRegistry: ISavedObjectTypeRegistry,
indexName: string,
client: ElasticsearchClient,
logger: Logger,
@@ -511,16 +523,11 @@ export class SavedObjectsRepository {
}
const { requestedId, rawMigratedDoc, esRequestIndex } = expectedResult.value;
- const { error, ...rawResponse } = Object.values(
- bulkResponse?.body.items[esRequestIndex] ?? {}
- )[0] as any;
+ const rawResponse = Object.values(bulkResponse?.body.items[esRequestIndex] ?? {})[0] as any;
+ const error = getBulkOperationError(rawMigratedDoc._source.type, requestedId, rawResponse);
if (error) {
- return {
- id: requestedId,
- type: rawMigratedDoc._source.type,
- error: getBulkOperationError(error, rawMigratedDoc._source.type, requestedId),
- };
+ return { type: rawMigratedDoc._source.type, id: requestedId, error };
}
// When method == 'index' the bulkResponse doesn't include the indexed
@@ -989,7 +996,7 @@ export class SavedObjectsRepository {
}
// @ts-expect-error MultiGetHit._source is optional
- return this.getSavedObjectFromSource(type, id, doc);
+ return getSavedObjectFromSource(this._registry, type, id, doc);
}),
};
}
@@ -1033,7 +1040,7 @@ export class SavedObjectsRepository {
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
- return this.getSavedObjectFromSource(type, id, body);
+ return getSavedObjectFromSource(this._registry, type, id, body);
}
/**
@@ -1138,20 +1145,25 @@ export class SavedObjectsRepository {
if (foundExactMatch && foundAliasMatch) {
return {
// @ts-expect-error MultiGetHit._source is optional
- saved_object: this.getSavedObjectFromSource(type, id, exactMatchDoc),
+ saved_object: getSavedObjectFromSource(this._registry, type, id, exactMatchDoc),
outcome: 'conflict',
aliasTargetId: legacyUrlAlias.targetId,
};
} else if (foundExactMatch) {
return {
// @ts-expect-error MultiGetHit._source is optional
- saved_object: this.getSavedObjectFromSource(type, id, exactMatchDoc),
+ saved_object: getSavedObjectFromSource(this._registry, type, id, exactMatchDoc),
outcome: 'exactMatch',
};
} else if (foundAliasMatch) {
return {
- // @ts-expect-error MultiGetHit._source is optional
- saved_object: this.getSavedObjectFromSource(type, legacyUrlAlias.targetId, aliasMatchDoc),
+ saved_object: getSavedObjectFromSource(
+ this._registry,
+ type,
+ legacyUrlAlias.targetId,
+ // @ts-expect-error MultiGetHit._source is optional
+ aliasMatchDoc
+ ),
outcome: 'aliasMatch',
aliasTargetId: legacyUrlAlias.targetId,
};
@@ -1263,169 +1275,52 @@ export class SavedObjectsRepository {
}
/**
- * Adds one or more namespaces to a given multi-namespace saved object. This method and
- * [`deleteFromNamespaces`]{@link SavedObjectsRepository.deleteFromNamespaces} are the only ways to change which Spaces a multi-namespace
- * saved object is shared to.
+ * Gets all references and transitive references of the given objects. Ignores any object and/or reference that is not a multi-namespace
+ * type.
+ *
+ * @param objects The objects to get the references for.
*/
- async addToNamespaces(
- type: string,
- id: string,
- namespaces: string[],
- options: SavedObjectsAddToNamespacesOptions = {}
- ): Promise {
- if (!this._allowedTypes.includes(type)) {
- throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
- }
-
- if (!this._registry.isShareable(type)) {
- throw SavedObjectsErrorHelpers.createBadRequestError(
- `${type} doesn't support multiple namespaces`
- );
- }
-
- if (!namespaces.length) {
- throw SavedObjectsErrorHelpers.createBadRequestError(
- 'namespaces must be a non-empty array of strings'
- );
- }
-
- const { version, namespace, refresh = DEFAULT_REFRESH_SETTING } = options;
- // we do not need to normalize the namespace to its ID format, since it will be converted to a namespace string before being used
-
- const rawId = this._serializer.generateRawId(undefined, type, id);
- const preflightResult = await this.preflightCheckIncludesNamespace(type, id, namespace);
- const existingNamespaces = getSavedObjectNamespaces(undefined, preflightResult);
- // there should never be a case where a multi-namespace object does not have any existing namespaces
- // however, it is a possibility if someone manually modifies the document in Elasticsearch
- const time = this._getCurrentTime();
-
- const doc = {
- updated_at: time,
- namespaces: existingNamespaces ? unique(existingNamespaces.concat(namespaces)) : namespaces,
- };
-
- const { statusCode } = await this.client.update(
- {
- id: rawId,
- index: this.getIndexForType(type),
- ...getExpectedVersionProperties(version, preflightResult),
- refresh,
- body: {
- doc,
- },
- },
- { ignore: [404] }
- );
-
- if (statusCode === 404) {
- // see "404s from missing index" above
- throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
- }
-
- return { namespaces: doc.namespaces };
+ async collectMultiNamespaceReferences(
+ objects: SavedObjectsCollectMultiNamespaceReferencesObject[],
+ options?: SavedObjectsCollectMultiNamespaceReferencesOptions
+ ) {
+ return collectMultiNamespaceReferences({
+ registry: this._registry,
+ allowedTypes: this._allowedTypes,
+ client: this.client,
+ serializer: this._serializer,
+ getIndexForType: this.getIndexForType.bind(this),
+ createPointInTimeFinder: this.createPointInTimeFinder.bind(this),
+ objects,
+ options,
+ });
}
/**
- * 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`]{@link SavedObjectsRepository.addToNamespaces} are the only ways to change which Spaces a
- * multi-namespace saved object is shared to.
+ * Updates one or more objects to add and/or remove them from specified spaces.
+ *
+ * @param objects
+ * @param spacesToAdd
+ * @param spacesToRemove
+ * @param options
*/
- async deleteFromNamespaces(
- type: string,
- id: string,
- namespaces: string[],
- options: SavedObjectsDeleteFromNamespacesOptions = {}
- ): Promise {
- if (!this._allowedTypes.includes(type)) {
- throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
- }
-
- if (!this._registry.isShareable(type)) {
- throw SavedObjectsErrorHelpers.createBadRequestError(
- `${type} doesn't support multiple namespaces`
- );
- }
-
- if (!namespaces.length) {
- throw SavedObjectsErrorHelpers.createBadRequestError(
- 'namespaces must be a non-empty array of strings'
- );
- }
-
- const { namespace, refresh = DEFAULT_REFRESH_SETTING } = options;
- // we do not need to normalize the namespace to its ID format, since it will be converted to a namespace string before being used
-
- const rawId = this._serializer.generateRawId(undefined, type, id);
- const preflightResult = await this.preflightCheckIncludesNamespace(type, id, namespace);
- const existingNamespaces = getSavedObjectNamespaces(undefined, preflightResult);
- // if there are somehow no existing namespaces, allow the operation to proceed and delete this saved object
- const remainingNamespaces = existingNamespaces?.filter((x) => !namespaces.includes(x));
-
- if (remainingNamespaces?.length) {
- // if there is 1 or more namespace remaining, update the saved object
- const time = this._getCurrentTime();
-
- const doc = {
- updated_at: time,
- namespaces: remainingNamespaces,
- };
-
- const { statusCode } = await this.client.update(
- {
- id: rawId,
- index: this.getIndexForType(type),
- ...getExpectedVersionProperties(undefined, preflightResult),
- refresh,
-
- body: {
- doc,
- },
- },
- {
- ignore: [404],
- }
- );
-
- if (statusCode === 404) {
- // see "404s from missing index" above
- throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
- }
- return { namespaces: doc.namespaces };
- } else {
- // if there are no namespaces remaining, delete the saved object
- const { body, statusCode } = await this.client.delete(
- {
- id: this._serializer.generateRawId(undefined, type, id),
- refresh,
- ...getExpectedVersionProperties(undefined, preflightResult),
- index: this.getIndexForType(type),
- },
- {
- ignore: [404],
- }
- );
-
- const deleted = body.result === 'deleted';
- if (deleted) {
- return { namespaces: [] };
- }
-
- const deleteDocNotFound = body.result === 'not_found';
- // @ts-expect-error
- const deleteIndexNotFound = body.error && body.error.type === 'index_not_found_exception';
- if (deleteDocNotFound || deleteIndexNotFound) {
- // see "404s from missing index" above
- throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
- }
-
- throw new Error(
- `Unexpected Elasticsearch DELETE response: ${JSON.stringify({
- type,
- id,
- response: { body, statusCode },
- })}`
- );
- }
+ async updateObjectsSpaces(
+ objects: SavedObjectsUpdateObjectsSpacesObject[],
+ spacesToAdd: string[],
+ spacesToRemove: string[],
+ options?: SavedObjectsUpdateObjectsSpacesOptions
+ ) {
+ return updateObjectsSpaces({
+ registry: this._registry,
+ allowedTypes: this._allowedTypes,
+ client: this.client,
+ serializer: this._serializer,
+ getIndexForType: this.getIndexForType.bind(this),
+ objects,
+ spacesToAdd,
+ spacesToRemove,
+ options,
+ });
}
/**
@@ -1617,21 +1512,19 @@ export class SavedObjectsRepository {
const { type, id, namespaces, documentToSave, esRequestIndex } = expectedResult.value;
const response = bulkUpdateResponse?.body.items[esRequestIndex] ?? {};
+ const rawResponse = Object.values(response)[0] as any;
+
+ const error = getBulkOperationError(type, id, rawResponse);
+ if (error) {
+ return { type, id, error };
+ }
+
// When a bulk update operation is completed, any fields specified in `_sourceIncludes` will be found in the "get" value of the
// returned object. We need to retrieve the `originId` if it exists so we can return it to the consumer.
- const { error, _seq_no: seqNo, _primary_term: primaryTerm, get } = Object.values(
- response
- )[0] as any;
+ const { _seq_no: seqNo, _primary_term: primaryTerm, get } = rawResponse;
// eslint-disable-next-line @typescript-eslint/naming-convention
const { [type]: attributes, references, updated_at } = documentToSave;
- if (error) {
- return {
- id,
- type,
- error: getBulkOperationError(error, type, id),
- };
- }
const { originId } = get._source;
return {
@@ -2055,10 +1948,10 @@ export class SavedObjectsRepository {
* }
* ```
*/
- createPointInTimeFinder(
+ createPointInTimeFinder(
findOptions: SavedObjectsCreatePointInTimeFinderOptions,
dependencies?: SavedObjectsCreatePointInTimeFinderDependencies
- ): ISavedObjectsPointInTimeFinder {
+ ): ISavedObjectsPointInTimeFinder {
return new PointInTimeFinder(findOptions, {
logger: this._logger,
client: this,
@@ -2108,28 +2001,8 @@ export class SavedObjectsRepository {
return omit(savedObject, ['namespace']) as SavedObject;
}
- /**
- * Check to ensure that a raw document exists in a namespace. If the document is not a multi-namespace type, then this returns `true` as
- * we rely on the guarantees of the document ID format. If the document is a multi-namespace type, this checks to ensure that the
- * document's `namespaces` value includes the string representation of the given namespace.
- *
- * WARNING: This should only be used for documents that were retrieved from Elasticsearch. Otherwise, the guarantees of the document ID
- * format mentioned above do not apply.
- */
- private rawDocExistsInNamespace(raw: SavedObjectsRawDoc, namespace?: string) {
- const rawDocType = raw._source.type;
-
- // if the type is namespace isolated, or namespace agnostic, we can continue to rely on the guarantees
- // of the document ID format and don't need to check this
- if (!this._registry.isMultiNamespace(rawDocType)) {
- return true;
- }
-
- const namespaces = raw._source.namespaces;
- const existsInNamespace =
- namespaces?.includes(SavedObjectsUtils.namespaceIdToString(namespace)) ||
- namespaces?.includes('*');
- return existsInNamespace ?? false;
+ private rawDocExistsInNamespace(raw: SavedObjectsRawDoc, namespace: string | undefined) {
+ return rawDocExistsInNamespace(this._registry, raw, namespace);
}
/**
@@ -2204,34 +2077,6 @@ export class SavedObjectsRepository {
return body;
}
- private getSavedObjectFromSource(
- type: string,
- id: string,
- doc: { _seq_no?: number; _primary_term?: number; _source: SavedObjectsRawDocSource }
- ): SavedObject {
- const { originId, updated_at: updatedAt } = doc._source;
-
- let namespaces: string[] = [];
- if (!this._registry.isNamespaceAgnostic(type)) {
- namespaces = doc._source.namespaces ?? [
- SavedObjectsUtils.namespaceIdToString(doc._source.namespace),
- ];
- }
-
- return {
- id,
- type,
- namespaces,
- ...(originId && { originId }),
- ...(updatedAt && { updated_at: updatedAt }),
- version: encodeHitVersion(doc),
- attributes: doc._source[type],
- references: doc._source.references || [],
- migrationVersion: doc._source.migrationVersion,
- coreMigrationVersion: doc._source.coreMigrationVersion,
- };
- }
-
private async resolveExactMatch(
type: string,
id: string,
@@ -2242,43 +2087,6 @@ export class SavedObjectsRepository {
}
}
-function getBulkOperationError(
- error: { type: string; reason?: string; index?: string },
- type: string,
- id: string
-) {
- switch (error.type) {
- case 'version_conflict_engine_exception':
- return errorContent(SavedObjectsErrorHelpers.createConflictError(type, id));
- case 'document_missing_exception':
- return errorContent(SavedObjectsErrorHelpers.createGenericNotFoundError(type, id));
- case 'index_not_found_exception':
- return errorContent(SavedObjectsErrorHelpers.createIndexAliasNotFoundError(error.index!));
- default:
- return {
- message: error.reason || JSON.stringify(error),
- };
- }
-}
-
-/**
- * Returns an object with the expected version properties. This facilitates Elasticsearch's Optimistic Concurrency Control.
- *
- * @param version Optional version specified by the consumer.
- * @param document Optional existing document that was obtained in a preflight operation.
- */
-function getExpectedVersionProperties(version?: string, document?: SavedObjectsRawDoc) {
- if (version) {
- return decodeRequestVersion(version);
- } else if (document) {
- return {
- if_seq_no: document._seq_no,
- if_primary_term: document._primary_term,
- };
- }
- return {};
-}
-
/**
* Returns a string array of namespaces for a given saved object. If the saved object is undefined, the result is an array that contains the
* current namespace. Value may be undefined if an existing saved object has no namespaces attribute; this should not happen in normal
diff --git a/src/core/server/saved_objects/service/lib/update_objects_spaces.test.mock.ts b/src/core/server/saved_objects/service/lib/update_objects_spaces.test.mock.ts
new file mode 100644
index 0000000000000..d7aa762e01aab
--- /dev/null
+++ b/src/core/server/saved_objects/service/lib/update_objects_spaces.test.mock.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import type * as InternalUtils from './internal_utils';
+
+export const mockGetBulkOperationError = jest.fn() as jest.MockedFunction<
+ typeof InternalUtils['getBulkOperationError']
+>;
+export const mockGetExpectedVersionProperties = jest.fn() as jest.MockedFunction<
+ typeof InternalUtils['getExpectedVersionProperties']
+>;
+export const mockRawDocExistsInNamespace = jest.fn() as jest.MockedFunction<
+ typeof InternalUtils['rawDocExistsInNamespace']
+>;
+
+jest.mock('./internal_utils', () => {
+ const actual = jest.requireActual('./internal_utils');
+ return {
+ ...actual,
+ getBulkOperationError: mockGetBulkOperationError,
+ getExpectedVersionProperties: mockGetExpectedVersionProperties,
+ rawDocExistsInNamespace: mockRawDocExistsInNamespace,
+ };
+});
diff --git a/src/core/server/saved_objects/service/lib/update_objects_spaces.test.ts b/src/core/server/saved_objects/service/lib/update_objects_spaces.test.ts
new file mode 100644
index 0000000000000..489432a4ab169
--- /dev/null
+++ b/src/core/server/saved_objects/service/lib/update_objects_spaces.test.ts
@@ -0,0 +1,453 @@
+/*
+ * Copyright 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 {
+ mockGetBulkOperationError,
+ mockGetExpectedVersionProperties,
+ mockRawDocExistsInNamespace,
+} from './update_objects_spaces.test.mock';
+
+import type { DeeplyMockedKeys } from '@kbn/utility-types/target/jest';
+import type { ElasticsearchClient } from 'src/core/server/elasticsearch';
+import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks';
+
+import { typeRegistryMock } from '../../saved_objects_type_registry.mock';
+import { SavedObjectsSerializer } from '../../serialization';
+import type {
+ SavedObjectsUpdateObjectsSpacesObject,
+ UpdateObjectsSpacesParams,
+} from './update_objects_spaces';
+import { updateObjectsSpaces } from './update_objects_spaces';
+
+type SetupParams = Partial<
+ Pick
+>;
+
+const EXISTING_SPACE = 'existing-space';
+const VERSION_PROPS = { _seq_no: 1, _primary_term: 1 };
+const EXPECTED_VERSION_PROPS = { if_seq_no: 1, if_primary_term: 1 };
+const BULK_ERROR = {
+ error: 'Oh no, a bulk error!',
+ type: 'error_type',
+ message: 'error_message',
+ statusCode: 400,
+};
+
+const SHAREABLE_OBJ_TYPE = 'type-a';
+const NON_SHAREABLE_OBJ_TYPE = 'type-b';
+const SHAREABLE_HIDDEN_OBJ_TYPE = 'type-c';
+
+const mockCurrentTime = new Date('2021-05-01T10:20:30Z');
+
+beforeAll(() => {
+ jest.useFakeTimers('modern');
+ jest.setSystemTime(mockCurrentTime);
+});
+
+beforeEach(() => {
+ mockGetExpectedVersionProperties.mockReturnValue(EXPECTED_VERSION_PROPS);
+ mockRawDocExistsInNamespace.mockReset();
+ mockRawDocExistsInNamespace.mockReturnValue(true); // return true by default
+});
+
+afterAll(() => {
+ jest.useRealTimers();
+});
+
+describe('#updateObjectsSpaces', () => {
+ let client: DeeplyMockedKeys;
+
+ /** Sets up the type registry, saved objects client, etc. and return the full parameters object to be passed to `updateObjectsSpaces` */
+ function setup({ objects = [], spacesToAdd = [], spacesToRemove = [], options }: SetupParams) {
+ const registry = typeRegistryMock.create();
+ registry.isShareable.mockImplementation(
+ (type) => [SHAREABLE_OBJ_TYPE, SHAREABLE_HIDDEN_OBJ_TYPE].includes(type) // NON_SHAREABLE_OBJ_TYPE is excluded
+ );
+ client = elasticsearchClientMock.createElasticsearchClient();
+ const serializer = new SavedObjectsSerializer(registry);
+ return {
+ registry,
+ allowedTypes: [SHAREABLE_OBJ_TYPE, NON_SHAREABLE_OBJ_TYPE], // SHAREABLE_HIDDEN_OBJ_TYPE is excluded
+ client,
+ serializer,
+ getIndexForType: (type: string) => `index-for-${type}`,
+ objects,
+ spacesToAdd,
+ spacesToRemove,
+ options,
+ };
+ }
+
+ /** Mocks the saved objects client so it returns the expected results */
+ function mockMgetResults(...results: Array<{ found: boolean }>) {
+ client.mget.mockReturnValueOnce(
+ elasticsearchClientMock.createSuccessTransportRequestPromise({
+ docs: results.map((x) =>
+ x.found
+ ? {
+ _id: 'doesnt-matter',
+ _index: 'doesnt-matter',
+ _source: { namespaces: [EXISTING_SPACE] },
+ ...VERSION_PROPS,
+ found: true,
+ }
+ : {
+ _id: 'doesnt-matter',
+ _index: 'doesnt-matter',
+ found: false,
+ }
+ ),
+ })
+ );
+ }
+
+ /** Asserts that mget is called for the given objects */
+ function expectMgetArgs(...objects: SavedObjectsUpdateObjectsSpacesObject[]) {
+ const docs = objects.map(({ type, id }) => expect.objectContaining({ _id: `${type}:${id}` }));
+ expect(client.mget).toHaveBeenCalledWith({ body: { docs } }, expect.anything());
+ }
+
+ /** Mocks the saved objects client so it returns the expected results */
+ function mockBulkResults(...results: Array<{ error: boolean }>) {
+ results.forEach(({ error }) => {
+ if (error) {
+ mockGetBulkOperationError.mockReturnValueOnce(BULK_ERROR);
+ } else {
+ mockGetBulkOperationError.mockReturnValueOnce(undefined);
+ }
+ });
+ client.bulk.mockReturnValueOnce(
+ elasticsearchClientMock.createSuccessTransportRequestPromise({
+ items: results.map(() => ({})), // as long as the result does not contain an error field, it is treated as a success
+ errors: false,
+ took: 0,
+ })
+ );
+ }
+
+ /** Asserts that mget is called for the given objects */
+ function expectBulkArgs(
+ ...objectActions: Array<{
+ object: { type: string; id: string; namespaces?: string[] };
+ action: 'update' | 'delete';
+ }>
+ ) {
+ const body = objectActions.flatMap(
+ ({ object: { type, id, namespaces = expect.any(Array) }, action }) => {
+ const operation = {
+ [action]: {
+ _id: `${type}:${id}`,
+ _index: `index-for-${type}`,
+ ...EXPECTED_VERSION_PROPS,
+ },
+ };
+ return action === 'update'
+ ? [operation, { doc: { namespaces, updated_at: mockCurrentTime.toISOString() } }] // 'update' uses an operation and document metadata
+ : [operation]; // 'delete' only uses an operation
+ }
+ );
+ expect(client.bulk).toHaveBeenCalledWith(expect.objectContaining({ body }));
+ }
+
+ beforeEach(() => {
+ mockGetBulkOperationError.mockReset(); // reset calls and return undefined by default
+ });
+
+ describe('errors', () => {
+ it('throws when spacesToAdd and spacesToRemove are empty', async () => {
+ const objects = [{ type: SHAREABLE_OBJ_TYPE, id: 'id-1' }];
+ const params = setup({ objects });
+
+ await expect(() => updateObjectsSpaces(params)).rejects.toThrow(
+ 'spacesToAdd and/or spacesToRemove must be a non-empty array of strings: Bad Request'
+ );
+ });
+
+ it('throws when spacesToAdd and spacesToRemove intersect', async () => {
+ const objects = [{ type: SHAREABLE_OBJ_TYPE, id: 'id-1' }];
+ const spacesToAdd = ['foo-space', 'bar-space'];
+ const spacesToRemove = ['bar-space', 'baz-space'];
+ const params = setup({ objects, spacesToAdd, spacesToRemove });
+
+ await expect(() => updateObjectsSpaces(params)).rejects.toThrow(
+ 'spacesToAdd and spacesToRemove cannot contain any of the same strings: Bad Request'
+ );
+ });
+
+ it('throws when mget cluster call fails', async () => {
+ const objects = [{ type: SHAREABLE_OBJ_TYPE, id: 'id-1' }];
+ const spacesToAdd = ['foo-space'];
+ const params = setup({ objects, spacesToAdd });
+ client.mget.mockReturnValueOnce(
+ elasticsearchClientMock.createErrorTransportRequestPromise(new Error('mget error'))
+ );
+
+ await expect(() => updateObjectsSpaces(params)).rejects.toThrow('mget error');
+ });
+
+ it('throws when bulk cluster call fails', async () => {
+ const objects = [{ type: SHAREABLE_OBJ_TYPE, id: 'id-1' }];
+ const spacesToAdd = ['foo-space'];
+ const params = setup({ objects, spacesToAdd });
+ mockMgetResults({ found: true });
+ client.bulk.mockReturnValueOnce(
+ elasticsearchClientMock.createErrorTransportRequestPromise(new Error('bulk error'))
+ );
+
+ await expect(() => updateObjectsSpaces(params)).rejects.toThrow('bulk error');
+ });
+
+ it('returns mix of type errors, mget/bulk cluster errors, and successes', async () => {
+ const obj1 = { type: SHAREABLE_HIDDEN_OBJ_TYPE, id: 'id-1' }; // invalid type (Not Found)
+ const obj2 = { type: NON_SHAREABLE_OBJ_TYPE, id: 'id-2' }; // non-shareable type (Bad Request)
+ // obj3 below is mocking an example where a SOC wrapper attempted to retrieve it in a pre-flight request but it was not found.
+ // Since it has 'spaces: []', that indicates it should be skipped for cluster calls and just returned as a Not Found error.
+ // Realistically this would not be intermingled with other requested objects that do not have 'spaces' arrays, but it's fine for this
+ // specific test case.
+ const obj3 = { type: SHAREABLE_OBJ_TYPE, id: 'id-3', spaces: [] }; // does not exist (Not Found)
+ const obj4 = { type: SHAREABLE_OBJ_TYPE, id: 'id-4' }; // mget error (found but doesn't exist in the current space)
+ const obj5 = { type: SHAREABLE_OBJ_TYPE, id: 'id-5' }; // mget error (Not Found)
+ const obj6 = { type: SHAREABLE_OBJ_TYPE, id: 'id-6' }; // bulk error (mocked as BULK_ERROR)
+ const obj7 = { type: SHAREABLE_OBJ_TYPE, id: 'id-7' }; // success
+
+ const objects = [obj1, obj2, obj3, obj4, obj5, obj6, obj7];
+ const spacesToAdd = ['foo-space'];
+ const params = setup({ objects, spacesToAdd });
+ mockMgetResults({ found: true }, { found: false }, { found: true }, { found: true }); // results for obj4, obj5, obj6, and obj7
+ mockRawDocExistsInNamespace.mockReturnValueOnce(false); // for obj4
+ mockRawDocExistsInNamespace.mockReturnValueOnce(true); // for obj6
+ mockRawDocExistsInNamespace.mockReturnValueOnce(true); // for obj7
+ mockBulkResults({ error: true }, { error: false }); // results for obj6 and obj7
+
+ const result = await updateObjectsSpaces(params);
+ expect(client.mget).toHaveBeenCalledTimes(1);
+ expectMgetArgs(obj4, obj5, obj6, obj7);
+ expect(mockRawDocExistsInNamespace).toHaveBeenCalledTimes(3);
+ expect(client.bulk).toHaveBeenCalledTimes(1);
+ expectBulkArgs({ action: 'update', object: obj6 }, { action: 'update', object: obj7 });
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: [], error: expect.objectContaining({ error: 'Not Found' }) },
+ { ...obj2, spaces: [], error: expect.objectContaining({ error: 'Bad Request' }) },
+ { ...obj3, spaces: [], error: expect.objectContaining({ error: 'Not Found' }) },
+ { ...obj4, spaces: [], error: expect.objectContaining({ error: 'Not Found' }) },
+ { ...obj5, spaces: [], error: expect.objectContaining({ error: 'Not Found' }) },
+ { ...obj6, spaces: [], error: BULK_ERROR },
+ { ...obj7, spaces: [EXISTING_SPACE, 'foo-space'] },
+ ]);
+ });
+ });
+
+ // Note: these test cases do not include requested objects that will result in errors (those are covered above)
+ describe('cluster and module calls', () => {
+ it('mget call skips objects that have "spaces" defined', async () => {
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [EXISTING_SPACE] }; // will not be retrieved
+ const obj2 = { type: SHAREABLE_OBJ_TYPE, id: 'id-2' }; // will be passed to mget
+
+ const objects = [obj1, obj2];
+ const spacesToAdd = ['foo-space'];
+ const params = setup({ objects, spacesToAdd });
+ mockMgetResults({ found: true }); // result for obj2
+ mockBulkResults({ error: false }, { error: false }); // results for obj1 and obj2
+
+ await updateObjectsSpaces(params);
+ expect(client.mget).toHaveBeenCalledTimes(1);
+ expectMgetArgs(obj2);
+ });
+
+ it('does not call mget if all objects have "spaces" defined', async () => {
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [EXISTING_SPACE] }; // will not be retrieved
+
+ const objects = [obj1];
+ const spacesToAdd = ['foo-space'];
+ const params = setup({ objects, spacesToAdd });
+ mockBulkResults({ error: false }); // result for obj1
+
+ await updateObjectsSpaces(params);
+ expect(client.mget).not.toHaveBeenCalled();
+ });
+
+ describe('bulk call skips objects that will not be changed', () => {
+ it('when adding spaces', async () => {
+ const space1 = 'space-to-add';
+ const space2 = 'other-space';
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [space1] }; // will not be changed
+ const obj2 = { type: SHAREABLE_OBJ_TYPE, id: 'id-2', spaces: [space2] }; // will be updated
+
+ const objects = [obj1, obj2];
+ const spacesToAdd = [space1];
+ const params = setup({ objects, spacesToAdd });
+ // this test case does not call mget
+ mockBulkResults({ error: false }); // result for obj2
+
+ await updateObjectsSpaces(params);
+ expect(client.bulk).toHaveBeenCalledTimes(1);
+ expectBulkArgs({
+ action: 'update',
+ object: { ...obj2, namespaces: [space2, space1] },
+ });
+ });
+
+ it('when removing spaces', async () => {
+ const space1 = 'space-to-remove';
+ const space2 = 'other-space';
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [space2] }; // will not be changed
+ const obj2 = { type: SHAREABLE_OBJ_TYPE, id: 'id-2', spaces: [space1, space2] }; // will be updated to remove space1
+ const obj3 = { type: SHAREABLE_OBJ_TYPE, id: 'id-3', spaces: [space1] }; // will be deleted (since it would have no spaces left)
+
+ const objects = [obj1, obj2, obj3];
+ const spacesToRemove = [space1];
+ const params = setup({ objects, spacesToRemove });
+ // this test case does not call mget
+ mockBulkResults({ error: false }, { error: false }); // results for obj2 and obj3
+
+ await updateObjectsSpaces(params);
+ expect(client.bulk).toHaveBeenCalledTimes(1);
+ expectBulkArgs(
+ { action: 'update', object: { ...obj2, namespaces: [space2] } },
+ { action: 'delete', object: obj3 }
+ );
+ });
+
+ it('when adding and removing spaces', async () => {
+ const space1 = 'space-to-add';
+ const space2 = 'space-to-remove';
+ const space3 = 'other-space';
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [space1] }; // will not be changed
+ const obj2 = { type: SHAREABLE_OBJ_TYPE, id: 'id-2', spaces: [space3] }; // will be updated to add space1
+ const obj3 = { type: SHAREABLE_OBJ_TYPE, id: 'id-3', spaces: [space1, space2] }; // will be updated to remove space2
+ const obj4 = { type: SHAREABLE_OBJ_TYPE, id: 'id-4', spaces: [space2, space3] }; // will be updated to add space1 and remove space2
+
+ const objects = [obj1, obj2, obj3, obj4];
+ const spacesToAdd = [space1];
+ const spacesToRemove = [space2];
+ const params = setup({ objects, spacesToAdd, spacesToRemove });
+ // this test case does not call mget
+ mockBulkResults({ error: false }, { error: false }, { error: false }); // results for obj2, obj3, and obj4
+
+ await updateObjectsSpaces(params);
+ expect(client.bulk).toHaveBeenCalledTimes(1);
+ expectBulkArgs(
+ { action: 'update', object: { ...obj2, namespaces: [space3, space1] } },
+ { action: 'update', object: { ...obj3, namespaces: [space1] } },
+ { action: 'update', object: { ...obj4, namespaces: [space3, space1] } }
+ );
+ });
+ });
+
+ describe('does not call bulk if all objects do not need to be changed', () => {
+ it('when adding spaces', async () => {
+ const space = 'space-to-add';
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [space] }; // will not be changed
+
+ const objects = [obj1];
+ const spacesToAdd = [space];
+ const params = setup({ objects, spacesToAdd });
+ // this test case does not call mget or bulk
+
+ await updateObjectsSpaces(params);
+ expect(client.bulk).not.toHaveBeenCalled();
+ });
+
+ it('when removing spaces', async () => {
+ const space1 = 'space-to-remove';
+ const space2 = 'other-space';
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [space2] }; // will not be changed
+
+ const objects = [obj1];
+ const spacesToRemove = [space1];
+ const params = setup({ objects, spacesToRemove });
+ // this test case does not call mget or bulk
+
+ await updateObjectsSpaces(params);
+ expect(client.bulk).not.toHaveBeenCalled();
+ });
+
+ it('when adding and removing spaces', async () => {
+ const space1 = 'space-to-add';
+ const space2 = 'space-to-remove';
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [space1] }; // will not be changed
+
+ const objects = [obj1];
+ const spacesToAdd = [space1];
+ const spacesToRemove = [space2];
+ const params = setup({ objects, spacesToAdd, spacesToRemove });
+ // this test case does not call mget or bulk
+
+ await updateObjectsSpaces(params);
+ expect(client.bulk).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('returns expected results', () => {
+ it('when adding spaces', async () => {
+ const space1 = 'space-to-add';
+ const space2 = 'other-space';
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [space1] }; // will not be changed
+ const obj2 = { type: SHAREABLE_OBJ_TYPE, id: 'id-2', spaces: [space2] }; // will be updated
+
+ const objects = [obj1, obj2];
+ const spacesToAdd = [space1];
+ const params = setup({ objects, spacesToAdd });
+ // this test case does not call mget
+ mockBulkResults({ error: false }); // result for obj2
+
+ const result = await updateObjectsSpaces(params);
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: [space1] },
+ { ...obj2, spaces: [space2, space1] },
+ ]);
+ });
+
+ it('when removing spaces', async () => {
+ const space1 = 'space-to-remove';
+ const space2 = 'other-space';
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [space2] }; // will not be changed
+ const obj2 = { type: SHAREABLE_OBJ_TYPE, id: 'id-2', spaces: [space1, space2] }; // will be updated to remove space1
+ const obj3 = { type: SHAREABLE_OBJ_TYPE, id: 'id-3', spaces: [space1] }; // will be deleted (since it would have no spaces left)
+
+ const objects = [obj1, obj2, obj3];
+ const spacesToRemove = [space1];
+ const params = setup({ objects, spacesToRemove });
+ // this test case does not call mget
+ mockBulkResults({ error: false }, { error: false }); // results for obj2 and obj3
+
+ const result = await updateObjectsSpaces(params);
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: [space2] },
+ { ...obj2, spaces: [space2] },
+ { ...obj3, spaces: [] },
+ ]);
+ });
+
+ it('when adding and removing spaces', async () => {
+ const space1 = 'space-to-add';
+ const space2 = 'space-to-remove';
+ const space3 = 'other-space';
+ const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1', spaces: [space1] }; // will not be changed
+ const obj2 = { type: SHAREABLE_OBJ_TYPE, id: 'id-2', spaces: [space3] }; // will be updated to add space1
+ const obj3 = { type: SHAREABLE_OBJ_TYPE, id: 'id-3', spaces: [space1, space2] }; // will be updated to remove space2
+ const obj4 = { type: SHAREABLE_OBJ_TYPE, id: 'id-4', spaces: [space2, space3] }; // will be updated to add space1 and remove space2
+
+ const objects = [obj1, obj2, obj3, obj4];
+ const spacesToAdd = [space1];
+ const spacesToRemove = [space2];
+ const params = setup({ objects, spacesToAdd, spacesToRemove });
+ // this test case does not call mget
+ mockBulkResults({ error: false }, { error: false }, { error: false }); // results for obj2, obj3, and obj4
+
+ const result = await updateObjectsSpaces(params);
+ expect(result.objects).toEqual([
+ { ...obj1, spaces: [space1] },
+ { ...obj2, spaces: [space3, space1] },
+ { ...obj3, spaces: [space1] },
+ { ...obj4, spaces: [space3, space1] },
+ ]);
+ });
+ });
+});
diff --git a/src/core/server/saved_objects/service/lib/update_objects_spaces.ts b/src/core/server/saved_objects/service/lib/update_objects_spaces.ts
new file mode 100644
index 0000000000000..079549265385c
--- /dev/null
+++ b/src/core/server/saved_objects/service/lib/update_objects_spaces.ts
@@ -0,0 +1,315 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import type { BulkOperationContainer, MultiGetOperation } from '@elastic/elasticsearch/api/types';
+import intersection from 'lodash/intersection';
+
+import type { ISavedObjectTypeRegistry } from '../../saved_objects_type_registry';
+import type { SavedObjectsRawDocSource, SavedObjectsSerializer } from '../../serialization';
+import type {
+ MutatingOperationRefreshSetting,
+ SavedObjectError,
+ SavedObjectsBaseOptions,
+} from '../../types';
+import type { DecoratedError } from './errors';
+import { SavedObjectsErrorHelpers } from './errors';
+import {
+ getBulkOperationError,
+ getExpectedVersionProperties,
+ rawDocExistsInNamespace,
+} from './internal_utils';
+import { DEFAULT_REFRESH_SETTING } from './repository';
+import type { RepositoryEsClient } from './repository_es_client';
+
+/**
+ * An object that should have its spaces updated.
+ *
+ * @public
+ */
+export interface SavedObjectsUpdateObjectsSpacesObject {
+ /** The type of the object to update */
+ id: string;
+ /** The ID of the object to update */
+ type: string;
+ /**
+ * The space(s) that the object to update currently exists in. This is only intended to be used by SOC wrappers.
+ *
+ * @internal
+ */
+ spaces?: string[];
+ /**
+ * The version of the object to update; this is used for optimistic concurrency control. This is only intended to be used by SOC wrappers.
+ *
+ * @internal
+ */
+ version?: string;
+}
+
+/**
+ * Options for the update operation.
+ *
+ * @public
+ */
+export interface SavedObjectsUpdateObjectsSpacesOptions extends SavedObjectsBaseOptions {
+ /** The Elasticsearch Refresh setting for this operation */
+ refresh?: MutatingOperationRefreshSetting;
+}
+
+/**
+ * The response when objects' spaces are updated.
+ *
+ * @public
+ */
+export interface SavedObjectsUpdateObjectsSpacesResponse {
+ objects: SavedObjectsUpdateObjectsSpacesResponseObject[];
+}
+
+/**
+ * Details about a specific object's update result.
+ *
+ * @public
+ */
+export interface SavedObjectsUpdateObjectsSpacesResponseObject {
+ /** The type of the referenced object */
+ type: string;
+ /** The ID of the referenced object */
+ id: string;
+ /** The space(s) that the referenced object exists in */
+ spaces: string[];
+ /** Included if there was an error updating this object's spaces */
+ error?: SavedObjectError;
+}
+
+// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
+type Left = { tag: 'Left'; error: SavedObjectsUpdateObjectsSpacesResponseObject };
+// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
+type Right = { tag: 'Right'; value: Record };
+type Either = Left | Right;
+const isLeft = (either: Either): either is Left => either.tag === 'Left';
+const isRight = (either: Either): either is Right => either.tag === 'Right';
+
+/**
+ * Parameters for the updateObjectsSpaces function.
+ *
+ * @internal
+ */
+export interface UpdateObjectsSpacesParams {
+ registry: ISavedObjectTypeRegistry;
+ allowedTypes: string[];
+ client: RepositoryEsClient;
+ serializer: SavedObjectsSerializer;
+ getIndexForType: (type: string) => string;
+ objects: SavedObjectsUpdateObjectsSpacesObject[];
+ spacesToAdd: string[];
+ spacesToRemove: string[];
+ options?: SavedObjectsUpdateObjectsSpacesOptions;
+}
+
+/**
+ * Gets all references and transitive references of the given objects. Ignores any object and/or reference that is not a multi-namespace
+ * type.
+ */
+export async function updateObjectsSpaces({
+ registry,
+ allowedTypes,
+ client,
+ serializer,
+ getIndexForType,
+ objects,
+ spacesToAdd,
+ spacesToRemove,
+ options = {},
+}: UpdateObjectsSpacesParams): Promise {
+ if (!spacesToAdd.length && !spacesToRemove.length) {
+ throw SavedObjectsErrorHelpers.createBadRequestError(
+ 'spacesToAdd and/or spacesToRemove must be a non-empty array of strings'
+ );
+ }
+ if (intersection(spacesToAdd, spacesToRemove).length > 0) {
+ throw SavedObjectsErrorHelpers.createBadRequestError(
+ 'spacesToAdd and spacesToRemove cannot contain any of the same strings'
+ );
+ }
+
+ const { namespace } = options;
+
+ let bulkGetRequestIndexCounter = 0;
+ const expectedBulkGetResults: Either[] = objects.map((object) => {
+ const { type, id, spaces, version } = object;
+
+ if (!allowedTypes.includes(type)) {
+ const error = errorContent(SavedObjectsErrorHelpers.createGenericNotFoundError(type, id));
+ return {
+ tag: 'Left' as 'Left',
+ error: { id, type, spaces: [], error },
+ };
+ }
+ if (!registry.isShareable(type)) {
+ const error = errorContent(
+ SavedObjectsErrorHelpers.createBadRequestError(
+ `${type} doesn't support multiple namespaces`
+ )
+ );
+ return {
+ tag: 'Left' as 'Left',
+ error: { id, type, spaces: [], error },
+ };
+ }
+
+ return {
+ tag: 'Right' as 'Right',
+ value: {
+ type,
+ id,
+ spaces,
+ version,
+ ...(!spaces && { esRequestIndex: bulkGetRequestIndexCounter++ }),
+ },
+ };
+ });
+
+ const bulkGetDocs = expectedBulkGetResults.reduce((acc, x) => {
+ if (isRight(x) && x.value.esRequestIndex !== undefined) {
+ acc.push({
+ _id: serializer.generateRawId(undefined, x.value.type, x.value.id),
+ _index: getIndexForType(x.value.type),
+ _source: ['type', 'namespaces'],
+ });
+ }
+ return acc;
+ }, []);
+ const bulkGetResponse = bulkGetDocs.length
+ ? await client.mget(
+ { body: { docs: bulkGetDocs } },
+ { ignore: [404] }
+ )
+ : undefined;
+
+ const time = new Date().toISOString();
+ let bulkOperationRequestIndexCounter = 0;
+ const bulkOperationParams: BulkOperationContainer[] = [];
+ const expectedBulkOperationResults: Either[] = expectedBulkGetResults.map(
+ (expectedBulkGetResult) => {
+ if (isLeft(expectedBulkGetResult)) {
+ return expectedBulkGetResult;
+ }
+
+ const { id, type, spaces, version, esRequestIndex } = expectedBulkGetResult.value;
+
+ let currentSpaces: string[] = spaces;
+ let versionProperties;
+ if (esRequestIndex !== undefined) {
+ const doc = bulkGetResponse?.body.docs[esRequestIndex];
+ // @ts-expect-error MultiGetHit._source is optional
+ if (!doc?.found || !rawDocExistsInNamespace(registry, doc, namespace)) {
+ const error = errorContent(SavedObjectsErrorHelpers.createGenericNotFoundError(type, id));
+ return {
+ tag: 'Left' as 'Left',
+ error: { id, type, spaces: [], error },
+ };
+ }
+ currentSpaces = doc._source?.namespaces ?? [];
+ // @ts-expect-error MultiGetHit._source is optional
+ versionProperties = getExpectedVersionProperties(version, doc);
+ } else if (spaces?.length === 0) {
+ // A SOC wrapper attempted to retrieve this object in a pre-flight request and it was not found.
+ const error = errorContent(SavedObjectsErrorHelpers.createGenericNotFoundError(type, id));
+ return {
+ tag: 'Left' as 'Left',
+ error: { id, type, spaces: [], error },
+ };
+ } else {
+ versionProperties = getExpectedVersionProperties(version);
+ }
+
+ const { newSpaces, isUpdateRequired } = getNewSpacesArray(
+ currentSpaces,
+ spacesToAdd,
+ spacesToRemove
+ );
+ const expectedResult = {
+ type,
+ id,
+ newSpaces,
+ ...(isUpdateRequired && { esRequestIndex: bulkOperationRequestIndexCounter++ }),
+ };
+
+ if (isUpdateRequired) {
+ const documentMetadata = {
+ _id: serializer.generateRawId(undefined, type, id),
+ _index: getIndexForType(type),
+ ...versionProperties,
+ };
+ if (newSpaces.length) {
+ const documentToSave = { updated_at: time, namespaces: newSpaces };
+ // @ts-expect-error BulkOperation.retry_on_conflict, BulkOperation.routing. BulkOperation.version, and BulkOperation.version_type are optional
+ bulkOperationParams.push({ update: documentMetadata }, { doc: documentToSave });
+ } else {
+ // @ts-expect-error BulkOperation.retry_on_conflict, BulkOperation.routing. BulkOperation.version, and BulkOperation.version_type are optional
+ bulkOperationParams.push({ delete: documentMetadata });
+ }
+ }
+
+ return { tag: 'Right' as 'Right', value: expectedResult };
+ }
+ );
+
+ const { refresh = DEFAULT_REFRESH_SETTING } = options;
+ const bulkOperationResponse = bulkOperationParams.length
+ ? await client.bulk({ refresh, body: bulkOperationParams, require_alias: true })
+ : undefined;
+
+ return {
+ objects: expectedBulkOperationResults.map(
+ (expectedResult) => {
+ if (isLeft(expectedResult)) {
+ return expectedResult.error;
+ }
+
+ const { type, id, newSpaces, esRequestIndex } = expectedResult.value;
+ if (esRequestIndex !== undefined) {
+ const response = bulkOperationResponse?.body.items[esRequestIndex] ?? {};
+ const rawResponse = Object.values(response)[0] as any;
+ const error = getBulkOperationError(type, id, rawResponse);
+ if (error) {
+ return { id, type, spaces: [], error };
+ }
+ }
+
+ return { id, type, spaces: newSpaces };
+ }
+ ),
+ };
+}
+
+/** Extracts the contents of a decorated error to return the attributes for bulk operations. */
+function errorContent(error: DecoratedError) {
+ return error.output.payload;
+}
+
+/** Gets the remaining spaces for an object after adding new ones and removing old ones. */
+function getNewSpacesArray(
+ existingSpaces: string[],
+ spacesToAdd: string[],
+ spacesToRemove: string[]
+) {
+ const addSet = new Set(spacesToAdd);
+ const removeSet = new Set(spacesToRemove);
+ const newSpaces = existingSpaces
+ .filter((x) => {
+ addSet.delete(x);
+ return !removeSet.delete(x);
+ })
+ .concat(Array.from(addSet));
+
+ const isAnySpaceAdded = addSet.size > 0;
+ const isAnySpaceRemoved = removeSet.size < spacesToRemove.length;
+ const isUpdateRequired = isAnySpaceAdded || isAnySpaceRemoved;
+
+ return { newSpaces, isUpdateRequired };
+}
diff --git a/src/core/server/saved_objects/service/saved_objects_client.mock.ts b/src/core/server/saved_objects/service/saved_objects_client.mock.ts
index 544e92e32f1a1..e02387d41addf 100644
--- a/src/core/server/saved_objects/service/saved_objects_client.mock.ts
+++ b/src/core/server/saved_objects/service/saved_objects_client.mock.ts
@@ -26,9 +26,9 @@ const create = () => {
openPointInTimeForType: jest.fn().mockResolvedValue({ id: 'some_pit_id' }),
resolve: jest.fn(),
update: jest.fn(),
- addToNamespaces: jest.fn(),
- deleteFromNamespaces: jest.fn(),
removeReferencesTo: jest.fn(),
+ collectMultiNamespaceReferences: jest.fn(),
+ updateObjectsSpaces: jest.fn(),
} as unknown) as jest.Mocked;
mock.createPointInTimeFinder = savedObjectsPointInTimeFinderMock.create({
diff --git a/src/core/server/saved_objects/service/saved_objects_client.test.js b/src/core/server/saved_objects/service/saved_objects_client.test.js
index 29381c7e418b5..1a369475f2c6d 100644
--- a/src/core/server/saved_objects/service/saved_objects_client.test.js
+++ b/src/core/server/saved_objects/service/saved_objects_client.test.js
@@ -237,52 +237,39 @@ test(`#bulkUpdate`, async () => {
expect(result).toBe(returnValue);
});
-test(`#addToNamespaces`, async () => {
+test(`#collectMultiNamespaceReferences`, async () => {
const returnValue = Symbol();
const mockRepository = {
- addToNamespaces: jest.fn().mockResolvedValue(returnValue),
+ collectMultiNamespaceReferences: jest.fn().mockResolvedValue(returnValue),
};
const client = new SavedObjectsClient(mockRepository);
- const type = Symbol();
- const id = Symbol();
- const namespaces = Symbol();
- const options = Symbol();
- const result = await client.addToNamespaces(type, id, namespaces, options);
-
- expect(mockRepository.addToNamespaces).toHaveBeenCalledWith(type, id, namespaces, options);
- expect(result).toBe(returnValue);
-});
-
-test(`#deleteFromNamespaces`, async () => {
- const returnValue = Symbol();
- const mockRepository = {
- deleteFromNamespaces: jest.fn().mockResolvedValue(returnValue),
- };
- const client = new SavedObjectsClient(mockRepository);
-
- const type = Symbol();
- const id = Symbol();
- const namespaces = Symbol();
+ const objects = Symbol();
const options = Symbol();
- const result = await client.deleteFromNamespaces(type, id, namespaces, options);
+ const result = await client.collectMultiNamespaceReferences(objects, options);
- expect(mockRepository.deleteFromNamespaces).toHaveBeenCalledWith(type, id, namespaces, options);
+ expect(mockRepository.collectMultiNamespaceReferences).toHaveBeenCalledWith(objects, options);
expect(result).toBe(returnValue);
});
-test(`#removeReferencesTo`, async () => {
+test(`#updateObjectsSpaces`, async () => {
const returnValue = Symbol();
const mockRepository = {
- removeReferencesTo: jest.fn().mockResolvedValue(returnValue),
+ updateObjectsSpaces: jest.fn().mockResolvedValue(returnValue),
};
const client = new SavedObjectsClient(mockRepository);
- const type = Symbol();
- const id = Symbol();
+ const objects = Symbol();
+ const spacesToAdd = Symbol();
+ const spacesToRemove = Symbol();
const options = Symbol();
- const result = await client.removeReferencesTo(type, id, options);
-
- expect(mockRepository.removeReferencesTo).toHaveBeenCalledWith(type, id, options);
+ const result = await client.updateObjectsSpaces(objects, spacesToAdd, spacesToRemove, options);
+
+ expect(mockRepository.updateObjectsSpaces).toHaveBeenCalledWith(
+ objects,
+ spacesToAdd,
+ spacesToRemove,
+ options
+ );
expect(result).toBe(returnValue);
});
diff --git a/src/core/server/saved_objects/service/saved_objects_client.ts b/src/core/server/saved_objects/service/saved_objects_client.ts
index bf5cae0736cad..af682cfb81296 100644
--- a/src/core/server/saved_objects/service/saved_objects_client.ts
+++ b/src/core/server/saved_objects/service/saved_objects_client.ts
@@ -11,6 +11,11 @@ import type {
ISavedObjectsPointInTimeFinder,
SavedObjectsCreatePointInTimeFinderOptions,
SavedObjectsCreatePointInTimeFinderDependencies,
+ SavedObjectsCollectMultiNamespaceReferencesObject,
+ SavedObjectsCollectMultiNamespaceReferencesOptions,
+ SavedObjectsCollectMultiNamespaceReferencesResponse,
+ SavedObjectsUpdateObjectsSpacesObject,
+ SavedObjectsUpdateObjectsSpacesOptions,
} from './lib';
import {
SavedObject,
@@ -218,44 +223,6 @@ export interface SavedObjectsUpdateOptions extends SavedOb
upsert?: Attributes;
}
-/**
- *
- * @public
- */
-export interface SavedObjectsAddToNamespacesOptions extends SavedObjectsBaseOptions {
- /** An opaque version number which changes on each successful write operation. Can be used for implementing optimistic concurrency control. */
- version?: string;
- /** The Elasticsearch Refresh setting for this operation */
- refresh?: MutatingOperationRefreshSetting;
-}
-
-/**
- *
- * @public
- */
-export interface SavedObjectsAddToNamespacesResponse {
- /** The namespaces the object exists in after this operation is complete. */
- namespaces: string[];
-}
-
-/**
- *
- * @public
- */
-export interface SavedObjectsDeleteFromNamespacesOptions extends SavedObjectsBaseOptions {
- /** The Elasticsearch Refresh setting for this operation */
- refresh?: MutatingOperationRefreshSetting;
-}
-
-/**
- *
- * @public
- */
-export interface SavedObjectsDeleteFromNamespacesResponse {
- /** The namespaces the object exists in after this operation is complete. An empty array indicates the object was deleted. */
- namespaces: string[];
-}
-
/**
*
* @public
@@ -536,40 +503,6 @@ export class SavedObjectsClient {
return await this._repository.update(type, id, attributes, options);
}
- /**
- * Adds namespaces to a SavedObject
- *
- * @param type
- * @param id
- * @param namespaces
- * @param options
- */
- async addToNamespaces(
- type: string,
- id: string,
- namespaces: string[],
- options: SavedObjectsAddToNamespacesOptions = {}
- ): Promise {
- return await this._repository.addToNamespaces(type, id, namespaces, options);
- }
-
- /**
- * Removes namespaces from a SavedObject
- *
- * @param type
- * @param id
- * @param namespaces
- * @param options
- */
- async deleteFromNamespaces(
- type: string,
- id: string,
- namespaces: string[],
- options: SavedObjectsDeleteFromNamespacesOptions = {}
- ): Promise {
- return await this._repository.deleteFromNamespaces(type, id, namespaces, options);
- }
-
/**
* Bulk Updates multiple SavedObject at once
*
@@ -665,14 +598,49 @@ export class SavedObjectsClient {
* }
* ```
*/
- createPointInTimeFinder(
+ createPointInTimeFinder(
findOptions: SavedObjectsCreatePointInTimeFinderOptions,
dependencies?: SavedObjectsCreatePointInTimeFinderDependencies
- ): ISavedObjectsPointInTimeFinder {
+ ): ISavedObjectsPointInTimeFinder {
return this._repository.createPointInTimeFinder(findOptions, {
client: this,
// Include dependencies last so that SO client wrappers have their settings applied.
...dependencies,
});
}
+
+ /**
+ * Gets all references and transitive references of the listed objects. Ignores any object that is not a multi-namespace type.
+ *
+ * @param objects
+ * @param options
+ */
+ async collectMultiNamespaceReferences(
+ objects: SavedObjectsCollectMultiNamespaceReferencesObject[],
+ options?: SavedObjectsCollectMultiNamespaceReferencesOptions
+ ): Promise {
+ return await this._repository.collectMultiNamespaceReferences(objects, options);
+ }
+
+ /**
+ * Updates one or more objects to add and/or remove them from specified spaces.
+ *
+ * @param objects
+ * @param spacesToAdd
+ * @param spacesToRemove
+ * @param options
+ */
+ async updateObjectsSpaces(
+ objects: SavedObjectsUpdateObjectsSpacesObject[],
+ spacesToAdd: string[],
+ spacesToRemove: string[],
+ options?: SavedObjectsUpdateObjectsSpacesOptions
+ ) {
+ return await this._repository.updateObjectsSpaces(
+ objects,
+ spacesToAdd,
+ spacesToRemove,
+ options
+ );
+ }
}
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index 4c12ca53b9098..3e6a69d159192 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -976,6 +976,8 @@ export interface ElasticsearchStatusMeta {
// (undocumented)
incompatibleNodes: NodesVersionCompatibility['incompatibleNodes'];
// (undocumented)
+ nodesInfoRequestError?: NodesVersionCompatibility['nodesInfoRequestError'];
+ // (undocumented)
warningNodes: NodesVersionCompatibility['warningNodes'];
}
@@ -1253,9 +1255,9 @@ export type ISavedObjectsExporter = PublicMethodsOf;
export type ISavedObjectsImporter = PublicMethodsOf;
// @public (undocumented)
-export interface ISavedObjectsPointInTimeFinder {
+export interface ISavedObjectsPointInTimeFinder {
close: () => Promise;
- find: () => AsyncGenerator;
+ find: () => AsyncGenerator>;
}
// @public
@@ -1727,6 +1729,8 @@ export interface NodesVersionCompatibility {
// (undocumented)
message?: string;
// (undocumented)
+ nodesInfoRequestError?: Error;
+ // (undocumented)
warningNodes: NodeInfo[];
}
@@ -2140,6 +2144,7 @@ export type SavedObjectAttributeSingle = string | number | boolean | null | unde
// @public (undocumented)
export interface SavedObjectExportBaseOptions {
excludeExportDetails?: boolean;
+ includeNamespaces?: boolean;
includeReferencesDeep?: boolean;
namespace?: string;
request: KibanaRequest;
@@ -2147,9 +2152,9 @@ export interface SavedObjectExportBaseOptions {
// @public
export interface SavedObjectMigrationContext {
- convertToMultiNamespaceTypeVersion?: string;
- log: SavedObjectsMigrationLogger;
- migrationVersion: string;
+ readonly convertToMultiNamespaceTypeVersion?: string;
+ readonly log: SavedObjectsMigrationLogger;
+ readonly migrationVersion: string;
}
// @public
@@ -2171,15 +2176,18 @@ export interface SavedObjectReference {
type: string;
}
-// @public (undocumented)
-export interface SavedObjectsAddToNamespacesOptions extends SavedObjectsBaseOptions {
- refresh?: MutatingOperationRefreshSetting;
- version?: string;
-}
-
-// @public (undocumented)
-export interface SavedObjectsAddToNamespacesResponse {
- namespaces: string[];
+// @public
+export interface SavedObjectReferenceWithContext {
+ id: string;
+ inboundReferences: Array<{
+ type: string;
+ id: string;
+ name: string;
+ }>;
+ isMissing?: boolean;
+ spaces: string[];
+ spacesWithMatchingAliases?: string[];
+ type: string;
}
// Warning: (ae-forgotten-export) The symbol "SavedObjectDoc" needs to be exported by the entry point index.d.ts
@@ -2273,16 +2281,15 @@ export interface SavedObjectsCheckConflictsResponse {
export class SavedObjectsClient {
// @internal
constructor(repository: ISavedObjectsRepository);
- addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise;
bulkCreate(objects: Array>, options?: SavedObjectsCreateOptions): Promise>;
bulkGet(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise>;
bulkUpdate(objects: Array>, options?: SavedObjectsBulkUpdateOptions): Promise>;
checkConflicts(objects?: SavedObjectsCheckConflictsObject[], options?: SavedObjectsBaseOptions): Promise;
closePointInTime(id: string, options?: SavedObjectsClosePointInTimeOptions): Promise;
+ collectMultiNamespaceReferences(objects: SavedObjectsCollectMultiNamespaceReferencesObject[], options?: SavedObjectsCollectMultiNamespaceReferencesOptions): Promise;
create(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise>;
- createPointInTimeFinder(findOptions: SavedObjectsCreatePointInTimeFinderOptions, dependencies?: SavedObjectsCreatePointInTimeFinderDependencies): ISavedObjectsPointInTimeFinder;
+ createPointInTimeFinder(findOptions: SavedObjectsCreatePointInTimeFinderOptions, dependencies?: SavedObjectsCreatePointInTimeFinderDependencies): ISavedObjectsPointInTimeFinder;
delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>;
- deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise;
// (undocumented)
static errors: typeof SavedObjectsErrorHelpers;
// (undocumented)
@@ -2293,6 +2300,7 @@ export class SavedObjectsClient {
removeReferencesTo(type: string, id: string, options?: SavedObjectsRemoveReferencesToOptions): Promise;
resolve(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>;
update(type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions): Promise>;
+ updateObjectsSpaces(objects: SavedObjectsUpdateObjectsSpacesObject[], spacesToAdd: string[], spacesToRemove: string[], options?: SavedObjectsUpdateObjectsSpacesOptions): Promise;
}
// @public
@@ -2337,6 +2345,25 @@ export interface SavedObjectsClosePointInTimeResponse {
succeeded: boolean;
}
+// @public
+export interface SavedObjectsCollectMultiNamespaceReferencesObject {
+ // (undocumented)
+ id: string;
+ // (undocumented)
+ type: string;
+}
+
+// @public
+export interface SavedObjectsCollectMultiNamespaceReferencesOptions extends SavedObjectsBaseOptions {
+ purpose?: 'collectMultiNamespaceReferences' | 'updateObjectsSpaces';
+}
+
+// @public
+export interface SavedObjectsCollectMultiNamespaceReferencesResponse {
+ // (undocumented)
+ objects: SavedObjectReferenceWithContext[];
+}
+
// @public
export interface SavedObjectsComplexFieldMapping {
// (undocumented)
@@ -2397,16 +2424,6 @@ export interface SavedObjectsDeleteByNamespaceOptions extends SavedObjectsBaseOp
refresh?: boolean;
}
-// @public (undocumented)
-export interface SavedObjectsDeleteFromNamespacesOptions extends SavedObjectsBaseOptions {
- refresh?: MutatingOperationRefreshSetting;
-}
-
-// @public (undocumented)
-export interface SavedObjectsDeleteFromNamespacesResponse {
- namespaces: string[];
-}
-
// @public (undocumented)
export interface SavedObjectsDeleteOptions extends SavedObjectsBaseOptions {
force?: boolean;
@@ -2880,21 +2897,20 @@ export interface SavedObjectsRemoveReferencesToResponse extends SavedObjectsBase
// @public (undocumented)
export class SavedObjectsRepository {
- addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise;
bulkCreate(objects: Array>, options?: SavedObjectsCreateOptions): Promise>;
bulkGet(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise>;
bulkUpdate(objects: Array>, options?: SavedObjectsBulkUpdateOptions): Promise>;
checkConflicts(objects?: SavedObjectsCheckConflictsObject[], options?: SavedObjectsBaseOptions): Promise;
closePointInTime(id: string, options?: SavedObjectsClosePointInTimeOptions): Promise;
+ collectMultiNamespaceReferences(objects: SavedObjectsCollectMultiNamespaceReferencesObject[], options?: SavedObjectsCollectMultiNamespaceReferencesOptions): Promise;
create(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise>;
- createPointInTimeFinder(findOptions: SavedObjectsCreatePointInTimeFinderOptions, dependencies?: SavedObjectsCreatePointInTimeFinderDependencies): ISavedObjectsPointInTimeFinder;
+ createPointInTimeFinder(findOptions: SavedObjectsCreatePointInTimeFinderOptions, dependencies?: SavedObjectsCreatePointInTimeFinderDependencies): ISavedObjectsPointInTimeFinder;
// Warning: (ae-forgotten-export) The symbol "IKibanaMigrator" needs to be exported by the entry point index.d.ts
//
// @internal
- static createRepository(migrator: IKibanaMigrator, typeRegistry: SavedObjectTypeRegistry, indexName: string, client: ElasticsearchClient, logger: Logger, includedHiddenTypes?: string[], injectedConstructor?: any): ISavedObjectsRepository;
+ static createRepository(migrator: IKibanaMigrator, typeRegistry: ISavedObjectTypeRegistry, indexName: string, client: ElasticsearchClient, logger: Logger, includedHiddenTypes?: string[], injectedConstructor?: any): ISavedObjectsRepository;
delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>;
deleteByNamespace(namespace: string, options?: SavedObjectsDeleteByNamespaceOptions): Promise;
- deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise;
// (undocumented)
find(options: SavedObjectsFindOptions): Promise>;
get(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>;
@@ -2903,6 +2919,7 @@ export class SavedObjectsRepository {
removeReferencesTo(type: string, id: string, options?: SavedObjectsRemoveReferencesToOptions): Promise;
resolve(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>;
update(type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions): Promise>;
+ updateObjectsSpaces(objects: SavedObjectsUpdateObjectsSpacesObject[], spacesToAdd: string[], spacesToRemove: string[], options?: SavedObjectsUpdateObjectsSpacesOptions): Promise;
}
// @public
@@ -2934,7 +2951,7 @@ export class SavedObjectsSerializer {
generateRawId(namespace: string | undefined, type: string, id: string): string;
generateRawLegacyUrlAliasId(namespace: string, type: string, id: string): string;
isRawSavedObject(doc: SavedObjectsRawDoc, options?: SavedObjectsRawDocParseOptions): boolean;
- rawToSavedObject(doc: SavedObjectsRawDoc, options?: SavedObjectsRawDocParseOptions): SavedObjectSanitizedDoc;
+ rawToSavedObject(doc: SavedObjectsRawDoc, options?: SavedObjectsRawDocParseOptions): SavedObjectSanitizedDoc;
savedObjectToRaw(savedObj: SavedObjectSanitizedDoc): SavedObjectsRawDoc;
}
@@ -3000,6 +3017,35 @@ export interface SavedObjectsTypeMappingDefinition {
properties: SavedObjectsMappingProperties;
}
+// @public
+export interface SavedObjectsUpdateObjectsSpacesObject {
+ id: string;
+ // @internal
+ spaces?: string[];
+ type: string;
+ // @internal
+ version?: string;
+}
+
+// @public
+export interface SavedObjectsUpdateObjectsSpacesOptions extends SavedObjectsBaseOptions {
+ refresh?: MutatingOperationRefreshSetting;
+}
+
+// @public
+export interface SavedObjectsUpdateObjectsSpacesResponse {
+ // (undocumented)
+ objects: SavedObjectsUpdateObjectsSpacesResponseObject[];
+}
+
+// @public
+export interface SavedObjectsUpdateObjectsSpacesResponseObject {
+ error?: SavedObjectError;
+ id: string;
+ spaces: string[];
+ type: string;
+}
+
// @public (undocumented)
export interface SavedObjectsUpdateOptions extends SavedObjectsBaseOptions {
references?: SavedObjectReference[];
diff --git a/src/core/server/types.ts b/src/core/server/types.ts
index be07a3cfb1fd3..77b5378f9477f 100644
--- a/src/core/server/types.ts
+++ b/src/core/server/types.ts
@@ -37,6 +37,10 @@ export type {
SavedObjectsClientContract,
SavedObjectsNamespaceType,
} from './saved_objects/types';
+export type {
+ SavedObjectReferenceWithContext,
+ SavedObjectsCollectMultiNamespaceReferencesResponse,
+} from './saved_objects/service';
export type { DomainDeprecationDetails, DeprecationsGetResponse } from './deprecations/types';
export * from './ui_settings/types';
export type { EnvironmentMode, PackageInfo } from '@kbn/config';
diff --git a/src/dev/run_find_plugins_with_circular_deps.ts b/src/dev/run_find_plugins_with_circular_deps.ts
index a737bc6a73004..4ce71b24332c1 100644
--- a/src/dev/run_find_plugins_with_circular_deps.ts
+++ b/src/dev/run_find_plugins_with_circular_deps.ts
@@ -19,9 +19,7 @@ interface Options {
type CircularDepList = Set;
-const allowedList: CircularDepList = new Set([
- 'x-pack/plugins/lists -> x-pack/plugins/security_solution',
-]);
+const allowedList: CircularDepList = new Set([]);
run(
async ({ flags, log }) => {
diff --git a/src/plugins/console/public/lib/autocomplete/engine.js b/src/plugins/console/public/lib/autocomplete/engine.js
index 7852c9da7898f..bd72af3c0e8cf 100644
--- a/src/plugins/console/public/lib/autocomplete/engine.js
+++ b/src/plugins/console/public/lib/autocomplete/engine.js
@@ -146,7 +146,7 @@ export function populateContext(tokenPath, context, editor, includeAutoComplete,
if (!wsToUse && walkStates.length > 1 && !includeAutoComplete) {
console.info(
- "more then one context active for current path, but autocomplete isn't requested",
+ "more than one context active for current path, but autocomplete isn't requested",
walkStates
);
}
diff --git a/src/plugins/dashboard/common/saved_dashboard_references.ts b/src/plugins/dashboard/common/saved_dashboard_references.ts
index 16ab470ce7d6f..9757415a7bc36 100644
--- a/src/plugins/dashboard/common/saved_dashboard_references.ts
+++ b/src/plugins/dashboard/common/saved_dashboard_references.ts
@@ -5,8 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
-
-import semverSatisfies from 'semver/functions/satisfies';
+import Semver from 'semver';
import { SavedObjectAttributes, SavedObjectReference } from '../../../core/types';
import { DashboardContainerStateWithType, DashboardPanelState } from './types';
import { EmbeddablePersistableStateService } from '../../embeddable/common/types';
@@ -24,7 +23,7 @@ export interface SavedObjectAttributesAndReferences {
}
const isPre730Panel = (panel: Record): boolean => {
- return 'version' in panel ? semverSatisfies(panel.version, '<7.3') : true;
+ return 'version' in panel ? Semver.gt('7.3.0', panel.version) : true;
};
function dashboardAttributesToState(
@@ -82,6 +81,9 @@ export function extractReferences(
}
const { panels, state } = dashboardAttributesToState(attributes);
+ if (!Array.isArray(panels)) {
+ return { attributes, references };
+ }
if (((panels as unknown) as Array>).some(isPre730Panel)) {
return pre730ExtractReferences({ attributes, references }, deps);
diff --git a/src/plugins/data/common/index_patterns/fields/types.ts b/src/plugins/data/common/index_patterns/fields/types.ts
index fa8f6c3bc1dc8..565dd6d926948 100644
--- a/src/plugins/data/common/index_patterns/fields/types.ts
+++ b/src/plugins/data/common/index_patterns/fields/types.ts
@@ -8,6 +8,10 @@
import { FieldSpec, IFieldSubType, IndexPattern } from '../..';
+/**
+ * @deprecated
+ * Use IndexPatternField or FieldSpec instead
+ */
export interface IFieldType {
name: string;
type: string;
diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts
index c906b809b08c4..0fcdea1a878eb 100644
--- a/src/plugins/data/common/index_patterns/types.ts
+++ b/src/plugins/data/common/index_patterns/types.ts
@@ -25,8 +25,9 @@ export interface RuntimeField {
}
/**
+ * @deprecated
* IIndexPattern allows for an IndexPattern OR an index pattern saved object
- * too ambiguous, should be avoided
+ * Use IndexPattern or IndexPatternSpec instead
*/
export interface IIndexPattern {
fields: IFieldType[];
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index 54cea5e09121b..8561d7bf8d6f5 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -1178,7 +1178,7 @@ export interface IFieldSubType {
// Warning: (ae-missing-release-tag) "IFieldType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
-// @public (undocumented)
+// @public @deprecated (undocumented)
export interface IFieldType {
// (undocumented)
aggregatable?: boolean;
@@ -1222,7 +1222,7 @@ export interface IFieldType {
// Warning: (ae-missing-release-tag) "IIndexPattern" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
-// @public
+// @public @deprecated (undocumented)
export interface IIndexPattern {
// Warning: (ae-forgotten-export) The symbol "SerializedFieldFormat" needs to be exported by the entry point index.d.ts
//
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index a71ce360a2190..ffdff2e33cf9c 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -692,7 +692,7 @@ export interface IFieldSubType {
// Warning: (ae-missing-release-tag) "IFieldType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
-// @public (undocumented)
+// @public @deprecated (undocumented)
export interface IFieldType {
// (undocumented)
aggregatable?: boolean;
@@ -993,7 +993,7 @@ export class IndexPatternsServiceProvider implements Plugin_3, { expressions, usageCollection }: IndexPatternsServiceSetupDeps): void;
// (undocumented)
start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps): {
- indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: ElasticsearchClient_2) => Promise;
+ indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: ElasticsearchClient_2) => Promise;
};
}
@@ -1263,7 +1263,7 @@ export class Plugin implements Plugin_2 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/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts
index 58ddf1eb7ba25..1d6956fc80920 100644
--- a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts
+++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts
@@ -6,27 +6,17 @@
* Side Public License, v 1.
*/
-import { find, template } from 'lodash';
+import { find } from 'lodash';
import $ from 'jquery';
import openRowHtml from './table_row/open.html';
import detailsHtml from './table_row/details.html';
import { dispatchRenderComplete } from '../../../../../../kibana_utils/public';
import { DOC_HIDE_TIME_COLUMN_SETTING } from '../../../../../common';
-import cellTemplateHtml from '../components/table_row/cell.html';
-import truncateByHeightTemplateHtml from '../components/table_row/truncate_by_height.html';
import { getServices } from '../../../../kibana_services';
import { getContextUrl } from '../../../helpers/get_context_url';
import { formatRow, formatTopLevelObject } from '../../helpers';
-
-const TAGS_WITH_WS = />\s+<');
-}
+import { truncateByHeight } from './table_row/truncate_by_height';
+import { cell } from './table_row/cell';
// guesstimate at the minimum number of chars wide cells in the table should be
const MIN_LINE_LENGTH = 20;
@@ -37,9 +27,6 @@ interface LazyScope extends ng.IScope {
}
export function createTableRowDirective($compile: ng.ICompileService) {
- const cellTemplate = template(noWhiteSpace(cellTemplateHtml));
- const truncateByHeightTemplate = template(noWhiteSpace(truncateByHeightTemplateHtml));
-
return {
restrict: 'A',
scope: {
@@ -133,7 +120,7 @@ export function createTableRowDirective($compile: ng.ICompileService) {
const hideTimeColumn = getServices().uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false);
if (indexPattern.timeFieldName && !hideTimeColumn) {
newHtmls.push(
- cellTemplate({
+ cell({
timefield: true,
formatted: _displayField(row, indexPattern.timeFieldName),
filterable: mapping(indexPattern.timeFieldName).filterable && $scope.filter,
@@ -146,7 +133,7 @@ export function createTableRowDirective($compile: ng.ICompileService) {
const formatted = formatRow(row, indexPattern);
newHtmls.push(
- cellTemplate({
+ cell({
timefield: false,
sourcefield: true,
formatted,
@@ -164,7 +151,7 @@ export function createTableRowDirective($compile: ng.ICompileService) {
})
);
newHtmls.push(
- cellTemplate({
+ cell({
timefield: false,
sourcefield: true,
formatted: formatTopLevelObject(row, innerColumns, indexPattern),
@@ -174,7 +161,7 @@ export function createTableRowDirective($compile: ng.ICompileService) {
);
} else {
newHtmls.push(
- cellTemplate({
+ cell({
timefield: false,
sourcefield: column === '_source',
formatted: _displayField(row, column, true),
@@ -191,8 +178,8 @@ export function createTableRowDirective($compile: ng.ICompileService) {
const $cell = $cells.eq(i);
if ($cell.data('discover:html') === html) return;
- const reuse = find($cells.slice(i + 1), (cell) => {
- return $.data(cell, 'discover:html') === html;
+ const reuse = find($cells.slice(i + 1), (c) => {
+ return $.data(c, 'discover:html') === html;
});
const $target = reuse ? $(reuse).detach() : $(html);
@@ -231,7 +218,7 @@ export function createTableRowDirective($compile: ng.ICompileService) {
const text = indexPattern.formatField(row, fieldName);
if (truncate && text.length > MIN_LINE_LENGTH) {
- return truncateByHeightTemplate({
+ return truncateByHeight({
body: text,
});
}
diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row/cell.test.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_row/cell.test.ts
new file mode 100644
index 0000000000000..c6d0d324b9bc2
--- /dev/null
+++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/cell.test.ts
@@ -0,0 +1,138 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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 { cell } from './cell';
+
+describe('cell renderer', () => {
+ it('renders a cell without filter buttons if it is not filterable', () => {
+ expect(
+ cell({
+ filterable: false,
+ column: 'foo',
+ timefield: true,
+ sourcefield: false,
+ formatted: 'formatted content',
+ })
+ ).toMatchInlineSnapshot(`
+ "
formatted content
+ "
+ `);
+ });
+
+ it('renders a cell with filter buttons if it is filterable', () => {
+ expect(
+ cell({
+ filterable: true,
+ column: 'foo',
+ timefield: true,
+ sourcefield: false,
+ formatted: 'formatted content',
+ })
+ ).toMatchInlineSnapshot(`
+ "
\ No newline at end of file
diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row/truncate_by_height.test.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_row/truncate_by_height.test.ts
new file mode 100644
index 0000000000000..70d8465589237
--- /dev/null
+++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/truncate_by_height.test.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 { truncateByHeight } from './truncate_by_height';
+
+describe('truncateByHeight', () => {
+ it('renders input without any formatting or escaping', () => {
+ expect(
+ truncateByHeight({
+ body:
+ '
hey you can put HTML & stuff in here
',
+ })
+ ).toMatchInlineSnapshot(
+ `"
hey you can put HTML & stuff in here
"`
+ );
+ });
+});
diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row/truncate_by_height.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_row/truncate_by_height.ts
new file mode 100644
index 0000000000000..7eb31459eb4f5
--- /dev/null
+++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/truncate_by_height.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 truncateByHeight = ({ body }: { body: string }) => {
+ return `
${body}
`;
+};
diff --git a/src/plugins/home/server/services/index.ts b/src/plugins/home/server/services/index.ts
index 7f26c886ab4b6..5674a3501f064 100644
--- a/src/plugins/home/server/services/index.ts
+++ b/src/plugins/home/server/services/index.ts
@@ -15,7 +15,6 @@ export type { TutorialsRegistrySetup, TutorialsRegistryStart } from './tutorials
export { TutorialsCategory } from './tutorials';
export type {
- ParamTypes,
InstructionSetSchema,
ParamsSchema,
InstructionsSchema,
diff --git a/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts b/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts
index 4d9dc3885e67d..09af7728f74d2 100644
--- a/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts
+++ b/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts
@@ -6,7 +6,8 @@
* Side Public License, v 1.
*/
-import { SavedObject } from 'src/core/server';
+import type { SampleDatasetSchema } from './sample_dataset_schema';
+export type { SampleDatasetSchema, AppLinkSchema, DataIndexSchema } from './sample_dataset_schema';
export enum DatasetStatusTypes {
NOT_INSTALLED = 'not_installed',
@@ -26,57 +27,4 @@ export enum EmbeddableTypes {
SEARCH_EMBEDDABLE_TYPE = 'search',
VISUALIZE_EMBEDDABLE_TYPE = 'visualization',
}
-export interface DataIndexSchema {
- id: string;
-
- // path to newline delimented JSON file containing data relative to KIBANA_HOME
- dataPath: string;
-
- // Object defining Elasticsearch field mappings (contents of index.mappings.type.properties)
- fields: object;
-
- // times fields that will be updated relative to now when data is installed
- timeFields: string[];
-
- // Reference to now in your test data set.
- // When data is installed, timestamps are converted to the present time.
- // The distance between a timestamp and currentTimeMarker is preserved but the date and time will change.
- // For example:
- // sample data set: timestamp: 2018-01-01T00:00:00Z, currentTimeMarker: 2018-01-01T12:00:00Z
- // installed data set: timestamp: 2018-04-18T20:33:14Z, currentTimeMarker: 2018-04-19T08:33:14Z
- currentTimeMarker: string;
-
- // Set to true to move timestamp to current week, preserving day of week and time of day
- // Relative distance from timestamp to currentTimeMarker will not remain the same
- preserveDayOfWeekTimeOfDay: boolean;
-}
-
-export interface AppLinkSchema {
- path: string;
- icon: string;
- label: string;
-}
-
-export interface SampleDatasetSchema {
- id: string;
- name: string;
- description: string;
- previewImagePath: string;
- darkPreviewImagePath: string;
-
- // saved object id of main dashboard for sample data set
- overviewDashboard: string;
- appLinks: AppLinkSchema[];
-
- // saved object id of default index-pattern for sample data set
- defaultIndex: string;
-
- // Kibana saved objects (index patter, visualizations, dashboard, ...)
- // Should provide a nice demo of Kibana's functionality with the sample data set
- savedObjects: Array>;
- dataIndices: DataIndexSchema[];
- status?: string | undefined;
- statusMsg?: unknown;
-}
-
export type SampleDatasetProvider = () => SampleDatasetSchema;
diff --git a/src/plugins/home/server/services/sample_data/lib/sample_dataset_schema.ts b/src/plugins/home/server/services/sample_data/lib/sample_dataset_schema.ts
index eb0b2252774b5..3c1764b2b8df1 100644
--- a/src/plugins/home/server/services/sample_data/lib/sample_dataset_schema.ts
+++ b/src/plugins/home/server/services/sample_data/lib/sample_dataset_schema.ts
@@ -5,22 +5,27 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
+import type { Writable } from '@kbn/utility-types';
+import { schema, TypeOf } from '@kbn/config-schema';
-import Joi from 'joi';
-
-const dataIndexSchema = Joi.object({
- id: Joi.string()
- .regex(/^[a-zA-Z0-9-]+$/)
- .required(),
+const idRegExp = /^[a-zA-Z0-9-]+$/;
+const dataIndexSchema = schema.object({
+ id: schema.string({
+ validate(value: string) {
+ if (!idRegExp.test(value)) {
+ return `Does not satisfy regexp: ${idRegExp.toString()}`;
+ }
+ },
+ }),
// path to newline delimented JSON file containing data relative to KIBANA_HOME
- dataPath: Joi.string().required(),
+ dataPath: schema.string(),
// Object defining Elasticsearch field mappings (contents of index.mappings.type.properties)
- fields: Joi.object().required(),
+ fields: schema.recordOf(schema.string(), schema.any()),
// times fields that will be updated relative to now when data is installed
- timeFields: Joi.array().items(Joi.string()).required(),
+ timeFields: schema.arrayOf(schema.string()),
// Reference to now in your test data set.
// When data is installed, timestamps are converted to the present time.
@@ -28,37 +33,66 @@ const dataIndexSchema = Joi.object({
// For example:
// sample data set: timestamp: 2018-01-01T00:00:00Z, currentTimeMarker: 2018-01-01T12:00:00Z
// installed data set: timestamp: 2018-04-18T20:33:14Z, currentTimeMarker: 2018-04-19T08:33:14Z
- currentTimeMarker: Joi.string().isoDate().required(),
+ currentTimeMarker: schema.string({
+ validate(value: string) {
+ if (isNaN(Date.parse(value))) {
+ return 'Expected a valid string in iso format';
+ }
+ },
+ }),
// Set to true to move timestamp to current week, preserving day of week and time of day
// Relative distance from timestamp to currentTimeMarker will not remain the same
- preserveDayOfWeekTimeOfDay: Joi.boolean().default(false),
+ preserveDayOfWeekTimeOfDay: schema.boolean({ defaultValue: false }),
});
-const appLinkSchema = Joi.object({
- path: Joi.string().required(),
- label: Joi.string().required(),
- icon: Joi.string().required(),
+export type DataIndexSchema = TypeOf;
+
+const appLinkSchema = schema.object({
+ path: schema.string(),
+ label: schema.string(),
+ icon: schema.string(),
});
+export type AppLinkSchema = TypeOf;
-export const sampleDataSchema = {
- id: Joi.string()
- .regex(/^[a-zA-Z0-9-]+$/)
- .required(),
- name: Joi.string().required(),
- description: Joi.string().required(),
- previewImagePath: Joi.string().required(),
- darkPreviewImagePath: Joi.string(),
+export const sampleDataSchema = schema.object({
+ id: schema.string({
+ validate(value: string) {
+ if (!idRegExp.test(value)) {
+ return `Does not satisfy regexp: ${idRegExp.toString()}`;
+ }
+ },
+ }),
+ name: schema.string(),
+ description: schema.string(),
+ previewImagePath: schema.string(),
+ darkPreviewImagePath: schema.maybe(schema.string()),
// saved object id of main dashboard for sample data set
- overviewDashboard: Joi.string().required(),
- appLinks: Joi.array().items(appLinkSchema).default([]),
+ overviewDashboard: schema.string(),
+ appLinks: schema.arrayOf(appLinkSchema, { defaultValue: [] }),
// saved object id of default index-pattern for sample data set
- defaultIndex: Joi.string().required(),
+ defaultIndex: schema.string(),
// Kibana saved objects (index patter, visualizations, dashboard, ...)
// Should provide a nice demo of Kibana's functionality with the sample data set
- savedObjects: Joi.array().items(Joi.object()).required(),
- dataIndices: Joi.array().items(dataIndexSchema).required(),
-};
+ savedObjects: schema.arrayOf(
+ schema.object(
+ {
+ id: schema.string(),
+ type: schema.string(),
+ attributes: schema.any(),
+ references: schema.arrayOf(schema.any()),
+ version: schema.maybe(schema.any()),
+ },
+ { unknowns: 'allow' }
+ )
+ ),
+ dataIndices: schema.arrayOf(dataIndexSchema),
+
+ status: schema.maybe(schema.string()),
+ statusMsg: schema.maybe(schema.string()),
+});
+
+export type SampleDatasetSchema = Writable>;
diff --git a/src/plugins/home/server/services/sample_data/routes/install.ts b/src/plugins/home/server/services/sample_data/routes/install.ts
index e5ff33d5c199d..d0457f0a6d301 100644
--- a/src/plugins/home/server/services/sample_data/routes/install.ts
+++ b/src/plugins/home/server/services/sample_data/routes/install.ts
@@ -7,7 +7,12 @@
*/
import { schema } from '@kbn/config-schema';
-import { IRouter, Logger, IScopedClusterClient } from 'src/core/server';
+import type {
+ IRouter,
+ Logger,
+ IScopedClusterClient,
+ SavedObjectsBulkCreateObject,
+} from 'src/core/server';
import { SampleDatasetSchema } from '../lib/sample_dataset_registry_types';
import { createIndexName } from '../lib/create_index_name';
import {
@@ -148,8 +153,9 @@ export function createInstallRoute(
const client = getClient({ includedHiddenTypes });
+ const savedObjects = sampleDataset.savedObjects as SavedObjectsBulkCreateObject[];
createResults = await client.bulkCreate(
- sampleDataset.savedObjects.map(({ version, ...savedObject }) => savedObject),
+ savedObjects.map(({ version, ...savedObject }) => savedObject),
{ overwrite: true }
);
} catch (err) {
diff --git a/src/plugins/home/server/services/sample_data/sample_data_registry.ts b/src/plugins/home/server/services/sample_data/sample_data_registry.ts
index ca75d20dc1d3f..dff0d86409974 100644
--- a/src/plugins/home/server/services/sample_data/sample_data_registry.ts
+++ b/src/plugins/home/server/services/sample_data/sample_data_registry.ts
@@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
-import Joi from 'joi';
import { CoreSetup, PluginInitializerContext } from 'src/core/server';
import { SavedObject } from 'src/core/public';
import {
@@ -55,11 +54,13 @@ export class SampleDataRegistry {
return {
registerSampleDataset: (specProvider: SampleDatasetProvider) => {
- const { error, value } = Joi.validate(specProvider(), sampleDataSchema);
-
- if (error) {
+ let value: SampleDatasetSchema;
+ try {
+ value = sampleDataSchema.validate(specProvider());
+ } catch (error) {
throw new Error(`Unable to register sample dataset spec because it's invalid. ${error}`);
}
+
const defaultIndexSavedObjectJson = value.savedObjects.find((savedObjectJson: any) => {
return (
savedObjectJson.type === 'index-pattern' && savedObjectJson.id === value.defaultIndex
diff --git a/src/plugins/home/server/services/tutorials/index.ts b/src/plugins/home/server/services/tutorials/index.ts
index 92f6de716185d..f745d0190efd5 100644
--- a/src/plugins/home/server/services/tutorials/index.ts
+++ b/src/plugins/home/server/services/tutorials/index.ts
@@ -12,7 +12,6 @@ export type { TutorialsRegistrySetup, TutorialsRegistryStart } from './tutorials
export { TutorialsCategory } from './lib/tutorials_registry_types';
export type {
- ParamTypes,
InstructionSetSchema,
ParamsSchema,
InstructionsSchema,
diff --git a/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts b/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts
index 0f06b6c3257c2..5efbe067f6ece 100644
--- a/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts
+++ b/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts
@@ -5,121 +5,153 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
+import { schema, TypeOf } from '@kbn/config-schema';
-import Joi from 'joi';
-
-const PARAM_TYPES = {
- NUMBER: 'number',
- STRING: 'string',
-};
-
-const TUTORIAL_CATEGORY = {
- LOGGING: 'logging',
- SECURITY_SOLUTION: 'security solution',
- METRICS: 'metrics',
- OTHER: 'other',
-};
-
-const dashboardSchema = Joi.object({
- id: Joi.string().required(), // Dashboard saved object id
- linkLabel: Joi.string().when('isOverview', {
- is: true,
- then: Joi.required(),
- }),
+const dashboardSchema = schema.object({
+ // Dashboard saved object id
+ id: schema.string(),
// Is this an Overview / Entry Point dashboard?
- isOverview: Joi.boolean().required(),
+ isOverview: schema.boolean(),
+ linkLabel: schema.conditional(
+ schema.siblingRef('isOverview'),
+ true,
+ schema.string(),
+ schema.maybe(schema.string())
+ ),
});
+export type DashboardSchema = TypeOf;
-const artifactsSchema = Joi.object({
+const artifactsSchema = schema.object({
// Fields present in Elasticsearch documents created by this product.
- exportedFields: Joi.object({
- documentationUrl: Joi.string().required(),
- }),
+ exportedFields: schema.maybe(
+ schema.object({
+ documentationUrl: schema.string(),
+ })
+ ),
// Kibana dashboards created by this product.
- dashboards: Joi.array().items(dashboardSchema).required(),
- application: Joi.object({
- path: Joi.string().required(),
- label: Joi.string().required(),
- }),
+ dashboards: schema.arrayOf(dashboardSchema),
+ application: schema.maybe(
+ schema.object({
+ path: schema.string(),
+ label: schema.string(),
+ })
+ ),
});
-
-const statusCheckSchema = Joi.object({
- title: Joi.string(),
- text: Joi.string(),
- btnLabel: Joi.string(),
- success: Joi.string(),
- error: Joi.string(),
- esHitsCheck: Joi.object({
- index: Joi.alternatives().try(Joi.string(), Joi.array().items(Joi.string())).required(),
- query: Joi.object().required(),
- }).required(),
+export type ArtifactsSchema = TypeOf;
+
+const statusCheckSchema = schema.object({
+ title: schema.maybe(schema.string()),
+ text: schema.maybe(schema.string()),
+ btnLabel: schema.maybe(schema.string()),
+ success: schema.maybe(schema.string()),
+ error: schema.maybe(schema.string()),
+ esHitsCheck: schema.object({
+ index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]),
+ query: schema.recordOf(schema.string(), schema.any()),
+ }),
});
-const instructionSchema = Joi.object({
- title: Joi.string(),
- textPre: Joi.string(),
- commands: Joi.array().items(Joi.string().allow('')),
- textPost: Joi.string(),
+const instructionSchema = schema.object({
+ title: schema.maybe(schema.string()),
+ textPre: schema.maybe(schema.string()),
+ commands: schema.maybe(schema.arrayOf(schema.string())),
+ textPost: schema.maybe(schema.string()),
});
+export type Instruction = TypeOf;
-const instructionVariantSchema = Joi.object({
- id: Joi.string().required(),
- instructions: Joi.array().items(instructionSchema).required(),
+const instructionVariantSchema = schema.object({
+ id: schema.string(),
+ instructions: schema.arrayOf(instructionSchema),
});
-const instructionSetSchema = Joi.object({
- title: Joi.string(),
- callOut: Joi.object({
- title: Joi.string().required(),
- message: Joi.string(),
- iconType: Joi.string(),
- }),
+export type InstructionVariant = TypeOf;
+
+const instructionSetSchema = schema.object({
+ title: schema.maybe(schema.string()),
+ callOut: schema.maybe(
+ schema.object({
+ title: schema.string(),
+ message: schema.maybe(schema.string()),
+ iconType: schema.maybe(schema.string()),
+ })
+ ),
// Variants (OSes, languages, etc.) for which tutorial instructions are specified.
- instructionVariants: Joi.array().items(instructionVariantSchema).required(),
- statusCheck: statusCheckSchema,
+ instructionVariants: schema.arrayOf(instructionVariantSchema),
+ statusCheck: schema.maybe(statusCheckSchema),
});
-
-const paramSchema = Joi.object({
- defaultValue: Joi.required(),
- id: Joi.string()
- .regex(/^[a-zA-Z_]+$/)
- .required(),
- label: Joi.string().required(),
- type: Joi.string().valid(Object.values(PARAM_TYPES)).required(),
+export type InstructionSetSchema = TypeOf;
+
+const idRegExp = /^[a-zA-Z_]+$/;
+const paramSchema = schema.object({
+ defaultValue: schema.any(),
+ id: schema.string({
+ validate(value: string) {
+ if (!idRegExp.test(value)) {
+ return `Does not satisfy regexp ${idRegExp.toString()}`;
+ }
+ },
+ }),
+ label: schema.string(),
+ type: schema.oneOf([schema.literal('number'), schema.literal('string')]),
});
+export type ParamsSchema = TypeOf;
-const instructionsSchema = Joi.object({
- instructionSets: Joi.array().items(instructionSetSchema).required(),
- params: Joi.array().items(paramSchema),
+const instructionsSchema = schema.object({
+ instructionSets: schema.arrayOf(instructionSetSchema),
+ params: schema.maybe(schema.arrayOf(paramSchema)),
});
-
-export const tutorialSchema = {
- id: Joi.string()
- .regex(/^[a-zA-Z0-9-]+$/)
- .required(),
- category: Joi.string().valid(Object.values(TUTORIAL_CATEGORY)).required(),
- name: Joi.string().required(),
- moduleName: Joi.string(),
- isBeta: Joi.boolean().default(false),
- shortDescription: Joi.string().required(),
- euiIconType: Joi.string(), // EUI icon type string, one of https://elastic.github.io/eui/#/icons
- longDescription: Joi.string().required(),
- completionTimeMinutes: Joi.number().integer(),
- previewImagePath: Joi.string(),
-
+export type InstructionsSchema = TypeOf;
+
+const tutorialIdRegExp = /^[a-zA-Z0-9-]+$/;
+export const tutorialSchema = schema.object({
+ id: schema.string({
+ validate(value: string) {
+ if (!tutorialIdRegExp.test(value)) {
+ return `Does not satisfy regexp ${tutorialIdRegExp.toString()}`;
+ }
+ },
+ }),
+ category: schema.oneOf([
+ schema.literal('logging'),
+ schema.literal('security'),
+ schema.literal('metrics'),
+ schema.literal('other'),
+ ]),
+ name: schema.string({
+ validate(value: string) {
+ if (value === '') {
+ return 'is not allowed to be empty';
+ }
+ },
+ }),
+ moduleName: schema.maybe(schema.string()),
+ isBeta: schema.maybe(schema.boolean()),
+ shortDescription: schema.string(),
+ // EUI icon type string, one of https://elastic.github.io/eui/#/icons
+ euiIconType: schema.maybe(schema.string()),
+ longDescription: schema.string(),
+ completionTimeMinutes: schema.maybe(
+ schema.number({
+ validate(value: number) {
+ if (!Number.isInteger(value)) {
+ return 'Expected to be a valid integer number';
+ }
+ },
+ })
+ ),
+ previewImagePath: schema.maybe(schema.string()),
// kibana and elastic cluster running on prem
- onPrem: instructionsSchema.required(),
-
+ onPrem: instructionsSchema,
// kibana and elastic cluster running in elastic's cloud
- elasticCloud: instructionsSchema,
-
+ elasticCloud: schema.maybe(instructionsSchema),
// kibana running on prem and elastic cluster running in elastic's cloud
- onPremElasticCloud: instructionsSchema,
-
+ onPremElasticCloud: schema.maybe(instructionsSchema),
// Elastic stack artifacts produced by product when it is setup and run.
- artifacts: artifactsSchema,
+ artifacts: schema.maybe(artifactsSchema),
// saved objects used by data module.
- savedObjects: Joi.array().items(),
- savedObjectsInstallMsg: Joi.string(),
-};
+ savedObjects: schema.maybe(schema.arrayOf(schema.any())),
+ savedObjectsInstallMsg: schema.maybe(schema.string()),
+});
+
+export type TutorialSchema = TypeOf;
diff --git a/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts b/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts
index b0837a99d65ad..4c80c8858a475 100644
--- a/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts
+++ b/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts
@@ -6,8 +6,18 @@
* Side Public License, v 1.
*/
-import { IconType } from '@elastic/eui';
-import { KibanaRequest } from 'src/core/server';
+import type { KibanaRequest } from 'src/core/server';
+import type { TutorialSchema } from './tutorial_schema';
+export type {
+ TutorialSchema,
+ ArtifactsSchema,
+ DashboardSchema,
+ InstructionsSchema,
+ ParamsSchema,
+ InstructionSetSchema,
+ InstructionVariant,
+ Instruction,
+} from './tutorial_schema';
/** @public */
export enum TutorialsCategory {
@@ -18,82 +28,6 @@ export enum TutorialsCategory {
}
export type Platform = 'WINDOWS' | 'OSX' | 'DEB' | 'RPM';
-export interface ParamTypes {
- NUMBER: string;
- STRING: string;
-}
-export interface Instruction {
- title?: string;
- textPre?: string;
- commands?: string[];
- textPost?: string;
-}
-export interface InstructionVariant {
- id: string;
- instructions: Instruction[];
-}
-export interface InstructionSetSchema {
- readonly title?: string;
- readonly callOut?: {
- title: string;
- message?: string;
- iconType?: IconType;
- };
- instructionVariants: InstructionVariant[];
-}
-export interface ParamsSchema {
- defaultValue: any;
- id: string;
- label: string;
- type: ParamTypes;
-}
-export interface InstructionsSchema {
- readonly instructionSets: InstructionSetSchema[];
- readonly params?: ParamsSchema[];
-}
-export interface DashboardSchema {
- id: string;
- linkLabel?: string;
- isOverview: boolean;
-}
-export interface ArtifactsSchema {
- exportedFields?: {
- documentationUrl: string;
- };
- dashboards: DashboardSchema[];
- application?: {
- path: string;
- label: string;
- };
-}
-export interface TutorialSchema {
- id: string;
- category: TutorialsCategory;
- name: string;
- moduleName?: string;
- isBeta?: boolean;
- shortDescription: string;
- euiIconType?: IconType; // EUI icon type string, one of https://elastic.github.io/eui/#/display/icons;
- longDescription: string;
- completionTimeMinutes?: number;
- previewImagePath?: string;
-
- // kibana and elastic cluster running on prem
- onPrem: InstructionsSchema;
-
- // kibana and elastic cluster running in elastic's cloud
- elasticCloud?: InstructionsSchema;
-
- // kibana running on prem and elastic cluster running in elastic's cloud
- onPremElasticCloud?: InstructionsSchema;
-
- // Elastic stack artifacts produced by product when it is setup and run.
- artifacts?: ArtifactsSchema;
-
- // saved objects used by data module.
- savedObjects?: any[];
- savedObjectsInstallMsg?: string;
-}
export interface TutorialContext {
[key: string]: unknown;
}
diff --git a/src/plugins/home/server/services/tutorials/tutorials_registry.test.ts b/src/plugins/home/server/services/tutorials/tutorials_registry.test.ts
index 94f5d65610083..a82699c231ad4 100644
--- a/src/plugins/home/server/services/tutorials/tutorials_registry.test.ts
+++ b/src/plugins/home/server/services/tutorials/tutorials_registry.test.ts
@@ -92,7 +92,7 @@ describe('TutorialsRegistry', () => {
const setup = new TutorialsRegistry().setup(mockCoreSetup);
testProvider = ({}) => invalidTutorialProvider;
expect(() => setup.registerTutorial(testProvider)).toThrowErrorMatchingInlineSnapshot(
- `"Unable to register tutorial spec because its invalid. ValidationError: child \\"name\\" fails because [\\"name\\" is not allowed to be empty]"`
+ `"Unable to register tutorial spec because its invalid. Error: [name]: is not allowed to be empty"`
);
});
diff --git a/src/plugins/home/server/services/tutorials/tutorials_registry.ts b/src/plugins/home/server/services/tutorials/tutorials_registry.ts
index f21f2ccd719c5..05f5600af307a 100644
--- a/src/plugins/home/server/services/tutorials/tutorials_registry.ts
+++ b/src/plugins/home/server/services/tutorials/tutorials_registry.ts
@@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
-import Joi from 'joi';
import { CoreSetup } from 'src/core/server';
import {
TutorialProvider,
@@ -42,10 +41,10 @@ export class TutorialsRegistry {
);
return {
registerTutorial: (specProvider: TutorialProvider) => {
- const emptyContext = {};
- const { error } = Joi.validate(specProvider(emptyContext), tutorialSchema);
-
- if (error) {
+ try {
+ const emptyContext = {};
+ tutorialSchema.validate(specProvider(emptyContext));
+ } catch (error) {
throw new Error(`Unable to register tutorial spec because its invalid. ${error}`);
}
diff --git a/src/plugins/index_pattern_field_editor/public/lib/documentation.ts b/src/plugins/index_pattern_field_editor/public/lib/documentation.ts
index 9577f25184ba0..70f180d7cb5f2 100644
--- a/src/plugins/index_pattern_field_editor/public/lib/documentation.ts
+++ b/src/plugins/index_pattern_field_editor/public/lib/documentation.ts
@@ -11,11 +11,11 @@ import { DocLinksStart } from 'src/core/public';
export const getLinks = (docLinks: DocLinksStart) => {
const { DOC_LINK_VERSION, ELASTIC_WEBSITE_URL } = docLinks;
const docsBase = `${ELASTIC_WEBSITE_URL}guide/en`;
- const esDocsBase = `${docsBase}/elasticsearch/reference/${DOC_LINK_VERSION}`;
const painlessDocsBase = `${docsBase}/elasticsearch/painless/${DOC_LINK_VERSION}`;
+ const kibanaDocsBase = `${docsBase}/kibana/${DOC_LINK_VERSION}`;
return {
- runtimePainless: `${esDocsBase}/runtime.html#runtime-mapping-fields`,
+ runtimePainless: `${kibanaDocsBase}/managing-index-patterns.html#runtime-fields`,
painlessSyntax: `${painlessDocsBase}/painless-lang-spec.html`,
};
};
diff --git a/src/plugins/spaces_oss/public/api.ts b/src/plugins/spaces_oss/public/api.ts
index e460d9a43ef6b..ddee9c0528ba1 100644
--- a/src/plugins/spaces_oss/public/api.ts
+++ b/src/plugins/spaces_oss/public/api.ts
@@ -169,8 +169,8 @@ export interface ShareToSpaceFlyoutProps {
behaviorContext?: 'within-space' | 'outside-space';
/**
* Optional handler that is called when the user has saved changes and there are spaces to be added to and/or removed from the object. If
- * this is not defined, a default handler will be used that calls `/api/spaces/_share_saved_object_add` and/or
- * `/api/spaces/_share_saved_object_remove` and displays toast(s) indicating what occurred.
+ * this is not defined, a default handler will be used that calls `/api/spaces/_update_objects_spaces` and displays a toast indicating
+ * what occurred.
*/
changeSpacesHandler?: (spacesToAdd: string[], spacesToRemove: string[]) => Promise;
/**
diff --git a/src/plugins/timelion/public/components/timelion_deprecation.tsx b/src/plugins/timelion/public/components/timelion_deprecation.tsx
index 41ae09f305863..efcef88b3d0a2 100644
--- a/src/plugins/timelion/public/components/timelion_deprecation.tsx
+++ b/src/plugins/timelion/public/components/timelion_deprecation.tsx
@@ -19,7 +19,7 @@ export const TimelionDeprecation = ({ links }: DocLinksStart) => {
title={
diff --git a/src/plugins/timelion/server/deprecations.ts b/src/plugins/timelion/server/deprecations.ts
index 3d4e687f154cf..e65d72cb460df 100644
--- a/src/plugins/timelion/server/deprecations.ts
+++ b/src/plugins/timelion/server/deprecations.ts
@@ -30,7 +30,7 @@ export const showWarningMessageIfTimelionSheetWasFound = async (
const count = await getTimelionSheetsCount(savedObjectsClient);
if (count > 0) {
logger.warn(
- 'Deprecated since 7.0, the Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard. See https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html.'
+ 'Deprecated since 7.0, the Timelion app will be removed in the last 7.x minor version. To continue using your Timelion worksheets, migrate them to a dashboard. See https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html.'
);
}
};
@@ -49,7 +49,7 @@ export async function getDeprecations({
if (count > 0) {
deprecations.push({
- message: `You have ${count} Timelion worksheets. The Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard.`,
+ message: `You have ${count} Timelion worksheets. The Timelion app will be removed in the last 7.x minor version. To continue using your Timelion worksheets, migrate them to a dashboard.`,
documentationUrl:
'https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html',
level: 'warning',
diff --git a/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.ts
index d5f8d978d5252..310486bfdfffd 100644
--- a/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.ts
+++ b/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.ts
@@ -7,7 +7,7 @@
*/
import { parse } from 'hjson';
-import { ElasticsearchClient, SavedObject } from 'src/core/server';
+import type { ElasticsearchClient } from 'src/core/server';
import { VegaSavedObjectAttributes, VisTypeVegaPluginSetupDependencies } from '../types';
@@ -27,7 +27,7 @@ const getDefaultVegaVisualizations = (home: UsageCollectorDependencies['home'])
const sampleDataSets = home?.sampleData.getSampleDatasets() ?? [];
sampleDataSets.forEach((sampleDataSet) =>
- sampleDataSet.savedObjects.forEach((savedObject: SavedObject) => {
+ sampleDataSet.savedObjects.forEach((savedObject) => {
try {
if (savedObject.type === 'visualization') {
const visState = JSON.parse(savedObject.attributes?.visState);
diff --git a/src/plugins/vis_type_vislib/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_type_vislib/public/__snapshots__/to_ast.test.ts.snap
index c3ffc0dd08412..3ca2834a54fca 100644
--- a/src/plugins/vis_type_vislib/public/__snapshots__/to_ast.test.ts.snap
+++ b/src/plugins/vis_type_vislib/public/__snapshots__/to_ast.test.ts.snap
@@ -8,7 +8,7 @@ Object {
"area",
],
"visConfig": Array [
- "{\\"type\\":\\"area\\",\\"grid\\":{\\"categoryLines\\":false,\\"style\\":{\\"color\\":\\"#eee\\"}},\\"categoryAxes\\":[{\\"id\\":\\"CategoryAxis-1\\",\\"type\\":\\"category\\",\\"position\\":\\"bottom\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\"},\\"labels\\":{\\"show\\":true,\\"truncate\\":100},\\"title\\":{}}],\\"valueAxes\\":[{\\"id\\":\\"ValueAxis-1\\",\\"name\\":\\"LeftAxis-1\\",\\"type\\":\\"value\\",\\"position\\":\\"left\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\",\\"mode\\":\\"normal\\"},\\"labels\\":{\\"show\\":true,\\"rotate\\":0,\\"filter\\":false,\\"truncate\\":100},\\"title\\":{\\"text\\":\\"Sum of total_quantity\\"}}],\\"seriesParams\\":[{\\"show\\":\\"true\\",\\"type\\":\\"area\\",\\"mode\\":\\"stacked\\",\\"data\\":{\\"label\\":\\"Sum of total_quantity\\",\\"id\\":\\"1\\"},\\"drawLinesBetweenPoints\\":true,\\"showCircles\\":true,\\"interpolate\\":\\"linear\\",\\"valueAxis\\":\\"ValueAxis-1\\"}],\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"top\\",\\"times\\":[],\\"addTimeMarker\\":false,\\"thresholdLine\\":{\\"show\\":false,\\"value\\":10,\\"width\\":1,\\"style\\":\\"full\\",\\"color\\":\\"#E7664C\\"},\\"labels\\":{},\\"dimensions\\":{\\"x\\":{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"HH:mm:ss.SSS\\"}},\\"params\\":{}},\\"y\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\",\\"params\\":{\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}],\\"series\\":[{\\"accessor\\":2,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}",
+ "{\\"type\\":\\"area\\",\\"grid\\":{\\"categoryLines\\":false,\\"style\\":{\\"color\\":\\"#eee\\"}},\\"categoryAxes\\":[{\\"id\\":\\"CategoryAxis-1\\",\\"type\\":\\"category\\",\\"position\\":\\"bottom\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\"},\\"labels\\":{\\"show\\":true,\\"truncate\\":100},\\"title\\":{}}],\\"valueAxes\\":[{\\"id\\":\\"ValueAxis-1\\",\\"name\\":\\"LeftAxis-1\\",\\"type\\":\\"value\\",\\"position\\":\\"left\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\",\\"mode\\":\\"normal\\"},\\"labels\\":{\\"show\\":true,\\"rotate\\":0,\\"filter\\":false,\\"truncate\\":100},\\"title\\":{\\"text\\":\\"Sum of total_quantity\\"}}],\\"seriesParams\\":[{\\"show\\":\\"true\\",\\"type\\":\\"area\\",\\"mode\\":\\"stacked\\",\\"data\\":{\\"label\\":\\"Sum of total_quantity\\",\\"id\\":\\"1\\"},\\"drawLinesBetweenPoints\\":true,\\"showCircles\\":true,\\"interpolate\\":\\"linear\\",\\"valueAxis\\":\\"ValueAxis-1\\"}],\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"top\\",\\"times\\":[],\\"addTimeMarker\\":false,\\"thresholdLine\\":{\\"show\\":false,\\"value\\":10,\\"width\\":1,\\"style\\":\\"full\\",\\"color\\":\\"#E7664C\\"},\\"palette\\":{\\"name\\":\\"default\\"},\\"labels\\":{},\\"dimensions\\":{\\"x\\":{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"HH:mm:ss.SSS\\"}},\\"params\\":{}},\\"y\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\",\\"params\\":{\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}],\\"series\\":[{\\"accessor\\":2,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}",
],
},
"getArgument": [Function],
diff --git a/src/plugins/vis_type_vislib/public/vislib/partials/touchdown.tmpl.html b/src/plugins/vis_type_vislib/public/vislib/partials/touchdown.tmpl.html
deleted file mode 100644
index ee95eef68f3b2..0000000000000
--- a/src/plugins/vis_type_vislib/public/vislib/partials/touchdown.tmpl.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- <%= wholeBucket ? 'Part of this bucket' : 'This area' %>
- may contain partial data. The selected time range does not fully cover it.
-
-
diff --git a/src/plugins/vis_type_vislib/public/vislib/partials/touchdown_template.tsx b/src/plugins/vis_type_vislib/public/vislib/partials/touchdown_template.tsx
new file mode 100644
index 0000000000000..55955da07ebdd
--- /dev/null
+++ b/src/plugins/vis_type_vislib/public/vislib/partials/touchdown_template.tsx
@@ -0,0 +1,26 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the 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/server';
+
+interface Props {
+ wholeBucket: boolean;
+}
+
+export const touchdownTemplate = ({ wholeBucket }: Props) => {
+ return ReactDOM.renderToStaticMarkup(
+
+
+
+ {wholeBucket ? 'Part of this bucket' : 'This area'} may contain partial data. The selected
+ time range does not fully cover it.
+
+
+ );
+};
diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series.js
index 28464009a8339..b4ab2ea2992c5 100644
--- a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series.js
+++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series.js
@@ -14,10 +14,10 @@ import { Tooltip } from '../components/tooltip';
import { Chart } from './_chart';
import { TimeMarker } from './time_marker';
import { seriesTypes } from './point_series/series_types';
-import touchdownTmplHtml from '../partials/touchdown.tmpl.html';
+import { touchdownTemplate } from '../partials/touchdown_template';
const seriTypes = seriesTypes;
-const touchdownTmpl = _.template(touchdownTmplHtml);
+
/**
* Line Chart Visualization
*
@@ -169,7 +169,7 @@ export class PointSeries extends Chart {
}
function textFormatter() {
- return touchdownTmpl(callPlay(d3.event));
+ return touchdownTemplate(callPlay(d3.event));
}
const endzoneTT = new Tooltip('endzones', this.handler.el, textFormatter, null);
diff --git a/src/plugins/vis_type_xy/common/index.ts b/src/plugins/vis_type_xy/common/index.ts
index 903adb53eb403..a80946f7c62fa 100644
--- a/src/plugins/vis_type_xy/common/index.ts
+++ b/src/plugins/vis_type_xy/common/index.ts
@@ -6,17 +6,14 @@
* Side Public License, v 1.
*/
-import { $Values } from '@kbn/utility-types';
-
/**
* Type of charts able to render
*/
-export const ChartType = Object.freeze({
- Line: 'line' as const,
- Area: 'area' as const,
- Histogram: 'histogram' as const,
-});
-export type ChartType = $Values;
+export enum ChartType {
+ Line = 'line',
+ Area = 'area',
+ Histogram = 'histogram',
+}
/**
* Type of xy visualizations
diff --git a/src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap
index e6665c26a2815..7c21e699216bc 100644
--- a/src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap
+++ b/src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap
@@ -4,11 +4,70 @@ exports[`xy vis toExpressionAst function should match basic snapshot 1`] = `
Object {
"addArgument": [Function],
"arguments": Object {
+ "addLegend": Array [
+ true,
+ ],
+ "addTimeMarker": Array [
+ false,
+ ],
+ "addTooltip": Array [
+ true,
+ ],
+ "categoryAxes": Array [
+ Object {
+ "toAst": [Function],
+ },
+ ],
+ "chartType": Array [
+ "area",
+ ],
+ "gridCategoryLines": Array [
+ false,
+ ],
+ "labels": Array [
+ Object {
+ "toAst": [Function],
+ },
+ ],
+ "legendPosition": Array [
+ "top",
+ ],
+ "palette": Array [
+ "default",
+ ],
+ "seriesDimension": Array [
+ Object {
+ "toAst": [Function],
+ },
+ ],
+ "seriesParams": Array [
+ Object {
+ "toAst": [Function],
+ },
+ ],
+ "thresholdLine": Array [
+ Object {
+ "toAst": [Function],
+ },
+ ],
+ "times": Array [],
"type": Array [
"area",
],
- "visConfig": Array [
- "{\\"type\\":\\"area\\",\\"grid\\":{\\"categoryLines\\":false,\\"style\\":{\\"color\\":\\"#eee\\"}},\\"categoryAxes\\":[{\\"id\\":\\"CategoryAxis-1\\",\\"type\\":\\"category\\",\\"position\\":\\"bottom\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\"},\\"labels\\":{\\"show\\":true,\\"truncate\\":100},\\"title\\":{}}],\\"valueAxes\\":[{\\"id\\":\\"ValueAxis-1\\",\\"name\\":\\"LeftAxis-1\\",\\"type\\":\\"value\\",\\"position\\":\\"left\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\",\\"mode\\":\\"normal\\"},\\"labels\\":{\\"show\\":true,\\"rotate\\":0,\\"filter\\":false,\\"truncate\\":100},\\"title\\":{\\"text\\":\\"Sum of total_quantity\\"}}],\\"seriesParams\\":[{\\"show\\":\\"true\\",\\"type\\":\\"area\\",\\"mode\\":\\"stacked\\",\\"data\\":{\\"label\\":\\"Sum of total_quantity\\",\\"id\\":\\"1\\"},\\"drawLinesBetweenPoints\\":true,\\"showCircles\\":true,\\"interpolate\\":\\"linear\\",\\"valueAxis\\":\\"ValueAxis-1\\"}],\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"top\\",\\"times\\":[],\\"addTimeMarker\\":false,\\"thresholdLine\\":{\\"show\\":false,\\"value\\":10,\\"width\\":1,\\"style\\":\\"full\\",\\"color\\":\\"#E7664C\\"},\\"labels\\":{},\\"dimensions\\":{\\"x\\":{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"HH:mm:ss.SSS\\"}},\\"params\\":{}},\\"y\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\",\\"params\\":{\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}],\\"series\\":[{\\"accessor\\":2,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}",
+ "valueAxes": Array [
+ Object {
+ "toAst": [Function],
+ },
+ ],
+ "xDimension": Array [
+ Object {
+ "toAst": [Function],
+ },
+ ],
+ "yDimension": Array [
+ Object {
+ "toAst": [Function],
+ },
],
},
"getArgument": [Function],
diff --git a/src/plugins/vis_type_xy/public/editor/common_config.tsx b/src/plugins/vis_type_xy/public/editor/common_config.tsx
index 1e4ac7df0082c..1815d9cfc429d 100644
--- a/src/plugins/vis_type_xy/public/editor/common_config.tsx
+++ b/src/plugins/vis_type_xy/public/editor/common_config.tsx
@@ -9,11 +9,11 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
-import { VisEditorOptionsProps } from '../../../visualizations/public';
+import type { VisEditorOptionsProps } from '../../../visualizations/public';
-import { VisParams } from '../types';
+import type { VisParams } from '../types';
import { MetricsAxisOptions, PointSeriesOptions } from './components/options';
-import { ValidationWrapper } from './components/common';
+import { ValidationWrapper } from './components/common/validation_wrapper';
export function getOptionTabs(showElasticChartsOptions = false) {
return [
diff --git a/src/plugins/vis_type_xy/public/expression_functions/category_axis.ts b/src/plugins/vis_type_xy/public/expression_functions/category_axis.ts
new file mode 100644
index 0000000000000..30215d8feb8a3
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/expression_functions/category_axis.ts
@@ -0,0 +1,116 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import type {
+ ExpressionFunctionDefinition,
+ Datatable,
+ ExpressionValueBoxed,
+} from '../../../expressions/public';
+import type { CategoryAxis } from '../types';
+import type { ExpressionValueScale } from './vis_scale';
+import type { ExpressionValueLabel } from './label';
+
+export interface Arguments extends Omit {
+ title?: string;
+ scale: ExpressionValueScale;
+ labels: ExpressionValueLabel;
+}
+
+export type ExpressionValueCategoryAxis = ExpressionValueBoxed<
+ 'category_axis',
+ {
+ id: CategoryAxis['id'];
+ show: CategoryAxis['show'];
+ position: CategoryAxis['position'];
+ axisType: CategoryAxis['type'];
+ title: {
+ text?: string;
+ };
+ labels: CategoryAxis['labels'];
+ scale: CategoryAxis['scale'];
+ }
+>;
+
+export const categoryAxis = (): ExpressionFunctionDefinition<
+ 'categoryaxis',
+ Datatable | null,
+ Arguments,
+ ExpressionValueCategoryAxis
+> => ({
+ name: 'categoryaxis',
+ help: i18n.translate('visTypeXy.function.categoryAxis.help', {
+ defaultMessage: 'Generates category axis object',
+ }),
+ type: 'category_axis',
+ args: {
+ id: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.categoryAxis.id.help', {
+ defaultMessage: 'Id of category axis',
+ }),
+ required: true,
+ },
+ show: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.categoryAxis.show.help', {
+ defaultMessage: 'Show the category axis',
+ }),
+ required: true,
+ },
+ position: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.categoryAxis.position.help', {
+ defaultMessage: 'Position of the category axis',
+ }),
+ required: true,
+ },
+ type: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.categoryAxis.type.help', {
+ defaultMessage: 'Type of the category axis. Can be category or value',
+ }),
+ required: true,
+ },
+ title: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.categoryAxis.title.help', {
+ defaultMessage: 'Title of the category axis',
+ }),
+ },
+ scale: {
+ types: ['vis_scale'],
+ help: i18n.translate('visTypeXy.function.categoryAxis.scale.help', {
+ defaultMessage: 'Scale config',
+ }),
+ },
+ labels: {
+ types: ['label'],
+ help: i18n.translate('visTypeXy.function.categoryAxis.labels.help', {
+ defaultMessage: 'Axis label config',
+ }),
+ },
+ },
+ fn: (context, args) => {
+ return {
+ type: 'category_axis',
+ id: args.id,
+ show: args.show,
+ position: args.position,
+ axisType: args.type,
+ title: {
+ text: args.title,
+ },
+ scale: {
+ ...args.scale,
+ type: args.scale.scaleType,
+ },
+ labels: args.labels,
+ };
+ },
+});
diff --git a/src/plugins/vis_type_xy/public/expression_functions/index.ts b/src/plugins/vis_type_xy/public/expression_functions/index.ts
new file mode 100644
index 0000000000000..4e7db57ee65d7
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/expression_functions/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.
+ */
+
+export { visTypeXyVisFn } from './xy_vis_fn';
+
+export { categoryAxis, ExpressionValueCategoryAxis } from './category_axis';
+export { timeMarker, ExpressionValueTimeMarker } from './time_marker';
+export { valueAxis, ExpressionValueValueAxis } from './value_axis';
+export { seriesParam, ExpressionValueSeriesParam } from './series_param';
+export { thresholdLine, ExpressionValueThresholdLine } from './threshold_line';
+export { label, ExpressionValueLabel } from './label';
+export { visScale, ExpressionValueScale } from './vis_scale';
+export { xyDimension, ExpressionValueXYDimension } from './xy_dimension';
diff --git a/src/plugins/vis_type_xy/public/expression_functions/label.ts b/src/plugins/vis_type_xy/public/expression_functions/label.ts
new file mode 100644
index 0000000000000..934278d13cff0
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/expression_functions/label.ts
@@ -0,0 +1,89 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import type { Labels } from '../../../charts/public';
+import type {
+ ExpressionFunctionDefinition,
+ Datatable,
+ ExpressionValueBoxed,
+} from '../../../expressions/public';
+
+export type ExpressionValueLabel = ExpressionValueBoxed<
+ 'label',
+ {
+ color?: Labels['color'];
+ filter?: Labels['filter'];
+ overwriteColor?: Labels['overwriteColor'];
+ rotate?: Labels['rotate'];
+ show?: Labels['show'];
+ truncate?: Labels['truncate'];
+ }
+>;
+
+export const label = (): ExpressionFunctionDefinition<
+ 'label',
+ Datatable | null,
+ Labels,
+ ExpressionValueLabel
+> => ({
+ name: 'label',
+ help: i18n.translate('visTypeXy.function.label.help', {
+ defaultMessage: 'Generates label object',
+ }),
+ type: 'label',
+ args: {
+ color: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.label.color.help', {
+ defaultMessage: 'Color of label',
+ }),
+ },
+ filter: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.label.filter.help', {
+ defaultMessage: 'Hides overlapping labels and duplicates on axis',
+ }),
+ },
+ overwriteColor: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.label.overwriteColor.help', {
+ defaultMessage: 'Overwrite color',
+ }),
+ },
+ rotate: {
+ types: ['number'],
+ help: i18n.translate('visTypeXy.function.label.rotate.help', {
+ defaultMessage: 'Rotate angle',
+ }),
+ },
+ show: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.label.show.help', {
+ defaultMessage: 'Show label',
+ }),
+ },
+ truncate: {
+ types: ['number', 'null'],
+ help: i18n.translate('visTypeXy.function.label.truncate.help', {
+ defaultMessage: 'The number of symbols before truncating',
+ }),
+ },
+ },
+ fn: (context, args) => {
+ return {
+ type: 'label',
+ color: args.color,
+ filter: args.hasOwnProperty('filter') ? args.filter : undefined,
+ overwriteColor: args.overwriteColor,
+ rotate: args.rotate,
+ show: args.show,
+ truncate: args.truncate,
+ };
+ },
+});
diff --git a/src/plugins/vis_type_xy/public/expression_functions/series_param.ts b/src/plugins/vis_type_xy/public/expression_functions/series_param.ts
new file mode 100644
index 0000000000000..402187cea6586
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/expression_functions/series_param.ts
@@ -0,0 +1,128 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import type {
+ ExpressionFunctionDefinition,
+ Datatable,
+ ExpressionValueBoxed,
+} from '../../../expressions/public';
+import type { SeriesParam } from '../types';
+
+export interface Arguments extends Omit {
+ label: string;
+ id: string;
+}
+
+export type ExpressionValueSeriesParam = ExpressionValueBoxed<
+ 'series_param',
+ {
+ data: { label: string; id: string };
+ drawLinesBetweenPoints?: boolean;
+ interpolate?: SeriesParam['interpolate'];
+ lineWidth?: number;
+ mode: SeriesParam['mode'];
+ show: boolean;
+ showCircles: boolean;
+ seriesParamType: SeriesParam['type'];
+ valueAxis: string;
+ }
+>;
+
+export const seriesParam = (): ExpressionFunctionDefinition<
+ 'seriesparam',
+ Datatable,
+ Arguments,
+ ExpressionValueSeriesParam
+> => ({
+ name: 'seriesparam',
+ help: i18n.translate('visTypeXy.function.seriesparam.help', {
+ defaultMessage: 'Generates series param object',
+ }),
+ type: 'series_param',
+ inputTypes: ['datatable'],
+ args: {
+ label: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.seriesParam.label.help', {
+ defaultMessage: 'Name of series param',
+ }),
+ required: true,
+ },
+ id: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.seriesParam.id.help', {
+ defaultMessage: 'Id of series param',
+ }),
+ required: true,
+ },
+ drawLinesBetweenPoints: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.seriesParam.drawLinesBetweenPoints.help', {
+ defaultMessage: 'Draw lines between points',
+ }),
+ },
+ interpolate: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.seriesParam.interpolate.help', {
+ defaultMessage: 'Interpolate mode. Can be linear, cardinal or step-after',
+ }),
+ },
+ show: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.seriesParam.show.help', {
+ defaultMessage: 'Show param',
+ }),
+ required: true,
+ },
+ lineWidth: {
+ types: ['number'],
+ help: i18n.translate('visTypeXy.function.seriesParam.lineWidth.help', {
+ defaultMessage: 'Width of line',
+ }),
+ },
+ mode: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.seriesParam.mode.help', {
+ defaultMessage: 'Chart mode. Can be stacked or percentage',
+ }),
+ },
+ showCircles: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.seriesParam.showCircles.help', {
+ defaultMessage: 'Show circles',
+ }),
+ },
+ type: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.seriesParam.type.help', {
+ defaultMessage: 'Chart type. Can be line, area or histogram',
+ }),
+ },
+ valueAxis: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.seriesParam.valueAxis.help', {
+ defaultMessage: 'Name of value axis',
+ }),
+ },
+ },
+ fn: (context, args) => {
+ return {
+ type: 'series_param',
+ data: { label: args.label, id: args.id },
+ drawLinesBetweenPoints: args.drawLinesBetweenPoints,
+ interpolate: args.interpolate,
+ lineWidth: args.lineWidth,
+ mode: args.mode,
+ show: args.show,
+ showCircles: args.showCircles,
+ seriesParamType: args.type,
+ valueAxis: args.valueAxis,
+ };
+ },
+});
diff --git a/src/plugins/vis_type_xy/public/expression_functions/threshold_line.ts b/src/plugins/vis_type_xy/public/expression_functions/threshold_line.ts
new file mode 100644
index 0000000000000..8c01e37503985
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/expression_functions/threshold_line.ts
@@ -0,0 +1,86 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import type {
+ ExpressionFunctionDefinition,
+ Datatable,
+ ExpressionValueBoxed,
+} from '../../../expressions/public';
+import type { ThresholdLine } from '../types';
+
+export type ExpressionValueThresholdLine = ExpressionValueBoxed<
+ 'threshold_line',
+ {
+ show: ThresholdLine['show'];
+ value: ThresholdLine['value'];
+ width: ThresholdLine['width'];
+ style: ThresholdLine['style'];
+ color: ThresholdLine['color'];
+ }
+>;
+
+export const thresholdLine = (): ExpressionFunctionDefinition<
+ 'thresholdline',
+ Datatable | null,
+ ThresholdLine,
+ ExpressionValueThresholdLine
+> => ({
+ name: 'thresholdline',
+ help: i18n.translate('visTypeXy.function.thresholdLine.help', {
+ defaultMessage: 'Generates threshold line object',
+ }),
+ type: 'threshold_line',
+ args: {
+ show: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.thresholdLine.show.help', {
+ defaultMessage: 'Show threshould line',
+ }),
+ required: true,
+ },
+ value: {
+ types: ['number', 'null'],
+ help: i18n.translate('visTypeXy.function.thresholdLine.value.help', {
+ defaultMessage: 'Threshold value',
+ }),
+ required: true,
+ },
+ width: {
+ types: ['number', 'null'],
+ help: i18n.translate('visTypeXy.function.thresholdLine.width.help', {
+ defaultMessage: 'Width of threshold line',
+ }),
+ required: true,
+ },
+ style: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.thresholdLine.style.help', {
+ defaultMessage: 'Style of threshold line. Can be full, dashed or dot-dashed',
+ }),
+ required: true,
+ },
+ color: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.thresholdLine.color.help', {
+ defaultMessage: 'Color of threshold line',
+ }),
+ required: true,
+ },
+ },
+ fn: (context, args) => {
+ return {
+ type: 'threshold_line',
+ show: args.show,
+ value: args.value,
+ width: args.width,
+ style: args.style,
+ color: args.color,
+ };
+ },
+});
diff --git a/src/plugins/vis_type_xy/public/expression_functions/time_marker.ts b/src/plugins/vis_type_xy/public/expression_functions/time_marker.ts
new file mode 100644
index 0000000000000..3d9f609292c00
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/expression_functions/time_marker.ts
@@ -0,0 +1,82 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import type {
+ ExpressionFunctionDefinition,
+ Datatable,
+ ExpressionValueBoxed,
+} from '../../../expressions/public';
+import type { TimeMarker } from '../types';
+
+export type ExpressionValueTimeMarker = ExpressionValueBoxed<
+ 'time_marker',
+ {
+ time: string;
+ class?: string;
+ color?: string;
+ opacity?: number;
+ width?: number;
+ }
+>;
+
+export const timeMarker = (): ExpressionFunctionDefinition<
+ 'timemarker',
+ Datatable | null,
+ TimeMarker,
+ ExpressionValueTimeMarker
+> => ({
+ name: 'timemarker',
+ help: i18n.translate('visTypeXy.function.timemarker.help', {
+ defaultMessage: 'Generates time marker object',
+ }),
+ type: 'time_marker',
+ args: {
+ time: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.timeMarker.time.help', {
+ defaultMessage: 'Exact Time',
+ }),
+ required: true,
+ },
+ class: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.timeMarker.class.help', {
+ defaultMessage: 'Css class name',
+ }),
+ },
+ color: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.timeMarker.color.help', {
+ defaultMessage: 'Color of time marker',
+ }),
+ },
+ opacity: {
+ types: ['number'],
+ help: i18n.translate('visTypeXy.function.timeMarker.opacity.help', {
+ defaultMessage: 'Opacity of time marker',
+ }),
+ },
+ width: {
+ types: ['number'],
+ help: i18n.translate('visTypeXy.function.timeMarker.width.help', {
+ defaultMessage: 'Width of time marker',
+ }),
+ },
+ },
+ fn: (context, args) => {
+ return {
+ type: 'time_marker',
+ time: args.time,
+ class: args.class,
+ color: args.color,
+ opacity: args.opacity,
+ width: args.width,
+ };
+ },
+});
diff --git a/src/plugins/vis_type_xy/public/expression_functions/value_axis.ts b/src/plugins/vis_type_xy/public/expression_functions/value_axis.ts
new file mode 100644
index 0000000000000..510ec9bc605d2
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/expression_functions/value_axis.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import type { ExpressionValueCategoryAxis } from './category_axis';
+import type { CategoryAxis } from '../types';
+import type {
+ ExpressionFunctionDefinition,
+ Datatable,
+ ExpressionValueBoxed,
+} from '../../../expressions/public';
+
+interface Arguments {
+ name: string;
+ axisParams: ExpressionValueCategoryAxis;
+}
+
+export type ExpressionValueValueAxis = ExpressionValueBoxed<
+ 'value_axis',
+ {
+ name: string;
+ id: string;
+ show: boolean;
+ position: CategoryAxis['position'];
+ axisType: CategoryAxis['type'];
+ title: {
+ text?: string;
+ };
+ labels: CategoryAxis['labels'];
+ scale: CategoryAxis['scale'];
+ }
+>;
+
+export const valueAxis = (): ExpressionFunctionDefinition<
+ 'valueaxis',
+ Datatable | null,
+ Arguments,
+ ExpressionValueValueAxis
+> => ({
+ name: 'valueaxis',
+ help: i18n.translate('visTypeXy.function.valueaxis.help', {
+ defaultMessage: 'Generates value axis object',
+ }),
+ type: 'value_axis',
+ args: {
+ name: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.valueAxis.name.help', {
+ defaultMessage: 'Name of value axis',
+ }),
+ required: true,
+ },
+ axisParams: {
+ types: ['category_axis'],
+ help: i18n.translate('visTypeXy.function.valueAxis.axisParams.help', {
+ defaultMessage: 'Value axis params',
+ }),
+ required: true,
+ },
+ },
+ fn: (context, args) => {
+ return {
+ type: 'value_axis',
+ name: args.name,
+ id: args.axisParams.id,
+ show: args.axisParams.show,
+ position: args.axisParams.position,
+ axisType: args.axisParams.axisType,
+ title: args.axisParams.title,
+ scale: args.axisParams.scale,
+ labels: args.axisParams.labels,
+ };
+ },
+});
diff --git a/src/plugins/vis_type_xy/public/expression_functions/vis_scale.ts b/src/plugins/vis_type_xy/public/expression_functions/vis_scale.ts
new file mode 100644
index 0000000000000..fadf3d80a6e81
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/expression_functions/vis_scale.ts
@@ -0,0 +1,98 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import type {
+ ExpressionFunctionDefinition,
+ Datatable,
+ ExpressionValueBoxed,
+} from '../../../expressions/public';
+import type { Scale } from '../types';
+
+export type ExpressionValueScale = ExpressionValueBoxed<
+ 'vis_scale',
+ {
+ boundsMargin?: Scale['boundsMargin'];
+ defaultYExtents?: Scale['defaultYExtents'];
+ max?: Scale['max'];
+ min?: Scale['min'];
+ mode?: Scale['mode'];
+ setYExtents?: Scale['setYExtents'];
+ scaleType: Scale['type'];
+ }
+>;
+
+export const visScale = (): ExpressionFunctionDefinition<
+ 'visscale',
+ Datatable | null,
+ Scale,
+ ExpressionValueScale
+> => ({
+ name: 'visscale',
+ help: i18n.translate('visTypeXy.function.scale.help', {
+ defaultMessage: 'Generates scale object',
+ }),
+ type: 'vis_scale',
+ args: {
+ boundsMargin: {
+ types: ['number', 'string'],
+ help: i18n.translate('visTypeXy.function.scale.boundsMargin.help', {
+ defaultMessage: 'Margin of bounds',
+ }),
+ },
+ defaultYExtents: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.scale.defaultYExtents.help', {
+ defaultMessage: 'Flag which allows to scale to data bounds',
+ }),
+ },
+ setYExtents: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.scale.setYExtents.help', {
+ defaultMessage: 'Flag which allows to set your own extents',
+ }),
+ },
+ max: {
+ types: ['number', 'null'],
+ help: i18n.translate('visTypeXy.function.scale.max.help', {
+ defaultMessage: 'Max value',
+ }),
+ },
+ min: {
+ types: ['number', 'null'],
+ help: i18n.translate('visTypeXy.function.scale.min.help', {
+ defaultMessage: 'Min value',
+ }),
+ },
+ mode: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.scale.mode.help', {
+ defaultMessage: 'Scale mode. Can be normal, percentage, wiggle or silhouette',
+ }),
+ },
+ type: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.scale.type.help', {
+ defaultMessage: 'Scale type. Can be linear, log or square root',
+ }),
+ required: true,
+ },
+ },
+ fn: (context, args) => {
+ return {
+ type: 'vis_scale',
+ boundsMargin: args.boundsMargin,
+ defaultYExtents: args.defaultYExtents,
+ setYExtents: args.setYExtents,
+ max: args.max,
+ min: args.min,
+ mode: args.mode,
+ scaleType: args.type,
+ };
+ },
+});
diff --git a/src/plugins/vis_type_xy/public/expression_functions/xy_dimension.ts b/src/plugins/vis_type_xy/public/expression_functions/xy_dimension.ts
new file mode 100644
index 0000000000000..ecbc3640c035b
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/expression_functions/xy_dimension.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 { i18n } from '@kbn/i18n';
+import type { ExpressionValueVisDimension } from '../../../visualizations/public';
+import type {
+ ExpressionFunctionDefinition,
+ Datatable,
+ ExpressionValueBoxed,
+} from '../../../expressions/public';
+import type { Dimension } from '../types';
+
+interface Arguments {
+ visDimension: ExpressionValueVisDimension;
+ params: string;
+ aggType: string;
+ label: string;
+}
+
+export type ExpressionValueXYDimension = ExpressionValueBoxed<
+ 'xy_dimension',
+ {
+ label: string;
+ aggType: string;
+ params: Dimension['params'];
+ accessor: number;
+ format: Dimension['format'];
+ }
+>;
+
+export const xyDimension = (): ExpressionFunctionDefinition<
+ 'xydimension',
+ Datatable | null,
+ Arguments,
+ ExpressionValueXYDimension
+> => ({
+ name: 'xydimension',
+ help: i18n.translate('visTypeXy.function.xydimension.help', {
+ defaultMessage: 'Generates xy dimension object',
+ }),
+ type: 'xy_dimension',
+ args: {
+ visDimension: {
+ types: ['vis_dimension'],
+ help: i18n.translate('visTypeXy.function.xyDimension.visDimension.help', {
+ defaultMessage: 'Dimension object config',
+ }),
+ required: true,
+ },
+ label: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.xyDimension.label.help', {
+ defaultMessage: 'Label',
+ }),
+ },
+ aggType: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.xyDimension.aggType.help', {
+ defaultMessage: 'Aggregation type',
+ }),
+ },
+ params: {
+ types: ['string'],
+ default: '"{}"',
+ help: i18n.translate('visTypeXy.function.xyDimension.params.help', {
+ defaultMessage: 'Params',
+ }),
+ },
+ },
+ fn: (context, args) => {
+ return {
+ type: 'xy_dimension',
+ label: args.label,
+ aggType: args.aggType,
+ params: JSON.parse(args.params!),
+ accessor: args.visDimension.accessor as number,
+ format: args.visDimension.format,
+ };
+ },
+});
diff --git a/src/plugins/vis_type_xy/public/expression_functions/xy_vis_fn.ts b/src/plugins/vis_type_xy/public/expression_functions/xy_vis_fn.ts
new file mode 100644
index 0000000000000..b8b8c0e8b8cca
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/expression_functions/xy_vis_fn.ts
@@ -0,0 +1,273 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+import type { ExpressionFunctionDefinition, Datatable, Render } from '../../../expressions/public';
+import type { ChartType } from '../../common';
+import type { VisParams, XYVisConfig } from '../types';
+
+export const visName = 'xy_vis';
+export interface RenderValue {
+ visData: Datatable;
+ visType: ChartType;
+ visConfig: VisParams;
+ syncColors: boolean;
+}
+
+export type VisTypeXyExpressionFunctionDefinition = ExpressionFunctionDefinition<
+ typeof visName,
+ Datatable,
+ XYVisConfig,
+ Render
+>;
+
+export const visTypeXyVisFn = (): VisTypeXyExpressionFunctionDefinition => ({
+ name: visName,
+ type: 'render',
+ context: {
+ types: ['datatable'],
+ },
+ help: i18n.translate('visTypeXy.functions.help', {
+ defaultMessage: 'XY visualization',
+ }),
+ args: {
+ type: {
+ types: ['string'],
+ default: '""',
+ help: 'xy vis type',
+ },
+ chartType: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.args.args.chartType.help', {
+ defaultMessage: 'Type of a chart. Can be line, area or histogram',
+ }),
+ },
+ addTimeMarker: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.args.addTimeMarker.help', {
+ defaultMessage: 'Show time marker',
+ }),
+ },
+ addLegend: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.args.addLegend.help', {
+ defaultMessage: 'Show chart legend',
+ }),
+ },
+ addTooltip: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.args.addTooltip.help', {
+ defaultMessage: 'Show tooltip on hover',
+ }),
+ },
+ legendPosition: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.args.legendPosition.help', {
+ defaultMessage: 'Position the legend on top, bottom, left, right of the chart',
+ }),
+ },
+ categoryAxes: {
+ types: ['category_axis'],
+ help: i18n.translate('visTypeXy.function.args.categoryAxes.help', {
+ defaultMessage: 'Category axis config',
+ }),
+ multi: true,
+ },
+ thresholdLine: {
+ types: ['threshold_line'],
+ help: i18n.translate('visTypeXy.function.args.thresholdLine.help', {
+ defaultMessage: 'Threshold line config',
+ }),
+ },
+ labels: {
+ types: ['label'],
+ help: i18n.translate('visTypeXy.function.args.labels.help', {
+ defaultMessage: 'Chart labels config',
+ }),
+ },
+ orderBucketsBySum: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.args.orderBucketsBySum.help', {
+ defaultMessage: 'Order buckets by sum',
+ }),
+ },
+ seriesParams: {
+ types: ['series_param'],
+ help: i18n.translate('visTypeXy.function.args.seriesParams.help', {
+ defaultMessage: 'Series param config',
+ }),
+ multi: true,
+ },
+ valueAxes: {
+ types: ['value_axis'],
+ help: i18n.translate('visTypeXy.function.args.valueAxes.help', {
+ defaultMessage: 'Value axis config',
+ }),
+ multi: true,
+ },
+ radiusRatio: {
+ types: ['number'],
+ help: i18n.translate('visTypeXy.function.args.radiusRatio.help', {
+ defaultMessage: 'Dot size ratio',
+ }),
+ },
+ gridCategoryLines: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.args.gridCategoryLines.help', {
+ defaultMessage: 'Show grid category lines in chart',
+ }),
+ },
+ gridValueAxis: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.args.gridValueAxis.help', {
+ defaultMessage: 'Name of value axis for which we show grid',
+ }),
+ },
+ isVislibVis: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.args.isVislibVis.help', {
+ defaultMessage:
+ 'Flag to indicate old vislib visualizations. Used for backwards compatibility including colors',
+ }),
+ },
+ detailedTooltip: {
+ types: ['boolean'],
+ help: i18n.translate('visTypeXy.function.args.detailedTooltip.help', {
+ defaultMessage: 'Show detailed tooltip',
+ }),
+ },
+ fittingFunction: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.args.fittingFunction.help', {
+ defaultMessage: 'Name of fitting function',
+ }),
+ },
+ times: {
+ types: ['time_marker'],
+ help: i18n.translate('visTypeXy.function.args.times.help', {
+ defaultMessage: 'Time marker config',
+ }),
+ multi: true,
+ },
+ palette: {
+ types: ['string'],
+ help: i18n.translate('visTypeXy.function.args.palette.help', {
+ defaultMessage: 'Defines the chart palette name',
+ }),
+ },
+ xDimension: {
+ types: ['xy_dimension', 'null'],
+ help: i18n.translate('visTypeXy.function.args.xDimension.help', {
+ defaultMessage: 'X axis dimension config',
+ }),
+ },
+ yDimension: {
+ types: ['xy_dimension'],
+ help: i18n.translate('visTypeXy.function.args.yDimension.help', {
+ defaultMessage: 'Y axis dimension config',
+ }),
+ multi: true,
+ },
+ zDimension: {
+ types: ['xy_dimension'],
+ help: i18n.translate('visTypeXy.function.args.zDimension.help', {
+ defaultMessage: 'Z axis dimension config',
+ }),
+ multi: true,
+ },
+ widthDimension: {
+ types: ['xy_dimension'],
+ help: i18n.translate('visTypeXy.function.args.widthDimension.help', {
+ defaultMessage: 'Width dimension config',
+ }),
+ multi: true,
+ },
+ seriesDimension: {
+ types: ['xy_dimension'],
+ help: i18n.translate('visTypeXy.function.args.seriesDimension.help', {
+ defaultMessage: 'Series dimension config',
+ }),
+ multi: true,
+ },
+ splitRowDimension: {
+ types: ['xy_dimension'],
+ help: i18n.translate('visTypeXy.function.args.splitRowDimension.help', {
+ defaultMessage: 'Split by row dimension config',
+ }),
+ multi: true,
+ },
+ splitColumnDimension: {
+ types: ['xy_dimension'],
+ help: i18n.translate('visTypeXy.function.args.splitColumnDimension.help', {
+ defaultMessage: 'Split by column dimension config',
+ }),
+ multi: true,
+ },
+ },
+ fn(context, args, handlers) {
+ const visType = args.chartType;
+ const visConfig = {
+ type: args.chartType,
+ addLegend: args.addLegend,
+ addTooltip: args.addTooltip,
+ legendPosition: args.legendPosition,
+ addTimeMarker: args.addTimeMarker,
+ categoryAxes: args.categoryAxes.map((categoryAxis) => ({
+ ...categoryAxis,
+ type: categoryAxis.axisType,
+ })),
+ orderBucketsBySum: args.orderBucketsBySum,
+ labels: args.labels,
+ thresholdLine: args.thresholdLine,
+ valueAxes: args.valueAxes.map((valueAxis) => ({ ...valueAxis, type: valueAxis.axisType })),
+ grid: {
+ categoryLines: args.gridCategoryLines,
+ valueAxis: args.gridValueAxis,
+ },
+ seriesParams: args.seriesParams.map((seriesParam) => ({
+ ...seriesParam,
+ type: seriesParam.seriesParamType,
+ })),
+ radiusRatio: args.radiusRatio,
+ times: args.times,
+ isVislibVis: args.isVislibVis,
+ detailedTooltip: args.detailedTooltip,
+ palette: {
+ type: 'palette',
+ name: args.palette,
+ },
+ fittingFunction: args.fittingFunction,
+ dimensions: {
+ x: args.xDimension,
+ y: args.yDimension,
+ z: args.zDimension,
+ width: args.widthDimension,
+ series: args.seriesDimension,
+ splitRow: args.splitRowDimension,
+ splitColumn: args.splitColumnDimension,
+ },
+ } as VisParams;
+
+ if (handlers?.inspectorAdapters?.tables) {
+ handlers.inspectorAdapters.tables.logDatatable('default', context);
+ }
+
+ return {
+ type: 'render',
+ as: visName,
+ value: {
+ context,
+ visType,
+ visConfig,
+ visData: context,
+ syncColors: handlers?.isSyncColorsEnabled?.() ?? false,
+ },
+ };
+ },
+});
diff --git a/src/plugins/vis_type_xy/public/plugin.ts b/src/plugins/vis_type_xy/public/plugin.ts
index d414da8f6dc97..7bdb4f78bc631 100644
--- a/src/plugins/vis_type_xy/public/plugin.ts
+++ b/src/plugins/vis_type_xy/public/plugin.ts
@@ -12,8 +12,6 @@ import { VisualizationsSetup, VisualizationsStart } from '../../visualizations/p
import { ChartsPluginSetup } from '../../charts/public';
import { DataPublicPluginStart } from '../../data/public';
import { UsageCollectionSetup } from '../../usage_collection/public';
-
-import { createVisTypeXyVisFn } from './xy_vis_fn';
import {
setDataActions,
setFormatService,
@@ -23,10 +21,13 @@ import {
setPalettesService,
setTrackUiMetric,
} from './services';
+
import { visTypesDefinitions } from './vis_types';
import { LEGACY_CHARTS_LIBRARY } from '../common';
import { xyVisRenderer } from './vis_renderer';
+import * as expressionFunctions from './expression_functions';
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface VisTypeXyPluginSetup {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
@@ -66,8 +67,18 @@ export class VisTypeXyPlugin
setUISettings(core.uiSettings);
setThemeService(charts.theme);
setPalettesService(charts.palettes);
- [createVisTypeXyVisFn].forEach(expressions.registerFunction);
+
expressions.registerRenderer(xyVisRenderer);
+ expressions.registerFunction(expressionFunctions.visTypeXyVisFn);
+ expressions.registerFunction(expressionFunctions.categoryAxis);
+ expressions.registerFunction(expressionFunctions.timeMarker);
+ expressions.registerFunction(expressionFunctions.valueAxis);
+ expressions.registerFunction(expressionFunctions.seriesParam);
+ expressions.registerFunction(expressionFunctions.thresholdLine);
+ expressions.registerFunction(expressionFunctions.label);
+ expressions.registerFunction(expressionFunctions.visScale);
+ expressions.registerFunction(expressionFunctions.xyDimension);
+
visTypesDefinitions.forEach(visualizations.createBaseVisualization);
}
diff --git a/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts b/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts
index c425eb71117e8..e15f9c4207702 100644
--- a/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts
+++ b/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts
@@ -1414,6 +1414,9 @@ export const sampleAreaVis = {
color: '#E7664C',
},
labels: {},
+ palette: {
+ name: 'default',
+ },
},
},
editorConfig: {
@@ -1575,6 +1578,9 @@ export const sampleAreaVis = {
style: 'full',
color: '#E7664C',
},
+ palette: {
+ name: 'default',
+ },
labels: {},
dimensions: {
x: {
diff --git a/src/plugins/vis_type_xy/public/to_ast.test.ts b/src/plugins/vis_type_xy/public/to_ast.test.ts
index 22e2d5f1cd9cc..4437986eff5f7 100644
--- a/src/plugins/vis_type_xy/public/to_ast.test.ts
+++ b/src/plugins/vis_type_xy/public/to_ast.test.ts
@@ -42,7 +42,7 @@ describe('xy vis toExpressionAst function', () => {
it('should match basic snapshot', () => {
toExpressionAst(vis, params);
- const [, builtExpression] = (buildExpression as jest.Mock).mock.calls[0][0];
+ const [, builtExpression] = (buildExpression as jest.Mock).mock.calls.pop()[0];
expect(builtExpression).toMatchSnapshot();
});
diff --git a/src/plugins/vis_type_xy/public/to_ast.ts b/src/plugins/vis_type_xy/public/to_ast.ts
index 84331af3a5329..c0a0ee566a445 100644
--- a/src/plugins/vis_type_xy/public/to_ast.ts
+++ b/src/plugins/vis_type_xy/public/to_ast.ts
@@ -11,13 +11,122 @@ import moment from 'moment';
import { VisToExpressionAst, getVisSchemas } from '../../visualizations/public';
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
import { BUCKET_TYPES } from '../../data/public';
+import { Labels } from '../../charts/public';
-import { DateHistogramParams, Dimensions, HistogramParams, VisParams } from './types';
-import { visName, VisTypeXyExpressionFunctionDefinition } from './xy_vis_fn';
+import {
+ DateHistogramParams,
+ Dimensions,
+ Dimension,
+ HistogramParams,
+ VisParams,
+ CategoryAxis,
+ SeriesParam,
+ ThresholdLine,
+ ValueAxis,
+ Scale,
+ TimeMarker,
+} from './types';
+import { visName, VisTypeXyExpressionFunctionDefinition } from './expression_functions/xy_vis_fn';
import { XyVisType } from '../common';
import { getEsaggsFn } from './to_ast_esaggs';
import { TimeRangeBounds } from '../../data/common';
+const prepareLabel = (data: Labels) => {
+ const label = buildExpressionFunction('label', {
+ ...data,
+ });
+
+ return buildExpression([label]);
+};
+
+const prepareScale = (data: Scale) => {
+ const scale = buildExpressionFunction('visscale', {
+ ...data,
+ });
+
+ return buildExpression([scale]);
+};
+
+const prepareThresholdLine = (data: ThresholdLine) => {
+ const thresholdLine = buildExpressionFunction('thresholdline', {
+ ...data,
+ });
+
+ return buildExpression([thresholdLine]);
+};
+
+const prepareTimeMarker = (data: TimeMarker) => {
+ const timeMarker = buildExpressionFunction('timemarker', {
+ ...data,
+ });
+
+ return buildExpression([timeMarker]);
+};
+
+const prepareCategoryAxis = (data: CategoryAxis) => {
+ const categoryAxis = buildExpressionFunction('categoryaxis', {
+ id: data.id,
+ show: data.show,
+ position: data.position,
+ type: data.type,
+ title: data.title.text,
+ scale: prepareScale(data.scale),
+ labels: prepareLabel(data.labels),
+ });
+
+ return buildExpression([categoryAxis]);
+};
+
+const prepareValueAxis = (data: ValueAxis) => {
+ const categoryAxis = buildExpressionFunction('valueaxis', {
+ name: data.name,
+ axisParams: prepareCategoryAxis({
+ ...data,
+ }),
+ });
+
+ return buildExpression([categoryAxis]);
+};
+
+const prepareSeriesParam = (data: SeriesParam) => {
+ const seriesParam = buildExpressionFunction('seriesparam', {
+ label: data.data.label,
+ id: data.data.id,
+ drawLinesBetweenPoints: data.drawLinesBetweenPoints,
+ interpolate: data.interpolate,
+ lineWidth: data.lineWidth,
+ mode: data.mode,
+ show: data.show,
+ showCircles: data.showCircles,
+ type: data.type,
+ valueAxis: data.valueAxis,
+ });
+
+ return buildExpression([seriesParam]);
+};
+
+const prepareVisDimension = (data: Dimension) => {
+ const visDimension = buildExpressionFunction('visdimension', { accessor: data.accessor });
+
+ if (data.format) {
+ visDimension.addArgument('format', data.format.id);
+ visDimension.addArgument('formatParams', JSON.stringify(data.format.params));
+ }
+
+ return buildExpression([visDimension]);
+};
+
+const prepareXYDimension = (data: Dimension) => {
+ const xyDimension = buildExpressionFunction('xydimension', {
+ params: JSON.stringify(data.params),
+ aggType: data.aggType,
+ label: data.label,
+ visDimension: prepareVisDimension(data),
+ });
+
+ return buildExpression([xyDimension]);
+};
+
export const toExpressionAst: VisToExpressionAst = async (vis, params) => {
const schemas = getVisSchemas(vis, params);
const dimensions: Dimensions = {
@@ -62,15 +171,13 @@ export const toExpressionAst: VisToExpressionAst = async (vis, params
}
}
- const visConfig = { ...vis.params };
-
(dimensions.y || []).forEach((yDimension) => {
const yAgg = responseAggs[yDimension.accessor];
- const seriesParam = (visConfig.seriesParams || []).find(
+ const seriesParam = (vis.params.seriesParams || []).find(
(param: any) => param.data.id === yAgg.id
);
if (seriesParam) {
- const usedValueAxis = (visConfig.valueAxes || []).find(
+ const usedValueAxis = (vis.params.valueAxes || []).find(
(valueAxis: any) => valueAxis.id === seriesParam.valueAxis
);
if (usedValueAxis?.scale.mode === 'percentage') {
@@ -79,11 +186,34 @@ export const toExpressionAst: VisToExpressionAst = async (vis, params
}
});
- visConfig.dimensions = dimensions;
-
const visTypeXy = buildExpressionFunction(visName, {
type: vis.type.name as XyVisType,
- visConfig: JSON.stringify(visConfig),
+ chartType: vis.params.type,
+ addTimeMarker: vis.params.addTimeMarker,
+ addLegend: vis.params.addLegend,
+ addTooltip: vis.params.addTooltip,
+ legendPosition: vis.params.legendPosition,
+ orderBucketsBySum: vis.params.orderBucketsBySum,
+ categoryAxes: vis.params.categoryAxes.map(prepareCategoryAxis),
+ valueAxes: vis.params.valueAxes.map(prepareValueAxis),
+ seriesParams: vis.params.seriesParams.map(prepareSeriesParam),
+ labels: prepareLabel(vis.params.labels),
+ thresholdLine: prepareThresholdLine(vis.params.thresholdLine),
+ gridCategoryLines: vis.params.grid.categoryLines,
+ gridValueAxis: vis.params.grid.valueAxis,
+ radiusRatio: vis.params.radiusRatio,
+ isVislibVis: vis.params.isVislibVis,
+ detailedTooltip: vis.params.detailedTooltip,
+ fittingFunction: vis.params.fittingFunction,
+ times: vis.params.times.map(prepareTimeMarker),
+ palette: vis.params.palette.name,
+ xDimension: dimensions.x ? prepareXYDimension(dimensions.x) : null,
+ yDimension: dimensions.y.map(prepareXYDimension),
+ zDimension: dimensions.z?.map(prepareXYDimension),
+ widthDimension: dimensions.width?.map(prepareXYDimension),
+ seriesDimension: dimensions.series?.map(prepareXYDimension),
+ splitRowDimension: dimensions.splitRow?.map(prepareXYDimension),
+ splitColumnDimension: dimensions.splitColumn?.map(prepareXYDimension),
});
const ast = buildExpression([getEsaggsFn(vis), visTypeXy]);
diff --git a/src/plugins/vis_type_xy/public/types/config.ts b/src/plugins/vis_type_xy/public/types/config.ts
index d5c5bfe004191..f025a36a82410 100644
--- a/src/plugins/vis_type_xy/public/types/config.ts
+++ b/src/plugins/vis_type_xy/public/types/config.ts
@@ -20,7 +20,7 @@ import {
YDomainRange,
} from '@elastic/charts';
-import { Dimension, Scale, ThresholdLine } from './param';
+import type { Dimension, Scale, ThresholdLine } from './param';
export interface Column {
id: string | null;
diff --git a/src/plugins/vis_type_xy/public/types/constants.ts b/src/plugins/vis_type_xy/public/types/constants.ts
index 5c2f23b76aa96..05ed0783d4c68 100644
--- a/src/plugins/vis_type_xy/public/types/constants.ts
+++ b/src/plugins/vis_type_xy/public/types/constants.ts
@@ -6,52 +6,43 @@
* Side Public License, v 1.
*/
-import { $Values } from '@kbn/utility-types';
-
-export const ChartMode = Object.freeze({
- Normal: 'normal' as const,
- Stacked: 'stacked' as const,
-});
-export type ChartMode = $Values;
-
-export const InterpolationMode = Object.freeze({
- Linear: 'linear' as const,
- Cardinal: 'cardinal' as const,
- StepAfter: 'step-after' as const,
-});
-export type InterpolationMode = $Values;
-
-export const AxisType = Object.freeze({
- Category: 'category' as const,
- Value: 'value' as const,
-});
-export type AxisType = $Values;
-
-export const ScaleType = Object.freeze({
- Linear: 'linear' as const,
- Log: 'log' as const,
- SquareRoot: 'square root' as const,
-});
-export type ScaleType = $Values