diff --git a/.gitignore b/.gitignore index b3911d0f8d0c2..b8adcf4508db2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /.es .DS_Store .node_binaries +.native_modules node_modules !/src/dev/npm/integration_tests/__fixtures__/fixture1/node_modules !/src/dev/notice/__fixtures__/node_modules diff --git a/.sass-lint.yml b/.sass-lint.yml index f868f5cc17900..055929006259c 100644 --- a/.sass-lint.yml +++ b/.sass-lint.yml @@ -12,6 +12,7 @@ files: - 'x-pack/plugins/maps/**/*.s+(a|c)ss' - 'x-pack/plugins/spaces/**/*.s+(a|c)ss' - 'x-pack/plugins/security/**/*.s+(a|c)ss' + - 'x-pack/plugins/monitoring/**/*.s+(a|c)ss' ignore: - 'x-pack/plugins/canvas/shareable_runtime/**/*.s+(a|c)ss' rules: diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc index 7411f37d3c692..54159b642dd1a 100644 --- a/docs/apm/api.asciidoc +++ b/docs/apm/api.asciidoc @@ -15,8 +15,39 @@ Some APM app features are provided via a REST API: [[apm-api-example]] === Using the APIs -Users interacting with APM APIs must have <>. -In addition, there are request headers to be aware of, like `kbn-xsrf: true`, and `Content-Type: applicaton/json`. +// The following content is reused throughout the API docs +// tag::using-the-APIs[] +Interact with APM APIs using cURL or another API tool. +All APM APIs are Kibana APIs, not Elasticsearch APIs; +because of this, the Kibana dev tools console cannot be used to interact with APM APIs. + +For all APM APIs, you must use a request header. +Supported headers are `Authorization`, `kbn-xsrf`, and `Content-Type`. + +`Authorization: ApiKey {credentials}`:: +Kibana supports token-based authentication with the Elasticsearch API key service. +The API key returned by the {ref}/security-api-create-api-key.html[Elasticsearch create API key API] +can be used by sending a request with an `Authorization` header that has a value of `ApiKey` followed by the `{credentials}`, +where `{credentials}` is the base64 encoding of `id` and `api_key` joined by a colon. ++ +Alternatively, you can create a user and use their username and password to authenticate API access: `-u $USER:$PASSWORD`. ++ +Whether using `Authorization: ApiKey {credentials}`, or `-u $USER:$PASSWORD`, +users interacting with APM APIs must have <>. + +`kbn-xsrf: true`:: + By default, you must use `kbn-xsrf` for all API calls, except in the following scenarios: + +* The API endpoint uses the `GET` or `HEAD` operations +* The path is whitelisted using the <> setting +* XSRF protections are disabled using the `server.xsrf.disableProtection` setting + +`Content-Type: application/json`:: + Applicable only when you send a payload in the API request. + {kib} API requests and responses use JSON. + Typically, if you include the `kbn-xsrf` header, you must also include the `Content-Type` header. +// end::using-the-APIs[] + Here's an example CURL request that adds an annotation to the APM app: [source,curl] @@ -38,9 +69,6 @@ curl -X POST \ }' ---- -The Kibana <> provides additional information on how to use Kibana APIs, -required request headers, and token-based authentication options. - //// ******************************************************* //// @@ -59,7 +87,15 @@ The following Agent configuration APIs are available: * <> to list all Agent configurations. * <> to search for an Agent configuration. -See <> for information on the privileges required to use this API endpoint. +[float] +[[use-agent-config-api]] +==== How to use APM APIs + +.Expand for required headers, privileges, and usage details +[%collapsible%closed] +====== +include::api.asciidoc[tag=using-the-APIs] +====== //// ******************************************************* @@ -100,7 +136,7 @@ See <> for information on the privileges required to [[apm-update-config-example]] ===== Example -[source,console] +[source,curl] -------------------------------------------------- PUT /api/apm/settings/agent-configuration { @@ -150,7 +186,7 @@ PUT /api/apm/settings/agent-configuration [[apm-delete-config-example]] ===== Example -[source,console] +[source,curl] -------------------------------------------------- DELETE /api/apm/settings/agent-configuration { @@ -228,7 +264,7 @@ DELETE /api/apm/settings/agent-configuration [[apm-list-config-example]] ===== Example -[source,console] +[source,curl] -------------------------------------------------- GET /api/apm/settings/agent-configuration -------------------------------------------------- @@ -293,7 +329,7 @@ GET /api/apm/settings/agent-configuration [[apm-search-config-example]] ===== Example -[source,console] +[source,curl] -------------------------------------------------- POST /api/apm/settings/agent-configuration/search { @@ -317,6 +353,9 @@ POST /api/apm/settings/agent-configuration/search The Annotation API allows you to annotate visualizations in the APM app with significant events, like deployments, allowing you to easily see how these events are impacting the performance of your existing applications. +By default, annotations are stored in a newly created `observability-annotations` index. +The name of this index can be changed in your `config.yml` by editing `xpack.observability.annotations.index`. + The following APIs are available: * <> to create an annotation for APM. @@ -324,10 +363,15 @@ The following APIs are available: // * <> GET /api/observability/annotation/:id // * <> DELETE /api/observability/annotation/:id -By default, annotations are stored in a newly created `observability-annotations` index. -The name of this index can be changed in your `config.yml` by editing `xpack.observability.annotations.index`. +[float] +[[use-annotation-api]] +==== How to use APM APIs -See <> for information on the privileges required to use this API endpoint. +.Expand for required headers, privileges, and usage details +[%collapsible%closed] +====== +include::api.asciidoc[tag=using-the-APIs] +====== //// ******************************************************* @@ -374,19 +418,20 @@ While you can add additional tags, you cannot remove the `apm` tag. The following example creates an annotation for a service named `opbeans-java`. -[source,console] +[source,curl] -------------------------------------------------- -POST /api/apm/services/opbeans-java/annotation -{ - "@timestamp": "2020-05-08T10:31:30.452Z", - "service": { - "version": "1.2" - }, - "message": "Deployment 1.2", - "tags": [ - "elastic.co", "customer" - ] -} +curl -X POST \ + http://localhost:5601/api/apm/services/opbeans-java/annotation \ +-H 'Content-Type: application/json' \ +-H 'kbn-xsrf: true' \ +-H 'Authorization: Basic YhUlubWZhM0FDbnlQeE6WRtaW49FQmSGZ4RUWXdX' \ +-d '{ + "@timestamp": "2020-05-08T10:31:30.452Z", + "service": { + "version": "1.2" + }, + "message": "Deployment 1.2" + }' -------------------------------------------------- [[apm-annotation-config-body]] diff --git a/docs/apm/deployment-annotations.asciidoc b/docs/apm/deployment-annotations.asciidoc index 142b0c0193d74..53fd963a81f73 100644 --- a/docs/apm/deployment-annotations.asciidoc +++ b/docs/apm/deployment-annotations.asciidoc @@ -18,7 +18,7 @@ Alternatively, you can explicitly create deployment annotations with our annotat The API can integrate into your CI/CD pipeline, so that each time you deploy, a POST request is sent to the annotation API endpoint: -[source,console] +[source,curl] ---- curl -X POST \ http://localhost:5601/api/apm/services/${SERVICE_NAME}/annotation \ <1> diff --git a/docs/development/core/public/kibana-plugin-core-public.applicationstart.navigatetoapp.md b/docs/development/core/public/kibana-plugin-core-public.applicationstart.navigatetoapp.md index b382d57a856a2..e1f08c7b38133 100644 --- a/docs/development/core/public/kibana-plugin-core-public.applicationstart.navigatetoapp.md +++ b/docs/development/core/public/kibana-plugin-core-public.applicationstart.navigatetoapp.md @@ -9,10 +9,7 @@ Navigate to a given app Signature: ```typescript -navigateToApp(appId: string, options?: { - path?: string; - state?: any; - }): Promise; +navigateToApp(appId: string, options?: NavigateToAppOptions): Promise; ``` ## Parameters @@ -20,7 +17,7 @@ navigateToApp(appId: string, options?: { | Parameter | Type | Description | | --- | --- | --- | | appId | string | | -| options | {
path?: string;
state?: any;
} | | +| options | NavigateToAppOptions | navigation options | Returns: diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index 7c7c9729504de..dda6b6ac0c60a 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -94,6 +94,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [LegacyCoreSetup](./kibana-plugin-core-public.legacycoresetup.md) | Setup interface exposed to the legacy platform via the ui/new_platform module. | | [LegacyCoreStart](./kibana-plugin-core-public.legacycorestart.md) | Start interface exposed to the legacy platform via the ui/new_platform module. | | [LegacyNavLink](./kibana-plugin-core-public.legacynavlink.md) | | +| [NavigateToAppOptions](./kibana-plugin-core-public.navigatetoappoptions.md) | Options for the [navigateToApp API](./kibana-plugin-core-public.applicationstart.navigatetoapp.md) | | [NotificationsSetup](./kibana-plugin-core-public.notificationssetup.md) | | | [NotificationsStart](./kibana-plugin-core-public.notificationsstart.md) | | | [OverlayBannersStart](./kibana-plugin-core-public.overlaybannersstart.md) | | diff --git a/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.md b/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.md new file mode 100644 index 0000000000000..aa51e5706e3d7 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [NavigateToAppOptions](./kibana-plugin-core-public.navigatetoappoptions.md) + +## NavigateToAppOptions interface + +Options for the [navigateToApp API](./kibana-plugin-core-public.applicationstart.navigatetoapp.md) + +Signature: + +```typescript +export interface NavigateToAppOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [path](./kibana-plugin-core-public.navigatetoappoptions.path.md) | string | optional path inside application to deep link to. If undefined, will use [the app's default path](./kibana-plugin-core-public.appbase.defaultpath.md)\` as default. | +| [replace](./kibana-plugin-core-public.navigatetoappoptions.replace.md) | boolean | if true, will not create a new history entry when navigating (using replace instead of push) | +| [state](./kibana-plugin-core-public.navigatetoappoptions.state.md) | unknown | optional state to forward to the application | + diff --git a/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.path.md b/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.path.md new file mode 100644 index 0000000000000..58ce7e02d8dd8 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.path.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [NavigateToAppOptions](./kibana-plugin-core-public.navigatetoappoptions.md) > [path](./kibana-plugin-core-public.navigatetoappoptions.path.md) + +## NavigateToAppOptions.path property + +optional path inside application to deep link to. If undefined, will use [the app's default path](./kibana-plugin-core-public.appbase.defaultpath.md)\` as default. + +Signature: + +```typescript +path?: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.replace.md b/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.replace.md new file mode 100644 index 0000000000000..9530d03486299 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.replace.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [NavigateToAppOptions](./kibana-plugin-core-public.navigatetoappoptions.md) > [replace](./kibana-plugin-core-public.navigatetoappoptions.replace.md) + +## NavigateToAppOptions.replace property + +if true, will not create a new history entry when navigating (using `replace` instead of `push`) + +Signature: + +```typescript +replace?: boolean; +``` + +## Remarks + +This option not be used when navigating from and/or to legacy applications. + diff --git a/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.state.md b/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.state.md new file mode 100644 index 0000000000000..ccb76f7bbf18e --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.navigatetoappoptions.state.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [NavigateToAppOptions](./kibana-plugin-core-public.navigatetoappoptions.md) > [state](./kibana-plugin-core-public.navigatetoappoptions.state.md) + +## NavigateToAppOptions.state property + +optional state to forward to the application + +Signature: + +```typescript +state?: unknown; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.sessionstoragecookieoptions.md b/docs/development/core/server/kibana-plugin-core-server.sessionstoragecookieoptions.md index c9c5ac72c6e1b..b5dad11117359 100644 --- a/docs/development/core/server/kibana-plugin-core-server.sessionstoragecookieoptions.md +++ b/docs/development/core/server/kibana-plugin-core-server.sessionstoragecookieoptions.md @@ -19,5 +19,6 @@ export interface SessionStorageCookieOptions | [encryptionKey](./kibana-plugin-core-server.sessionstoragecookieoptions.encryptionkey.md) | string | A key used to encrypt a cookie's value. Should be at least 32 characters long. | | [isSecure](./kibana-plugin-core-server.sessionstoragecookieoptions.issecure.md) | boolean | Flag indicating whether the cookie should be sent only via a secure connection. | | [name](./kibana-plugin-core-server.sessionstoragecookieoptions.name.md) | string | Name of the session cookie. | +| [sameSite](./kibana-plugin-core-server.sessionstoragecookieoptions.samesite.md) | 'Strict' | 'Lax' | 'None' | Defines SameSite attribute of the Set-Cookie Header. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite | | [validate](./kibana-plugin-core-server.sessionstoragecookieoptions.validate.md) | (sessionValue: T | T[]) => SessionCookieValidationResult | Function called to validate a cookie's decrypted value. | diff --git a/docs/development/core/server/kibana-plugin-core-server.sessionstoragecookieoptions.samesite.md b/docs/development/core/server/kibana-plugin-core-server.sessionstoragecookieoptions.samesite.md new file mode 100644 index 0000000000000..a2bf79a52d521 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.sessionstoragecookieoptions.samesite.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SessionStorageCookieOptions](./kibana-plugin-core-server.sessionstoragecookieoptions.md) > [sameSite](./kibana-plugin-core-server.sessionstoragecookieoptions.samesite.md) + +## SessionStorageCookieOptions.sameSite property + +Defines SameSite attribute of the Set-Cookie Header. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + +Signature: + +```typescript +sameSite?: 'Strict' | 'Lax' | 'None'; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.expandshorthand.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.expandshorthand.md index 71835e6d28763..6c8594b7eeffd 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.expandshorthand.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.expandshorthand.md @@ -8,5 +8,5 @@ Signature: ```typescript -expandShorthand: (sh: Record) => Record +expandShorthand: (sh: Record) => MappingObject ``` diff --git a/docs/index.asciidoc b/docs/index.asciidoc index add91600a34ea..182e16d2baa1d 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -17,6 +17,7 @@ include::{docs-root}/shared/versions/stack/{source_branch}.asciidoc[] :commit: {repo}commit/ :blob: {repo}blob/{branch}/ :security-ref: https://www.elastic.co/community/security/ +:kibana-ref-all: https://www.elastic.co/guide/en/kibana include::{docs-root}/shared/attributes.asciidoc[] @@ -26,8 +27,6 @@ include::accessibility.asciidoc[] include::limitations.asciidoc[] -include::release-notes/highlights.asciidoc[] - include::migration.asciidoc[] include::CHANGELOG.asciidoc[] diff --git a/docs/migration.asciidoc b/docs/migration.asciidoc index e42f0399b40a2..8a9bddc87515b 100644 --- a/docs/migration.asciidoc +++ b/docs/migration.asciidoc @@ -15,7 +15,7 @@ your application from one version of Kibana to another. * <> * <> -See also <> and <>. +See also <> and <>. -- include::migration/migrate_7_7.asciidoc[] diff --git a/docs/migration/migrate_7_0.asciidoc b/docs/migration/migrate_7_0.asciidoc index bfbf09f55c26b..01857f0de5588 100644 --- a/docs/migration/migrate_7_0.asciidoc +++ b/docs/migration/migrate_7_0.asciidoc @@ -7,7 +7,7 @@ This section discusses the changes that you need to be aware of when migrating your application to Kibana 7.0. -See also <> and <>. +See also <> and <>. * <> * <> diff --git a/docs/migration/migrate_7_2.asciidoc b/docs/migration/migrate_7_2.asciidoc index ac877f8514e64..ca3555fc3aab9 100644 --- a/docs/migration/migrate_7_2.asciidoc +++ b/docs/migration/migrate_7_2.asciidoc @@ -7,7 +7,8 @@ This section discusses the changes that you need to be aware of when migrating your application to Kibana 7.2. -See also <> and <>. +See also {kibana-ref-all}/7.2/release-highlights-7.2.0.html[release highlights] +and <>. //NOTE: The notable-breaking-changes tagged regions are re-used in the //Installation and Upgrade Guide diff --git a/docs/migration/migrate_7_3.asciidoc b/docs/migration/migrate_7_3.asciidoc index 27ae9767a4155..0d8f883e0be23 100644 --- a/docs/migration/migrate_7_3.asciidoc +++ b/docs/migration/migrate_7_3.asciidoc @@ -7,7 +7,8 @@ This section discusses the changes that you need to be aware of when migrating your application to Kibana 7.3. -See also <> and <>. +See also {kibana-ref-all}/7.3/release-highlights-7.3.0.html[release highlights] +and <>. //NOTE: The notable-breaking-changes tagged regions are re-used in the //Installation and Upgrade Guide diff --git a/docs/migration/migrate_7_4.asciidoc b/docs/migration/migrate_7_4.asciidoc index 159e74c191ca0..8ff3c06aecbde 100644 --- a/docs/migration/migrate_7_4.asciidoc +++ b/docs/migration/migrate_7_4.asciidoc @@ -7,7 +7,7 @@ This section discusses the changes that you need to be aware of when migrating your application to Kibana 7.4. -//See also <> and <>. +//See also {kibana-ref-all}/7.4/release-highlights-7.4.0.html[release highlights] and <>. //NOTE: The notable-breaking-changes tagged regions are re-used in the //Installation and Upgrade Guide diff --git a/docs/migration/migrate_7_5.asciidoc b/docs/migration/migrate_7_5.asciidoc index 04e8de26297c5..b0bf2a247215b 100644 --- a/docs/migration/migrate_7_5.asciidoc +++ b/docs/migration/migrate_7_5.asciidoc @@ -7,7 +7,7 @@ This page discusses the breaking changes that you need to be aware of when migrating your application to Kibana 7.5. -//See also <> and <>. +//See also {kibana-ref-all}/7.5/release-highlights-7.5.0.html[release highlights] and <>. //NOTE: The notable-breaking-changes tagged regions are re-used in the //Installation and Upgrade Guide diff --git a/docs/release-notes/highlights-7.0.0.asciidoc b/docs/release-notes/highlights-7.0.0.asciidoc deleted file mode 100644 index c18a635ebe286..0000000000000 --- a/docs/release-notes/highlights-7.0.0.asciidoc +++ /dev/null @@ -1,124 +0,0 @@ -[[release-highlights-7.0.0]] -== 7.0.0 release highlights -++++ -7.0.0 -++++ - -Each release of {kib} brings new features and product improvements. -Here are the highlights of the feature and user experience changes in 7.0.0. - -Refer to the {kib} <> and <> for a list of bug fixes and other changes. - -//NOTE: The notable-highlights tagged regions are re-used in the -//Installation and Upgrade Guide - -// tag::notable-highlights[] - -[float] -=== Fresh, sleek design - -Since the introduction of the https://elastic.github.io/eui/#/[Elastic UI framework] in 6.2, -{kib} has undergone a major design revamp. This release showcases a lighter, more -minimal design that brings it all together. The biggest change is a persistent global header, which -displays information related to where you are in {kib}. The side navigation -is now collapsed by default, giving applications more horizontal space. -Dashboards and most of the {kib} applications also got much needed touch-ups. -Finally, the font changed to https://rsms.me/inter/[Inter UI], which is closer to -more modern system fonts found on your OS of choice. - - -[role="screenshot"] -image::images/highlights-7.0.0-design.png[Fresh, sleek Kibana design] - -[float] -=== {kib} Query Language on by default - -In 6.3, {kib} introduced the -{kibana-ref}/kuery-query.html[Kibana Query Language (KQL)], and in 7.0.0, -KQL is the default. KQL offers a simplified query syntax and support for -scripted fields. If you have a https://www.elastic.co/subscriptions[Basic license] -(the default distribution) or above, KQL also provides -autocomplete. If you prefer the -{kibana-ref}/lucene-query.html[Lucene query syntax], you have the option -to turn off KQL. - -[role="screenshot"] -image::images/highlights-7.0.0-kql.png[Kibana Query Language] - -[float] -=== {kib} dark theme - -{kib} now supports a dark theme across the entire application. To try out this theme, go -to *Management > Advanced settings*, flip the dark mode switch, and reload your -page. Because theme selection is saved at the {kibana-ref}/xpack-spaces.html[Spaces] level, -you can no longer set a dark or light theme on an individual dashboard. - -[role="screenshot"] -image::images/highlights-7.0.0-dark-theme.png[Kibana dark theme] - -// end::notable-highlights[] - -[float] -=== Responsive dashboards - -experimental[] As part of the enhancements to {kib} dashboards in 7.0, -the design is -more responsive. If you want to view and interact with {kib} on the go, -pull it up on your phone and test out this new, experimental responsive layout. - - -[role="screenshot"] -image::images/highlights-7.0.0-responsive.png[Responsive dashboard] - - -[float] -=== New look for timepicker and filters - -The timepicker now makes it easier to deal with complex date queries. For example, -you can mix relative times with absolute ones. The timepicker design is also improved. -The control no longer pushes the content down the page as you interact -with it. The design is also more compact, which is necessary -to make it work better on mobile dashboards. - -[role="screenshot"] -image::images/highlights-7.0.0-timepicker.png[Kibana timepicker] - -Filters are enhanced in two ways. Each filter now provides a dropdown menu -that clearly defines the actions you can take on the filter. There’s also a -new menu for dealing with filters in bulk. - -[role="screenshot"] -image::images/highlights-7.0.0-filters.png[Filters menu] - -[float] -=== Saved objects structure improvements - -The <> so -that they are now aware of each other. -This means you can now export a dashboard, -including all visualization dependencies, from the Saved objects page. -You can also easily move objects from one {kib} instance to another or -between Spaces. - -[float] -=== Canvas enhancements - -Canvas has two notable improvements: an expandable expression editor and -additional keyboard shortcuts. - -To maximize or minimize the expression editor, -use the expand button image:images/highlights-7.0.0-expand.png[] -in the top right corner of the editor. To adjust the font size, use the slider -at the bottom. - -Canvas has more keyboard shortcuts for moving elements to the front and -back and for copying your elements. To discover all shortcuts available in Canvas, -click the help icon. - -[role="screenshot"] -image::images/highlights-7.0.0-canvas-kbd.png[] - - - - diff --git a/docs/release-notes/highlights-7.1.0.asciidoc b/docs/release-notes/highlights-7.1.0.asciidoc deleted file mode 100644 index 3d30dc13ef229..0000000000000 --- a/docs/release-notes/highlights-7.1.0.asciidoc +++ /dev/null @@ -1,22 +0,0 @@ -[[release-highlights-7.1.0]] -== 7.1.0 release highlights -++++ -7.1.0 -++++ - -Each release of {kib} brings new features and product improvements. Here are the -highlights of the feature and user experience changes in 7.1.0. - -//NOTE: The notable-highlights tagged regions are re-used in the -//Installation and Upgrade Guide - -// tag::notable-highlights[] - -* Some Elastic Stack security features, such as encrypted communications, -file and native authentication, and role-based access control, are now available -in more subscription levels. For details, see https://www.elastic.co/subscriptions. - -Refer to the <> for a list of -enhancements and bug fixes. - -// end::notable-highlights[] \ No newline at end of file diff --git a/docs/release-notes/highlights-7.2.0.asciidoc b/docs/release-notes/highlights-7.2.0.asciidoc deleted file mode 100644 index 746793e05bab4..0000000000000 --- a/docs/release-notes/highlights-7.2.0.asciidoc +++ /dev/null @@ -1,249 +0,0 @@ -[[release-highlights-7.2.0]] -== 7.2.0 release highlights -++++ -7.2.0 -++++ - -Each release of {kib} brings new features and product improvements. -Here are the highlights of the feature and user experience changes in 7.2.0. - -Refer to the {kib} <> and <> for a list of bug fixes and other changes. - -//NOTE: The notable-highlights tagged regions are re-used in the -//Installation and Upgrade Guide - -// tag::notable-highlights[] -// end::notable-highlights[] - - -[float] -=== Feature controls - -Want to hide Dev Tools from the left navigation? Show Stack Monitoring only to -admins? Or, give certain users access to only Dashboard and Canvas? -<> allow you to hide and -restrict applications and features in the {kib} UI. - -[role="screenshot"] -image::release-notes/images/7.2-feature-controls.png[Feature controls] - -You can configure {kib} applications and features based on your users’ needs, -and when used with security, based on their privileges. This means different -roles can have access to different features in the same space. Power users -might have privileges to create and edit visualizations and dashboards, -while analysts or executives might have Dashboard and Canvas with -read-only privileges. - -[role="screenshot"] -image::release-notes/images/7.2-space-privileges.png[Space privileges] - -[float] -=== Saved Object Import and Export API - -{kib} has new APIs for <> and -<> saved objects, including dashboards, -visualizations, index patterns, maps, Canvas workpads, and advanced settings. -A benefit of this new API is that it automatically exports the dependencies -of the saved object. Just export the dashboard you need and its visualizations -and index patterns are exported as well. You can use the new APIs in the Management UI, -or call them directly. - -[role="screenshot"] -image::release-notes/images/7.2-saved-objects.png[Saved objects] - -[float] -=== Rolled up data in Time Series Visual Builder - -Time Series Visual Builder now has features for visualizing rolled up data. Similar -to Visualize, you can create index patterns that use rollup indices or mix rollup -and raw indices to visualize all data together. You can also create multiple -y-axes with different index patterns or rollup indices in the same chart. - -When you specify the index in Visual Builder, you must explicitly state the -rollup name. Visual Builder will not include a rollup index if you use a wildcard. -See https://github.com/elastic/kibana/pull/28762[PR #28762] -for details. - -[float] -=== Canvas enhancements - -Canvas provides enhancements to custom elements, workpad filtering, and autoplay. - -[float] -==== Custom elements - -Elements, such as charts, metrics, shapes, and images, are the building blocks -of a Canvas workpad. You can combine these elements to create new and interesting -presentations of your data. Your custom element might be as simple as a metric -on top of a geometric shape. Or, you might create a more complex grouping backed -by queries and conditional statements. - -With 7.2, you can save custom elements for reuse across your workpads. This -reduces the need for copy and paste. It also enables you to share your custom -elements with your teammates. - -[role="screenshot"] -image::release-notes/images/7.2-canvas-custom-elements.png[Canvas custom elements] - -[float] -==== Filter groups - -If you’ve used the time or dropdown filter elements in Canvas, you know that -they apply to the entire workpad (and not just the page they are in). This -behavior is still the default, but you can now choose which elements are -affected by specific filters. This gives you more options for exploring data -visualizations. - -[role="screenshot"] -image::release-notes/images/7.2-canvas-filters.png[Canvas custom elements] - -[float] -==== Autoplay - -As part of the ongoing work to provide kiosk mode in Canvas, version 7.2 -introduces the ability to autoplay your workpad pages. Once enabled, full screen -mode automatically cycles through your workpad pages based on the time interval -you define. - -[float] -=== Snapshot repositories - -This release introduces phase one of the {kib} Snapshot UI for managing backups -of your {es} indices and clusters. Snapshots are important because they provide -a copy of your data in case something goes wrong. If you need to roll back to an -older version of your data, you can restore a snapshot from the repository. - -Use the Snapshot UI to register new repositories, edit existing ones, and drill down into -details about your repositories and snapshots. See <> -for step-by-step instructions. Additional snapshot and recovery features are -planned for upcoming versions. - -[role="screenshot"] -image::release-notes/images/7.2-snapshot-repositories.png[Snapshot repositories] - -[float] -=== Maps in dashboards and other improvements - -beta[]The Maps app now adds the ability to include your saved map on a -dashboard. You can view and interact with your geospatial data alongside -bar charts, pie charts, and other visualizations. Click the map to -quickly hone in on the data you desire, and that data is shown across the dashboard, -similar to how you interact with other visualization types. See -<> for step-by-step instructions. - -[role="screenshot"] -image::release-notes/images/7.2-maps.png[Embedded map in dark mode] - -Additional improvements to the new Maps app include: - -* *Dark mode.* Building on the support for dark mode in {kib} 7.0, the -Elastic Maps Service now publishes dark themed tiles. This means that the -Maps app now fully respects dark mode. - -* *Collapsible map legends.* Use this feature to get an unobstructed -view of a map in a dashboard. - -* *Class styling.* With this style, you can symbolize map features by class. -Use layer filtering to define the class for each layer, and static styling to -symbolize each class. See <> for an example. - - -[float] -=== {kib} in Japanese - -{kib} now supports Japanese, in addition to Simplified Chinese and English. -If you’d like to use {kib} with Japanese, set `i18n.locale` to `ja-JP` in your -`kibana.yml` file. The Japanese localization is a work in progress. -We welcome your feedback in the form of a https://github.com/elastic/kibana/issues[Github issue]. -You can also https://github.com/elastic/kibana/pulls[issue a pull request in GitHub]. - -[role="screenshot"] -image::release-notes/images/7.2-localization.jpg[Kibana in Japanese] - - -[float] -=== {dataframes-cap} - -beta[] Machine Learning now enables you to transform your data with -{ref}/transforms.html[{dataframes}]. A -new wizard guides you through the process of creating a {dataframe}: selecting -the source data, the fields you want to group by, and the aggregations for -pivoting the data. There is also a preview of the transform for reviewing the configuration. - -[role="screenshot"] -image::release-notes/images/7.2-data-frames.png[{dataframes-cap}] - -{transforms-cap} are managed on a new list page, displaying the details and -status of each {transform}, and controls for starting, stopping, or deleting {transforms}. - -[role="screenshot"] -image::release-notes/images/7.2-data-frames-list-view.png[{dataframes-cap}] - -[float] -=== Search for influencers in Anomaly Explorer - -A query bar is now available in the Machine Learning Anomaly Explorer, which -enables you to filter anomaly results for one or more selected influencers. -The query bar suggests influencer field names and values for the selected jobs -as you type. - -[float] -=== Simplified creation of {ml} jobs for sample data sets and {metricbeat} data - -If you use the {kib} sample data sets, there are now {ml} jobs for the -sample eCommerce orders and the sample web logs. These jobs demonstrate a -variety of ways that you can detect anomalous behavior in the sample data. You -can add the jobs when you <> or use the -supplied configuration when you are creating jobs in the Machine Learning app. - -Likewise, if you use the -{metricbeat-ref}/metricbeat-module-system.html[{metricbeat} system module] to -monitor your servers, there is a {ml} wizard that recognizes this type of data. -For example, with the supplied configuration you can detect unusual increases in -disk utilization: - -[role="screenshot"] -image::release-notes/images/7.2-metricbeat-ml-modules.png[Embedded map in dark mode] - -These wizards expedite the creation of pre-configured jobs, dashboards, searches, -and visualizations. For more information, see <>. - -[float] -=== View recent log entries alongside your monitoring data - -You can now see your cluster's recent log entries within the *Stack Monitoring* -application: - -[role="screenshot"] -image::release-notes/images/7.2-monitoring-logs.png["Monitoring recent {es} logs"] - -See <>. If you want to see earlier log entries, you can -follow a link to the <> and continue your investigation -there. - -TIP: You must use {filebeat} to collect the logs. - -//See {ref}/configuring-filebeat.html[Collecting {es} log data with {filebeat}]. - -[float] -=== Observability - -Observability has two new notable features. - -* Metrics Explorer is a new view in the Infrastructure app. Metrics Explorer -enhances how you interact with infrastructure metrics in an ad hoc way. - -* APM agents now collect language-specific metrics, in addition to the common -key performance indicators. These new metrics are automatically displayed in the APM UI. - -For more information, visit the https://www.elastic.co/blog/elastic-observability-update-7-2-0[Observability blog]. - -[float] -=== Give us your feedback - -Enjoy all the new features and functionality in {kib} 7.2. Play around with them -locally or on the https://www.elastic.co/cloud/elasticsearch-service[Elasticsearch Service] -and reach out on the https://discuss.elastic.co/c/kibana[Kibana Discuss forum] -if you have any questions or feedback. - diff --git a/docs/release-notes/highlights-7.3.0.asciidoc b/docs/release-notes/highlights-7.3.0.asciidoc deleted file mode 100644 index c7cc81e53ab00..0000000000000 --- a/docs/release-notes/highlights-7.3.0.asciidoc +++ /dev/null @@ -1,197 +0,0 @@ -[[release-highlights-7.3.0]] -== 7.3.0 release highlights -++++ -7.3.0 -++++ - -Each release of {kib} brings new features and product improvements. -Here are the highlights of the feature and user experience changes in 7.3.0. - -Refer to the {kib} <> and <> for a list of bug fixes and other changes. - -//NOTE: The notable-highlights tagged regions are re-used in the -//Installation and Upgrade Guide - -* <> -* <> -* <> -* <> -* <> -* <> -* <> -* <> -* <> - - -[float] -[[maps-7.3]] -=== Maps app is now production ready - -Maps is production ready for 7.3, and includes many improvements, including -a new GeoJson upload and improved layer styling, aggregations, and tooltips. -To find out more about what’s in Maps and how to use it, check out the -https://www.elastic.co/blog/elastic-maps-7-3-0-released[Maps 7.3.0 release blog] and -<>. - -[role="screenshot"] -image::release-notes/images/7.3-maps.png[Maps is production ready] - - -[float] -==== Upload GeoJSON data (beta) - -Maps now makes it easier for you to get your geospatial data into the Elastic Stack. -Use the new GeoJSON upload feature to drag and drop your point, shape files, -and features directly into {es}, so you can use them as layers in the map. - -[float] -==== More layer styling - -Maps gives you more options for styling your layers. You can now select the -color ramp for heatmap layers. You can also symbolize geo_point features with icons that you can color, size, and rotate based on property values. - -[float] -==== New top hits aggregation - -The new top hits aggregation enables you to plot the most recent entities from your {es} indices. - -[float] -==== Better tooltip content - -Maps now helps you create more in-depth tooltip content. Your tooltips can -display information on each feature at the current location. -To cycle through the features and view their properties, use the tooltip page controls. -You can also include custom labels for Grid aggregation and join metrics. - -[float] -[[kerberos-7.3]] -=== Kerberos authentication provider - -{es} provides support for Kerberos starting in 6.6, and in 7.3, -{kib} introduces Kerberos authentication provider. You now have single sign-on -access and can log into {kib} without repeatedly providing your username and password. -For details, see <>. - -[float] -[[snapshot-7.3]] -=== Snapshot phase 2: restore and delete - -The second phase of the snapshot management UI is out, and includes -two notable enhancements: restore and delete. You can recover data and state -from a snapshot using the new *Restore* wizard, and track its progress in the -*Restore Status* view. You can delete snapshots, and free up repository storage space, -in the *Snapshots* view. For more information, see <>. - -[role="screenshot"] -image::release-notes/images/7.3-snapshot-restore.png[Snapshot and restore phase 2] - -[float] -[[csv-export-7.3]] -=== CSV export from a saved search - -{kib} now allows you to export a saved search on a dashboard directly to CSV. -This action is available in the dashboard panel menu for the saved search. -To enable this feature, you must set -`xpack.reporting.csv.enablePanelActionDownload: true` in your `kibana.yml` file. - -[role="screenshot"] -image::release-notes/images/7.3-csv-export.png[CSV export from saved search] - -[float] -[[canvas-7.3]] -=== Canvas templates, experiences, and documentation - -Canvas improves the user experience with new templates, experiences, and documentation. - -[float] -==== Templates - -Canvas is all about creating content and telling a story with your data. The -content templates can help you facilitate this vision, and in -7.3, Canvas releases three new templates: - -* Pitch is a 14-page template that you can brand and customize for your -next board-level presentation. - -* The Status template gives you a professional template -for status reports and other updates. - -* The Summary template, shown below, can help you create a report with live representations -of your data. -+ -[role="screenshot"] -image::release-notes/images/7.3-canvas-summary.png[Canvas summary template] - -[float] -==== Experiences - -Canvas adds a number of experiences in 7.3: nudge, alignment, distribution, -zoom, and fit-to-screen. Nudge enables you to more precisely align elements -in your workpad. Use the up, down, left, or right arrow key to nudge an -element by 10 pixels. To nudge an element by 1 pixel, use Shift + an arrow key. - -[float] -==== Documentation - -Check out the -updated <>, the new introduction to <>, -and the improved <>. - -[float] -[[KQL-7.3]] -=== KQL + autocomplete in filter aggregations - -In 7.0 and later, the {kib} query language (KQL) is enabled by default, -making it easier to create queries. If you have a Basic license or above, -autocomplete is also enabled. Starting in 7.3, {kib} expands KQL and -autocomplete support to include filter aggregations in Visualize and -TSVB. Just start typing, and you’ll get suggestions for fields, values -for keyword fields, and query operators. - -[role="screenshot"] -image::release-notes/images/7.3-kql.png[Canvas summary template] - -[float] -[[bar-charts-7.3]] -=== Values inside bar charts - -You can now show values directly inside a bar chart, if space is available. -When you create the bar chart, open Panel Settings and select *Show values on chart*. - -[role="screenshot"] -image::release-notes/images/7.3-bar-charts.png[Canvas summary template] - -[float] -[[visual-builder-7.3]] -=== Visual Builder: even better under a new name - -Time Series Visual Builder is now simply referred to as TSVB. When you open -TSVB, you’ll also notice several enhancements -and bug fixes. - -[float] -[[kibana-observability-7.3]] -=== Observability enhancements - -Check out these release blogs: - -* https://www.elastic.co/blog/elastic-apm-7-3-0-released[APM] -* https://www.elastic.co/blog/elastic-infrastructure-7-3-0-released[Infrastructure] -* https://www.elastic.co/blog/elastic-logs-7-3-0-released[Logs] -* https://www.elastic.co/blog/elastic-uptime-monitoring-7-3-0-released[Uptime] - -[float] -=== Give us your feedback - -Enjoy all the new features and functionality in {kib} 7.3. -Play around with them locally or on the https://www.elastic.co/products/elasticsearch/service[{es} Service] -and reach out on the -https://discuss.elastic.co/c/kibana[{kib} Discuss forum] if you have any questions or feedback. - - - - - -// tag::notable-highlights[] -// end::notable-highlights[] diff --git a/docs/release-notes/highlights-7.4.0.asciidoc b/docs/release-notes/highlights-7.4.0.asciidoc deleted file mode 100644 index 507189036034c..0000000000000 --- a/docs/release-notes/highlights-7.4.0.asciidoc +++ /dev/null @@ -1,84 +0,0 @@ -[[release-highlights-7.4.0]] -== 7.4.0 release highlights -++++ -7.4.0 -++++ - -Each release of {kib} brings new features and product improvements. -Here are the most notable feature and user experience changes in 7.4. - -//NOTE: The notable-highlights tagged regions are re-used in the -//Installation and Upgrade Guide - -// tag::notable-highlights[] - -For a complete list of highlights, -see the https://www.elastic.co/blog/kibana-7-4-0-released[{kib} 7.4 release blog]. - -[float] -[[pki-7.4]] -=== Public Key Infrastructure (PKI) authentication - -Building off the success of feature controls in 7.2 and -support for Kerberos in 7.3, {kib} 7.4 adds support for another -highly requested authentication mechanism, Public Key Infrastructure (PKI). -With native support for PKI authentication, users are now able to log into -{kib} using X.509 client certificates and a two-way encryption system. -This provides new ways to authenticate {kib}--from general client certificate -authorization to identity card access. To start using PKI in production, check out -{kibana-ref}/kibana-authentication.html#pki-authentication[our documentation]. - -[float] -[[siem-gets-maps-7.4]] -=== SIEM gets maps - -A geospatial perspective is especially useful for security -events data sets. In 7.4, SIEM embeds maps directly in -the Network interface to show common request paths between origin and -destination sources. -For more information, see the https://www.elastic.co/blog/elastic-siem-7-4-0-released[SIEM 7.4 release blog]. - - -[role="screenshot"] -image::release-notes/images/7.4-siem-maps.png[SIEM and Maps] - -[float] -[[saved-queries-and-filters-7.4]] -=== Reusable saved queries and filters - -Have you ever written a query in {kib} that you’ve wanted to reuse? -7.4 introduces saved queries, which -save the contents of the search bar query, filter pills, and time filter. You can now reuse your -saved query in dashboards, visualizations, maps, and saved -searches, and share them with other users. - -[role="screenshot"] -image::release-notes/images/7.4-saved-queries.gif[Saved queries] - -[float] -[[snapshot-lifecycle-management-7.4]] -=== Snapshot lifecycle management - -Adding to the *Snapshot and Restore* UI introduced in 7.2, -{kib} has a new view -for managing snapshot lifecycle policies. The *Policies* view provides an overview -of your snapshot status, a history of the -snapshots taken with the defined policies, and a wizard for creating a new policy. - -[role="screenshot"] -image::release-notes/images/7.4-snapshot-and-restore.png[Policies view in Snapshot and Restore] - -[float] -[[outlier-detection-7.4]] -=== Outlier detection on transforms - -experimental[] In 7.3, {kib} added an API that provided outlier detection analysis. In this release, -{kib} has a new view in Machine Learning where you can create, -manage, and view your outlier detection jobs and their results. Because the -results are just another index, you can create some insightful visualizations and -maps based on the outliers found in your entity-centric data. - -[role="screenshot"] -image::release-notes/images/7.4-outlier-detection.gif[Outlier detection analysis] - -// end::notable-highlights[] diff --git a/docs/release-notes/highlights-7.5.0.asciidoc b/docs/release-notes/highlights-7.5.0.asciidoc deleted file mode 100644 index bfefc6a75bd46..0000000000000 --- a/docs/release-notes/highlights-7.5.0.asciidoc +++ /dev/null @@ -1,121 +0,0 @@ -[[release-highlights-7.5.0]] -== 7.5.0 release highlights -++++ -7.5.0 -++++ - -Each release of {kib} brings new features and product improvements. -Following are the most notable feature and user experience changes in 7.5. -Play around with them locally, or on the -https://www.elastic.co/products/elasticsearch/service[Elasticsearch Service], -and reach out on the https://discuss.elastic.co/c/kibana[Kibana Discuss forum] -if you have any questions or feedback. - -//NOTE: The notable-highlights tagged regions are re-used in the -//Installation and Upgrade Guide - -// tag::notable-highlights[] - -For a complete list of highlights, -see the https://www.elastic.co/blog/kibana-7-5-0-released[Kibana 7.5 release blog]. - -[float] -[[lens-7.5]] -=== Lens—a new way to create visualizations (beta) - -Lens is a simple, more intuitive way to visualize your data. Lens provides a -drag-and-drop experience that helps you get from your data to insights -in just a few clicks. If there is a better visualization for your data, -Lens makes smart visualization suggestions for you, which allows you to -quickly switch between visualization types. Want to use a different data set? -With Lens, you can simply choose a different index pattern right from the UI. -Check out our {kibana-ref}/lens.html[documentation] for full details. - -[role="screenshot"] -image::release-notes/images/7.5-lens.gif[Lens] - -[float] -[[smart-query-cancellation-7.5]] -=== Smart query cancellation - -Starting in 7.5, if you navigate to a different page or -update a query before getting the results, {kib} applies a smart cancellation policy, -canceling the -{es} query. Canceling unnecessary queries improves cluster performance -by reducing noise caused by queries that you no longer need. - -[float] -[[canvas-shareables-7.5]] -=== Canvas workpads outside of Kibana - -Canvas introduces shareables, a new way to externally share information from -{kib}. Shareables enables our community to share static Canvas workpads -using a code snippet. When the workpad is added to your website, -you can customize the workpad behavior to autoplay the pages. - -[role="screenshot"] -image::release-notes/images/7.5-canvas.gif[Canvas Shareables] - -[float] -[[newsfeed-7.5]] -=== A newsfeed to keep you informed - -Keep up with what’s going on at Elastic with our newsfeed in {kib}. -You’ll learn about what’s new, what we’re blogging about, our latest webinars, -and more. - -[role="screenshot"] -image::release-notes/images/7.5-newsfeed.png[Newsfeed] - -[float] -[[maps-7.5]] -=== More Maps enhancements - -GeoJSON upload was released as Beta in 7.3, -and now it's production ready in 7.5. -GeoJSON upload makes it easier to add custom vector shapes to your map. -Check out the {kibana-ref}/indexing-geojson-data-tutorial.html[Indexing GeoJSON data tutorial]. - -In 7.5, Maps also introduces these new features: - -* Styling by date and time field, which is good for tracking time -series data and for highlighting newer data over older records. -* Document sorting to ensure that your most important data is on top of the map. -* A new tooltip design with support for drag-drop field sorting and image embeds. - -[role="screenshot"] -image::release-notes/images/7.5-maps.png[Maps] - -[float] -[[api-keys-7.5]] -=== A new UI for managing API Keys - -Our new API keys UI allows cluster administrators to view and invalidate -all API keys, and users to view and invalidate their own keys. -API keys are is especially useful when working with automated scripts, -or workflow integration with other software. For example, you can use API -keys to automate the ingestion of new data from remote sources, -without live user interaction. See {kibana-ref}/api-keys.html[the documentation] for more details. - -[role="screenshot"] -image::release-notes/images/7.5-api-keys.png[API Keys UI] - -[float] -[[snapshot-retention-7.5]] -=== Automatic deletion of snapshots - -When creating a snapshot lifecycle policy with our -*Snapshot and Restore* UI, you can now optionally configure a retention period. -This feature enables you to automate the deletion of your snapshots, -reducing the need for manual cleanup while allowing for retention of only the -most relevant and recent snapshots. For example, you might define a -policy that deletes snapshots older than thirty days, always keeps -the latest five snapshots, and deletes the oldest snapshot if more than 50 -are present. See {ref}/slm-retention.html[Snapshot lifecycle management retention] -for the full documentation. - -[role="screenshot"] -image::release-notes/images/7.5-snapshot-retention.png[Snapshot Retention UI] - - -// end::notable-highlights[] diff --git a/docs/release-notes/highlights-7.6.0.asciidoc b/docs/release-notes/highlights-7.6.0.asciidoc deleted file mode 100644 index e72d5da33ecda..0000000000000 --- a/docs/release-notes/highlights-7.6.0.asciidoc +++ /dev/null @@ -1,78 +0,0 @@ -[[release-highlights-7.6.0]] -== 7.6.0 release highlights -++++ -7.6.0 -++++ - -Explore the new features in Kibana 7.6. - -//NOTE: The notable-highlights tagged regions are re-used in the -//Installation and Upgrade Guide - -// tag::notable-highlights[] - -[cols="50, 50"] -|=== - -a| *Elastic Maps improvements* - -Color the data points on your map based on the discrete values in a categorical field. -Add text labels to give your viewers more information about the data you’re -overlaying on your maps. - -| image:release-notes/images/7-6-maps-category.png[Categorical styling in Maps] - -a| *Embeddable maps in Canvas* - -Incorporate a geospatial perspective into a Canvas workpad. - -| image:release-notes/images/7-6-canvas-map.png[Embedded maps in Canvas] - - -a| *Lens improvements* - -Use scripted fields in your visualizations, just like any other field in the index. -Rapidly reset a layer -with a single click instead of removing data fields one at a time. - -| image:release-notes/images/7-6-lens-reset-layer.png[Scripted fields in Lens] - -a| *Nested field support* - -Query on nested fields using KQL. -You can query for a user whose first name is Tom and whose -last name is Hanks, or a user whose first name is Tom and last name is Smith. -{kibana-ref}/kuery-query.html[Our documentation] contains examples for you to follow. - -| image:release-notes/images/7-6-nested-field.png[Nested field] - -a| *Machine learning improvements* - -Explore the results of your -{ml-docs}/dfa-classification.html[{classification}] and -{ml-docs}/dfa-regression.html[{regression}] {dfanalytics-jobs} in {kib}. -Use a new wizard to create -{ml-docs}/ml-configuring-categories.html[categorization {anomaly-jobs}]. If you -use APM, there are also new pre-configured {anomaly-jobs}. - -| image:release-notes/images/7.6-classification.png[Classification results] - -|=== - -[float] -=== Learn more - -Get more details on these features in the https://www.elastic.co/blog/kibana-7-6-0-released[{kib} 7.6 release blog]. -For a complete list of enhancements and other changes, check out the -{kibana-ref}/release-notes-7.6.0.html[{kib} 7.6 release notes]. - -// end::notable-highlights[] - -[float] -=== Give 7.6 a try - -Try 7.6 now by deploying {es} and {kib} on -https://www.elastic.co/cloud/elasticsearch-service/signup[Elastic Cloud] or -by https://www.elastic.co/start[downloading them]. -Let us know what you think on Twitter https://twitter.com/elastic[(@elastic)] -or in our https://discuss.elastic.co/c/elasticsearch[forum]. diff --git a/docs/release-notes/highlights-7.7.0.asciidoc b/docs/release-notes/highlights-7.7.0.asciidoc deleted file mode 100644 index b597e01d5c1ab..0000000000000 --- a/docs/release-notes/highlights-7.7.0.asciidoc +++ /dev/null @@ -1,121 +0,0 @@ -[[release-highlights-7.7.0]] -== 7.7.0 release highlights -++++ -7.7.0 -++++ - -Explore the new features in Kibana 7.7. - -//NOTE: The notable-highlights tagged regions are re-used in the -//Installation and Upgrade Guide - -// tag::notable-highlights[] - - -[float] -[[alerting-framework-highlights]] -=== New alerting framework - -beta:[]Alerting allows you to detect complex conditions in -{kib} apps and trigger actions when those conditions are met. -Alerting is integrated with <>, <>, -<>, and <> and is -centrally managed from <>. -Alerting has built-in <> and -<> for you to use. - -[role="screenshot"] -image:user/alerting/images/alerting-overview.png[Alerts and actions UI] - -[float] -[[canvas-visualizations-highlights]] -=== Canvas with your visualizations - -Visualizations that you created in Lens, Visualize, and -TSVB can now be embedded in a Canvas workpad. This makes it easier for you -to brand your Canvas presentation to your executive team and partners. - -[role="screenshot"] -image:release-notes/images/7-7-canvas-and-lens.png[Embedded maps in Canvas] - -[float] -[[lens-filtering-highlights]] -=== Improved filtering of Lens visualizations in dashboards - -Digging into the Lens visualizations on your -dashboard just got faster. You can now filter the data in one -Lens visualization, and immediately apply that filter to all -Lens visualizations on the dashboard. - -[role="screenshot"] -image:release-notes/images/7-7-lens-filter-in-dashboard.png[Filter Lens visualizations in dashboard] - -[float] -[[lens-formatting-highlights]] -=== Number formatting in Lens - -To accurately display the data in Lens visualizations, -try out the new number formats. The new dropdown has options for formatting -numbers as percentages -and bytes. There's also a field for setting how many decimal places to display. - -[role="screenshot"] -image:release-notes/images/7-7-lens-format-values.png[Formatted values in Lens] - -[float] -[[map-points-highlights]] -=== Individual map points on zoom - -*Elastic Maps* improves the way you work with large amounts of coordinate data. -With the new default setting, points are clustered on a map until you zoom into an area with -less than 10,000 discrete points. Then, you'll see -individual points. This feature is useful for map data -points that are in both dense urban areas and rural locations. - -[role="screenshot"] -image:release-notes/images/7-7-maps-zoom.png[Individual map points on zoom] - -[float] -[[map-distance-filter-highlights]] -=== Distance filters in Elastic Maps - -The distance filter is a new way -to filter map data -within a specific radius. Simply select a -central location, and then draw a circle around it with your mouse. -The distance filter is in addition to the tools for filtering data by custom shape and by rectangular bound. - -[role="screenshot"] -image:release-notes/images/7-7-maps-distance-filter.png[Distance filters in Elastic Maps] - -[float] -[[file-uploader-highlights]] -=== {data-viz} now recommends {filebeat} config - -experimental[] When you upload a file to the *{data-viz}*, a {filebeat} -configuration is recommended for you to use. You can then use {filebeat} to add -more data of the same structure to the same index that was created during an -initial upload. - -[role="screenshot"] -image:release-notes/images/7-7-file-uploader-filebeat.png[Recommended Filebeat config in Data Vizualizer] -For more information about *{data-viz}*, refer to <>. - -[float] -=== Learn more - -Get details on these release highlights and more in the -https://www.elastic.co/blog/kibana-7-7-0-released[{kib} 7.7 release blog]. -For a complete list of enhancements and other changes, check out the -{kibana-ref}/release-notes-7.7.0.html[{kib} 7.7 release notes]. - -// end::notable-highlights[] - -[float] -=== Give 7.7 a try - -Try 7.7 now by deploying {es} and {kib} on -https://www.elastic.co/cloud/elasticsearch-service/signup[Elastic Cloud] or -by https://www.elastic.co/start[downloading them]. -Let us know what you think on Twitter https://twitter.com/elastic[(@elastic)] -or in our https://discuss.elastic.co/c/elasticsearch[forum]. diff --git a/docs/release-notes/highlights.asciidoc b/docs/release-notes/highlights.asciidoc deleted file mode 100644 index ba878b73ff03a..0000000000000 --- a/docs/release-notes/highlights.asciidoc +++ /dev/null @@ -1,27 +0,0 @@ -[[release-highlights]] -= Release Highlights - -[partintro] --- -This section summarizes the most important changes in each release. For the -full list, see <> and <>. - -* <> -* <> -* <> -* <> -* <> -* <> -* <> -* <> - --- - -include::highlights-7.7.0.asciidoc[] -include::highlights-7.6.0.asciidoc[] -include::highlights-7.5.0.asciidoc[] -include::highlights-7.4.0.asciidoc[] -include::highlights-7.3.0.asciidoc[] -include::highlights-7.2.0.asciidoc[] -include::highlights-7.1.0.asciidoc[] -include::highlights-7.0.0.asciidoc[] diff --git a/docs/release-notes/images/7-6-canvas-map.png b/docs/release-notes/images/7-6-canvas-map.png deleted file mode 100644 index 1299f99b97978..0000000000000 Binary files a/docs/release-notes/images/7-6-canvas-map.png and /dev/null differ diff --git a/docs/release-notes/images/7-6-lens-reset-layer.png b/docs/release-notes/images/7-6-lens-reset-layer.png deleted file mode 100644 index 62a76875d2f57..0000000000000 Binary files a/docs/release-notes/images/7-6-lens-reset-layer.png and /dev/null differ diff --git a/docs/release-notes/images/7-6-maps-category.png b/docs/release-notes/images/7-6-maps-category.png deleted file mode 100644 index 3ac79e680899a..0000000000000 Binary files a/docs/release-notes/images/7-6-maps-category.png and /dev/null differ diff --git a/docs/release-notes/images/7-6-nested-field.png b/docs/release-notes/images/7-6-nested-field.png deleted file mode 100644 index 347733fb18561..0000000000000 Binary files a/docs/release-notes/images/7-6-nested-field.png and /dev/null differ diff --git a/docs/release-notes/images/7-7-canvas-and-lens.png b/docs/release-notes/images/7-7-canvas-and-lens.png deleted file mode 100644 index b750b44092f77..0000000000000 Binary files a/docs/release-notes/images/7-7-canvas-and-lens.png and /dev/null differ diff --git a/docs/release-notes/images/7-7-file-uploader-filebeat.png b/docs/release-notes/images/7-7-file-uploader-filebeat.png deleted file mode 100644 index 3f760575ec5dc..0000000000000 Binary files a/docs/release-notes/images/7-7-file-uploader-filebeat.png and /dev/null differ diff --git a/docs/release-notes/images/7-7-lens-filter-in-dashboard.png b/docs/release-notes/images/7-7-lens-filter-in-dashboard.png deleted file mode 100644 index 14e5f5aa4b08e..0000000000000 Binary files a/docs/release-notes/images/7-7-lens-filter-in-dashboard.png and /dev/null differ diff --git a/docs/release-notes/images/7-7-lens-format-values.png b/docs/release-notes/images/7-7-lens-format-values.png deleted file mode 100644 index 6572960508cb7..0000000000000 Binary files a/docs/release-notes/images/7-7-lens-format-values.png and /dev/null differ diff --git a/docs/release-notes/images/7-7-maps-distance-filter.png b/docs/release-notes/images/7-7-maps-distance-filter.png deleted file mode 100644 index 56345a56b4c72..0000000000000 Binary files a/docs/release-notes/images/7-7-maps-distance-filter.png and /dev/null differ diff --git a/docs/release-notes/images/7-7-maps-zoom.png b/docs/release-notes/images/7-7-maps-zoom.png deleted file mode 100644 index edd1ae1b6c724..0000000000000 Binary files a/docs/release-notes/images/7-7-maps-zoom.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-canvas-custom-elements.png b/docs/release-notes/images/7.2-canvas-custom-elements.png deleted file mode 100755 index e74198a91c10d..0000000000000 Binary files a/docs/release-notes/images/7.2-canvas-custom-elements.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-canvas-filters.png b/docs/release-notes/images/7.2-canvas-filters.png deleted file mode 100644 index 67392e9d4f1d5..0000000000000 Binary files a/docs/release-notes/images/7.2-canvas-filters.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-data-frames-list-view.png b/docs/release-notes/images/7.2-data-frames-list-view.png deleted file mode 100755 index 13cfe4b94fa00..0000000000000 Binary files a/docs/release-notes/images/7.2-data-frames-list-view.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-data-frames.png b/docs/release-notes/images/7.2-data-frames.png deleted file mode 100755 index 768fe56cfc025..0000000000000 Binary files a/docs/release-notes/images/7.2-data-frames.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-feature-controls.png b/docs/release-notes/images/7.2-feature-controls.png deleted file mode 100755 index cbf65e612c71d..0000000000000 Binary files a/docs/release-notes/images/7.2-feature-controls.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-localization.jpg b/docs/release-notes/images/7.2-localization.jpg deleted file mode 100644 index 950fd70641038..0000000000000 Binary files a/docs/release-notes/images/7.2-localization.jpg and /dev/null differ diff --git a/docs/release-notes/images/7.2-maps.png b/docs/release-notes/images/7.2-maps.png deleted file mode 100755 index b2591afc0d8f8..0000000000000 Binary files a/docs/release-notes/images/7.2-maps.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-metricbeat-ml-modules.png b/docs/release-notes/images/7.2-metricbeat-ml-modules.png deleted file mode 100644 index 8dabc48fba1ef..0000000000000 Binary files a/docs/release-notes/images/7.2-metricbeat-ml-modules.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-monitoring-logs.png b/docs/release-notes/images/7.2-monitoring-logs.png deleted file mode 100644 index 0f20ba5369f66..0000000000000 Binary files a/docs/release-notes/images/7.2-monitoring-logs.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-saved-objects.png b/docs/release-notes/images/7.2-saved-objects.png deleted file mode 100644 index 5717976296d37..0000000000000 Binary files a/docs/release-notes/images/7.2-saved-objects.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-snapshot-repositories.png b/docs/release-notes/images/7.2-snapshot-repositories.png deleted file mode 100755 index c98faad53dc69..0000000000000 Binary files a/docs/release-notes/images/7.2-snapshot-repositories.png and /dev/null differ diff --git a/docs/release-notes/images/7.2-space-privileges.png b/docs/release-notes/images/7.2-space-privileges.png deleted file mode 100755 index 2ecbfed6017c4..0000000000000 Binary files a/docs/release-notes/images/7.2-space-privileges.png and /dev/null differ diff --git a/docs/release-notes/images/7.3-bar-charts.png b/docs/release-notes/images/7.3-bar-charts.png deleted file mode 100755 index 35c2c5bdb8321..0000000000000 Binary files a/docs/release-notes/images/7.3-bar-charts.png and /dev/null differ diff --git a/docs/release-notes/images/7.3-canvas-summary.png b/docs/release-notes/images/7.3-canvas-summary.png deleted file mode 100755 index e3d14381ba19f..0000000000000 Binary files a/docs/release-notes/images/7.3-canvas-summary.png and /dev/null differ diff --git a/docs/release-notes/images/7.3-csv-export.png b/docs/release-notes/images/7.3-csv-export.png deleted file mode 100755 index 577acc4c36d80..0000000000000 Binary files a/docs/release-notes/images/7.3-csv-export.png and /dev/null differ diff --git a/docs/release-notes/images/7.3-kql.png b/docs/release-notes/images/7.3-kql.png deleted file mode 100755 index 447127fe89941..0000000000000 Binary files a/docs/release-notes/images/7.3-kql.png and /dev/null differ diff --git a/docs/release-notes/images/7.3-maps.png b/docs/release-notes/images/7.3-maps.png deleted file mode 100644 index 03d29288a4d8c..0000000000000 Binary files a/docs/release-notes/images/7.3-maps.png and /dev/null differ diff --git a/docs/release-notes/images/7.3-snapshot-restore.png b/docs/release-notes/images/7.3-snapshot-restore.png deleted file mode 100755 index fa48e32d2fef3..0000000000000 Binary files a/docs/release-notes/images/7.3-snapshot-restore.png and /dev/null differ diff --git a/docs/release-notes/images/7.4-outlier-detection.gif b/docs/release-notes/images/7.4-outlier-detection.gif deleted file mode 100755 index 6f8162892e938..0000000000000 Binary files a/docs/release-notes/images/7.4-outlier-detection.gif and /dev/null differ diff --git a/docs/release-notes/images/7.4-saved-queries.gif b/docs/release-notes/images/7.4-saved-queries.gif deleted file mode 100755 index 11e9024da9944..0000000000000 Binary files a/docs/release-notes/images/7.4-saved-queries.gif and /dev/null differ diff --git a/docs/release-notes/images/7.4-siem-maps.png b/docs/release-notes/images/7.4-siem-maps.png deleted file mode 100755 index 3bd145db8b3f9..0000000000000 Binary files a/docs/release-notes/images/7.4-siem-maps.png and /dev/null differ diff --git a/docs/release-notes/images/7.4-snapshot-and-restore.png b/docs/release-notes/images/7.4-snapshot-and-restore.png deleted file mode 100755 index 0bc793700bfee..0000000000000 Binary files a/docs/release-notes/images/7.4-snapshot-and-restore.png and /dev/null differ diff --git a/docs/release-notes/images/7.5-api-keys.png b/docs/release-notes/images/7.5-api-keys.png deleted file mode 100755 index df74f245676d9..0000000000000 Binary files a/docs/release-notes/images/7.5-api-keys.png and /dev/null differ diff --git a/docs/release-notes/images/7.5-canvas.gif b/docs/release-notes/images/7.5-canvas.gif deleted file mode 100755 index d7b69c5dbde28..0000000000000 Binary files a/docs/release-notes/images/7.5-canvas.gif and /dev/null differ diff --git a/docs/release-notes/images/7.5-lens.gif b/docs/release-notes/images/7.5-lens.gif deleted file mode 100755 index 0a02f7e591fed..0000000000000 Binary files a/docs/release-notes/images/7.5-lens.gif and /dev/null differ diff --git a/docs/release-notes/images/7.5-maps.png b/docs/release-notes/images/7.5-maps.png deleted file mode 100755 index fe7e06c3efc35..0000000000000 Binary files a/docs/release-notes/images/7.5-maps.png and /dev/null differ diff --git a/docs/release-notes/images/7.5-newsfeed.png b/docs/release-notes/images/7.5-newsfeed.png deleted file mode 100755 index 8733021c2cd36..0000000000000 Binary files a/docs/release-notes/images/7.5-newsfeed.png and /dev/null differ diff --git a/docs/release-notes/images/7.5-snapshot-retention.png b/docs/release-notes/images/7.5-snapshot-retention.png deleted file mode 100755 index 94335472eac08..0000000000000 Binary files a/docs/release-notes/images/7.5-snapshot-retention.png and /dev/null differ diff --git a/docs/release-notes/images/7.6-classification.png b/docs/release-notes/images/7.6-classification.png deleted file mode 100644 index 1e46eca9f0ff9..0000000000000 Binary files a/docs/release-notes/images/7.6-classification.png and /dev/null differ diff --git a/docs/settings/security-settings.asciidoc b/docs/settings/security-settings.asciidoc index 4eaa4dfa55c4d..058d53118b076 100644 --- a/docs/settings/security-settings.asciidoc +++ b/docs/settings/security-settings.asciidoc @@ -55,6 +55,11 @@ You can configure the following settings in the `kibana.yml` file. this to `true` if SSL is configured outside of {kib} (for example, you are routing requests through a load balancer or proxy). +| `xpack.security.sameSiteCookies` + | Sets the `SameSite` attribute of the session cookie. This allows you to declare whether your cookie should be restricted to a first-party or same-site context. + Valid values are `Strict`, `Lax`, `None`. + This is *not set* by default, which modern browsers will treat as `Lax`. If you use Kibana embedded in an iframe in modern browsers, you might need to set it to `None`. Setting this value to `None` requires cookies to be sent over a secure connection by setting `xpack.security.secureCookies: true`. Some old versions of IE11 do not support `SameSite: None`. + | `xpack.security.session.idleTimeout` | Sets the session duration. By default, sessions stay active until the browser is closed. When this is set to an explicit idle timeout, closing the diff --git a/docs/setup/docker.asciidoc b/docs/setup/docker.asciidoc index 1a0b13edf8086..ab7a85a2ff851 100644 --- a/docs/setup/docker.asciidoc +++ b/docs/setup/docker.asciidoc @@ -82,14 +82,16 @@ services: [[environment-variable-config]] ==== Environment variable configuration -Under Docker, Kibana can be configured via environment variables. When +Under Docker, {kib} can be configured via environment variables. When the container starts, a helper process checks the environment for variables that can be mapped to Kibana command-line arguments. For compatibility with container orchestration systems, these environment variables are written in all capitals, with underscores as word separators. The helper translates these names to valid -Kibana setting names. +{kib} setting names. + +WARNING: All information that you include in environment variables is visible through the `ps` command, including sensitive information. Some example translations are shown here: diff --git a/docs/user/index.asciidoc b/docs/user/index.asciidoc index 750e5aea7d4a8..bc8a503fec424 100644 --- a/docs/user/index.asciidoc +++ b/docs/user/index.asciidoc @@ -1,5 +1,7 @@ include::introduction.asciidoc[] +include::whats-new.asciidoc[] + include::getting-started.asciidoc[] include::setup.asciidoc[] diff --git a/docs/user/whats-new.asciidoc b/docs/user/whats-new.asciidoc new file mode 100644 index 0000000000000..d8becb5c70327 --- /dev/null +++ b/docs/user/whats-new.asciidoc @@ -0,0 +1,27 @@ +[[whats-new]] +== What's new in {minor-version} + +This section summarizes the most important changes in this release. For previous +releases, refer to: + +//* {kibana-ref-all}/7.8/whats-new.html[7.8] +* {kibana-ref-all}/7.7/release-highlights-7.7.0.html[7.7] +* {kibana-ref-all}/7.6/release-highlights-7.6.0.html[7.6] +* {kibana-ref-all}/7.5/release-highlights-7.5.0.html[7.5] +* {kibana-ref-all}/7.4/release-highlights-7.4.0.html[7.4] +* {kibana-ref-all}/7.3/release-highlights-7.3.0.html[7.3] +* {kibana-ref-all}/7.2/release-highlights-7.2.0.html[7.2] +* {kibana-ref-all}/7.1/release-highlights-7.1.0.html[7.1] +* {kibana-ref-all}/7.0/release-highlights-7.0.0.html[7.0] + +For the full list of changes in {minor-version}, see <> and +<>. + +coming[7.9.0] + +//NOTE: The notable-highlights tagged regions are re-used in the +//Installation and Upgrade Guide + +// tag::notable-highlights[] + +// end::notable-highlights[] \ No newline at end of file diff --git a/package.json b/package.json index 46bafdaa36df0..8393e9eac28ba 100644 --- a/package.json +++ b/package.json @@ -122,8 +122,8 @@ "@babel/register": "^7.10.1", "@elastic/apm-rum": "^5.1.1", "@elastic/charts": "19.2.0", - "@elastic/datemath": "5.0.2", - "@elastic/ems-client": "7.8.0", + "@elastic/datemath": "5.0.3", + "@elastic/ems-client": "7.9.3", "@elastic/eui": "24.1.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", @@ -233,6 +233,7 @@ "pug": "^2.0.4", "query-string": "5.1.1", "raw-loader": "3.1.0", + "re2": "1.14.0", "react": "^16.12.0", "react-color": "^2.13.8", "react-dom": "^16.12.0", @@ -501,4 +502,4 @@ "node": "10.21.0", "yarn": "^1.21.1" } -} \ No newline at end of file +} diff --git a/packages/elastic-datemath/.npmignore b/packages/elastic-datemath/.npmignore index 915a694e9066c..a56a2f3ff793e 100644 --- a/packages/elastic-datemath/.npmignore +++ b/packages/elastic-datemath/.npmignore @@ -2,3 +2,5 @@ /test /tsconfig.json /.babelrc +/yarn.lock +/__tests__ diff --git a/packages/elastic-datemath/package.json b/packages/elastic-datemath/package.json index 8096b5275c666..15040a6243ff2 100644 --- a/packages/elastic-datemath/package.json +++ b/packages/elastic-datemath/package.json @@ -1,6 +1,6 @@ { "name": "@elastic/datemath", - "version": "5.0.2", + "version": "5.0.3", "description": "elasticsearch datemath parser, used in kibana", "license": "Apache-2.0", "main": "target/index.js", diff --git a/src/core/public/application/__snapshots__/application_service.test.ts.snap b/src/core/public/application/__snapshots__/application_service.test.ts.snap index c085fb028cd5a..c63a22170c4f6 100644 --- a/src/core/public/application/__snapshots__/application_service.test.ts.snap +++ b/src/core/public/application/__snapshots__/application_service.test.ts.snap @@ -76,6 +76,7 @@ exports[`#start() getComponent returns renderable JSX tree 1`] = ` history={ Object { "push": [MockFunction], + "replace": [MockFunction], } } mounters={Map {}} diff --git a/src/core/public/application/application_service.test.mocks.ts b/src/core/public/application/application_service.test.mocks.ts index a096f05209708..727295237d741 100644 --- a/src/core/public/application/application_service.test.mocks.ts +++ b/src/core/public/application/application_service.test.mocks.ts @@ -29,6 +29,7 @@ jest.doMock('./capabilities', () => ({ export const MockHistory = { push: jest.fn(), + replace: jest.fn(), }; export const createBrowserHistoryMock = jest.fn().mockReturnValue(MockHistory); jest.doMock('history', () => ({ diff --git a/src/core/public/application/application_service.test.ts b/src/core/public/application/application_service.test.ts index 400d1881a5af8..50e47bdf71772 100644 --- a/src/core/public/application/application_service.test.ts +++ b/src/core/public/application/application_service.test.ts @@ -482,9 +482,6 @@ describe('#setup()', () => { describe('#start()', () => { beforeEach(() => { - MockHistory.push.mockReset(); - parseAppUrlMock.mockReset(); - const http = httpServiceMock.createSetupContract({ basePath: '/base-path' }); setupDeps = { http, @@ -497,6 +494,12 @@ describe('#start()', () => { service = new ApplicationService(); }); + afterEach(() => { + MockHistory.push.mockReset(); + MockHistory.replace.mockReset(); + parseAppUrlMock.mockReset(); + }); + it('rejects if called prior to #setup()', async () => { await expect(service.start(startDeps)).rejects.toThrowErrorMatchingInlineSnapshot( `"ApplicationService#setup() must be invoked before start."` @@ -924,6 +927,79 @@ describe('#start()', () => { await navigateToApp('baseApp:legacyApp1'); expect(setupDeps.redirectTo).toHaveBeenCalledWith('/test/app/baseApp'); }); + + describe('when `replace` option is true', () => { + it('use `history.replace` instead of `history.push`', async () => { + service.setup(setupDeps); + + const { navigateToApp } = await service.start(startDeps); + + await navigateToApp('myTestApp', { replace: true }); + expect(MockHistory.replace).toHaveBeenCalledWith('/app/myTestApp', undefined); + + await navigateToApp('myOtherApp', { replace: true }); + expect(MockHistory.replace).toHaveBeenCalledWith('/app/myOtherApp', undefined); + }); + + it('includes state if specified', async () => { + const { register } = service.setup(setupDeps); + + register(Symbol(), createApp({ id: 'app2', appRoute: '/custom/path' })); + + const { navigateToApp } = await service.start(startDeps); + + await navigateToApp('myTestApp', { state: 'my-state', replace: true }); + expect(MockHistory.replace).toHaveBeenCalledWith('/app/myTestApp', 'my-state'); + + await navigateToApp('app2', { state: 'my-state', replace: true }); + expect(MockHistory.replace).toHaveBeenCalledWith('/custom/path', 'my-state'); + }); + it('appends a path if specified', async () => { + const { register } = service.setup(setupDeps); + + register(Symbol(), createApp({ id: 'app2', appRoute: '/custom/path' })); + + const { navigateToApp } = await service.start(startDeps); + + await navigateToApp('myTestApp', { path: 'deep/link/to/location/2', replace: true }); + expect(MockHistory.replace).toHaveBeenCalledWith( + '/app/myTestApp/deep/link/to/location/2', + undefined + ); + + await navigateToApp('app2', { path: 'deep/link/to/location/2', replace: true }); + expect(MockHistory.replace).toHaveBeenCalledWith( + '/custom/path/deep/link/to/location/2', + undefined + ); + }); + it('do not change the behavior when in legacy mode', async () => { + setupDeps.http = httpServiceMock.createSetupContract({ basePath: '/test' }); + setupDeps.injectedMetadata.getLegacyMode.mockReturnValue(true); + service.setup(setupDeps); + + const { navigateToApp } = await service.start(startDeps); + + await navigateToApp('alpha', { replace: true }); + expect(setupDeps.redirectTo).toHaveBeenCalledWith('/test/app/alpha'); + }); + }); + + describe('when `replace` option is false', () => { + it('behave as when the option is unspecified', async () => { + service.setup(setupDeps); + + const { navigateToApp } = await service.start(startDeps); + + await navigateToApp('myTestApp', { replace: false }); + expect(MockHistory.push).toHaveBeenCalledWith('/app/myTestApp', undefined); + + await navigateToApp('myOtherApp', { replace: false }); + expect(MockHistory.push).toHaveBeenCalledWith('/app/myOtherApp', undefined); + + expect(MockHistory.replace).not.toHaveBeenCalled(); + }); + }); }); describe('navigateToUrl', () => { diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx index 850422a61bde7..95361d8287c71 100644 --- a/src/core/public/application/application_service.tsx +++ b/src/core/public/application/application_service.tsx @@ -44,6 +44,7 @@ import { LegacyApp, LegacyAppMounter, Mounter, + NavigateToAppOptions, } from './types'; import { getLeaveAction, isConfirmAction } from './application_leave'; import { appendAppPath, parseAppUrl, relativeToAbsolute, getAppInfo } from './utils'; @@ -105,7 +106,7 @@ export class ApplicationService { private registrationClosed = false; private history?: History; private mountContext?: IContextContainer; - private navigate?: (url: string, state: any) => void; + private navigate?: (url: string, state: unknown, replace: boolean) => void; private redirectTo?: (url: string) => void; public setup({ @@ -125,10 +126,16 @@ export class ApplicationService { this.history = history || createBrowserHistory({ basename }); } - // If we do not have history available, use redirectTo to do a full page refresh. - this.navigate = (url, state) => - // basePath not needed here because `history` is configured with basename - this.history ? this.history.push(url, state) : redirectTo(basePath.prepend(url)); + this.navigate = (url, state, replace) => { + if (this.history) { + // basePath not needed here because `history` is configured with basename + return replace ? this.history.replace(url, state) : this.history.push(url, state); + } else { + // If we do not have history available (legacy mode), use redirectTo to do a full page refresh. + return redirectTo(basePath.prepend(url)); + } + }; + this.redirectTo = redirectTo; this.mountContext = context.createContextContainer(); @@ -278,14 +285,14 @@ export class ApplicationService { const navigateToApp: InternalApplicationStart['navigateToApp'] = async ( appId, - { path, state }: { path?: string; state?: any } = {} + { path, state, replace = false }: NavigateToAppOptions = {} ) => { if (await this.shouldNavigate(overlays)) { if (path === undefined) { path = applications$.value.get(appId)?.defaultPath; } this.appLeaveHandlers.delete(this.currentAppId$.value!); - this.navigate!(getAppUrl(availableMounters, appId, path), state); + this.navigate!(getAppUrl(availableMounters, appId, path), state, replace); this.currentAppId$.next(appId); } }; diff --git a/src/core/public/application/index.ts b/src/core/public/application/index.ts index 74356cbd88b34..121f0c7ac07d6 100644 --- a/src/core/public/application/index.ts +++ b/src/core/public/application/index.ts @@ -40,6 +40,7 @@ export { AppLeaveDefaultAction, AppLeaveConfirmAction, LegacyApp, + NavigateToAppOptions, PublicAppInfo, PublicLegacyAppInfo, // Internal types diff --git a/src/core/public/application/integration_tests/application_service.test.tsx b/src/core/public/application/integration_tests/application_service.test.tsx index 89f90a9899dda..e3cd0761c3244 100644 --- a/src/core/public/application/integration_tests/application_service.test.tsx +++ b/src/core/public/application/integration_tests/application_service.test.tsx @@ -125,6 +125,25 @@ describe('ApplicationService', () => { expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1'); }); + + it('replaces the current history entry when the `replace` option is true', async () => { + const { register } = service.setup(setupDeps); + + register(Symbol(), { + id: 'app1', + title: 'App1', + mount: async ({}: AppMountParameters) => { + return () => undefined; + }, + }); + + const { navigateToApp } = await service.start(startDeps); + + await navigateToApp('app1', { path: '/foo' }); + await navigateToApp('app1', { path: '/bar', replace: true }); + + expect(history.entries.map((entry) => entry.pathname)).toEqual(['/', '/app/app1/bar']); + }); }); }); diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index 7c83c684ac73d..53eae58c996a0 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -661,6 +661,28 @@ export interface InternalApplicationSetup extends Pick; + navigateToApp(appId: string, options?: NavigateToAppOptions): Promise; /** * Navigate to given url, which can either be an absolute url or a relative path, in a SPA friendly way when possible. diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 131e97f58fbd7..b83e8c476ccef 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -126,6 +126,7 @@ export { ScopedHistory, LegacyApp, PublicLegacyAppInfo, + NavigateToAppOptions, } from './application'; export { diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index dd111383e9b07..1914fa7be89ec 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -238,10 +238,7 @@ export interface ApplicationStart { path?: string; absolute?: boolean; }): string; - navigateToApp(appId: string, options?: { - path?: string; - state?: any; - }): Promise; + navigateToApp(appId: string, options?: NavigateToAppOptions): Promise; navigateToUrl(url: string): Promise; // @deprecated registerMountContext(contextName: T, provider: IContextProvider): void; @@ -1035,6 +1032,15 @@ export function modifyUrl(url: string, urlModifier: (urlParts: URLMeaningfulPart // @public export type MountPoint = (element: T) => UnmountCallback; +// Warning: (ae-missing-release-tag) "NavigateToAppOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface NavigateToAppOptions { + path?: string; + replace?: boolean; + state?: unknown; +} + // Warning: (ae-missing-release-tag) "NavType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/src/core/server/http/cookie_session_storage.test.ts b/src/core/server/http/cookie_session_storage.test.ts index a5612675c37de..3afe5e0c4dfc7 100644 --- a/src/core/server/http/cookie_session_storage.test.ts +++ b/src/core/server/http/cookie_session_storage.test.ts @@ -424,4 +424,60 @@ describe('Cookie based SessionStorage', () => { ]); }); }); + + describe('#options', () => { + describe('#SameSite', () => { + it('throws an exception if "SameSite: None" set on not Secure connection', async () => { + const { server: innerServer } = await server.setup(setupDeps); + + expect( + createCookieSessionStorageFactory(logger.get(), innerServer, { + ...cookieOptions, + sameSite: 'None', + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"\\"SameSite: None\\" requires Secure connection"` + ); + }); + + for (const sameSite of ['Strict', 'Lax', 'None'] as const) { + it(`sets and parses SameSite = ${sameSite} correctly`, async () => { + const { server: innerServer, createRouter } = await server.setup(setupDeps); + const router = createRouter(''); + + router.get({ path: '/', validate: false }, async (context, req, res) => { + const sessionStorage = factory.asScoped(req); + const sessionValue = await sessionStorage.get(); + if (!sessionValue) { + sessionStorage.set(sessVal()); + return res.ok(); + } + return res.ok({ body: { value: sessionValue.value } }); + }); + + const factory = await createCookieSessionStorageFactory(logger.get(), innerServer, { + ...cookieOptions, + isSecure: true, + name: `sid-${sameSite}`, + sameSite, + }); + await server.start(); + + const response = await supertest(innerServer.listener).get('/').expect(200); + + const cookies = response.get('set-cookie'); + expect(cookies).toBeDefined(); + expect(cookies).toHaveLength(1); + + const sessionCookie = retrieveSessionCookie(cookies[0]); + expect(sessionCookie.extensions).toContain(`SameSite=${sameSite}`); + + await supertest(innerServer.listener) + .get('/') + .set('Cookie', `${sessionCookie.key}=${sessionCookie.value}`) + .expect(200, { value: userData }); + }); + } + }); + }); }); diff --git a/src/core/server/http/cookie_session_storage.ts b/src/core/server/http/cookie_session_storage.ts index 25b463140bfbc..13f498233f695 100644 --- a/src/core/server/http/cookie_session_storage.ts +++ b/src/core/server/http/cookie_session_storage.ts @@ -19,6 +19,8 @@ import { Request, Server } from 'hapi'; import hapiAuthCookie from 'hapi-auth-cookie'; +// @ts-ignore no TS definitions +import Statehood from 'statehood'; import { KibanaRequest, ensureRawRequest } from './router'; import { SessionStorageFactory, SessionStorage } from './session_storage'; @@ -45,6 +47,11 @@ export interface SessionStorageCookieOptions { * Flag indicating whether the cookie should be sent only via a secure connection. */ isSecure: boolean; + /** + * Defines SameSite attribute of the Set-Cookie Header. + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + */ + sameSite?: 'Strict' | 'Lax' | 'None'; } /** @@ -100,6 +107,12 @@ class ScopedCookieSessionStorage> implements Sessi } } +function validateOptions(options: SessionStorageCookieOptions) { + if (options.sameSite === 'None' && options.isSecure !== true) { + throw new Error('"SameSite: None" requires Secure connection'); + } +} + /** * Creates SessionStorage factory, which abstract the way of * session storage implementation and scoping to the incoming requests. @@ -113,10 +126,12 @@ export async function createCookieSessionStorageFactory( cookieOptions: SessionStorageCookieOptions, basePath?: string ): Promise> { + validateOptions(cookieOptions); + function clearInvalidCookie(req: Request | undefined, path: string = basePath || '/') { // if the cookie did not include the 'path' attribute in the session value, it is a legacy cookie // we will assume that the cookie was created with the current configuration - log.debug(`Clearing invalid session cookie`); + log.debug('Clearing invalid session cookie'); // need to use Hapi toolkit to clear cookie with defined options if (req) { (req.cookieAuth as any).h.unstate(cookieOptions.name, { path }); @@ -139,9 +154,26 @@ export async function createCookieSessionStorageFactory( path: basePath, clearInvalid: false, isHttpOnly: true, - isSameSite: false, + isSameSite: cookieOptions.sameSite === 'None' ? false : cookieOptions.sameSite ?? false, }); + // A hack to support SameSite: 'None'. + // Remove it after update Hapi to v19 that supports SameSite: 'None' out of the box. + if (cookieOptions.sameSite === 'None') { + log.debug('Patching Statehood.prepareValue'); + const originalPrepareValue = Statehood.prepareValue; + Statehood.prepareValue = function kibanaStatehoodPrepareValueWrapper( + name: string, + value: unknown, + options: any + ) { + if (name === cookieOptions.name) { + options.isSameSite = cookieOptions.sameSite; + } + return originalPrepareValue(name, value, options); + }; + } + return { asScoped(request: KibanaRequest) { return new ScopedCookieSessionStorage(log, server, ensureRawRequest(request)); diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 0d21586798be0..78cc02d39e6c4 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -2429,6 +2429,7 @@ export interface SessionStorageCookieOptions { encryptionKey: string; isSecure: boolean; name: string; + sameSite?: 'Strict' | 'Lax' | 'None'; validate: (sessionValue: T | T[]) => SessionCookieValidationResult; } diff --git a/src/dev/build/build_distributables.js b/src/dev/build/build_distributables.js index 66f0c0355c2d9..2ea71fa2c1d33 100644 --- a/src/dev/build/build_distributables.js +++ b/src/dev/build/build_distributables.js @@ -47,6 +47,7 @@ import { InstallDependenciesTask, BuildKibanaPlatformPluginsTask, OptimizeBuildTask, + PatchNativeModulesTask, RemovePackageJsonDepsTask, RemoveWorkspacesTask, TranspileBabelTask, @@ -136,6 +137,7 @@ export async function buildDistributables(options) { * directories and perform platform-specific steps */ await run(CreateArchivesSourcesTask); + await run(PatchNativeModulesTask); await run(CleanExtraBinScriptsTask); await run(CleanExtraBrowsersTask); await run(CleanNodeBuildsTask); diff --git a/src/dev/build/tasks/nodejs/__tests__/download.js b/src/dev/build/lib/__tests__/download.js similarity index 100% rename from src/dev/build/tasks/nodejs/__tests__/download.js rename to src/dev/build/lib/__tests__/download.js diff --git a/src/dev/build/lib/__tests__/fixtures/foo.txt.gz b/src/dev/build/lib/__tests__/fixtures/foo.txt.gz new file mode 100644 index 0000000000000..46fef5a6af78c Binary files /dev/null and b/src/dev/build/lib/__tests__/fixtures/foo.txt.gz differ diff --git a/src/dev/build/lib/__tests__/fs.js b/src/dev/build/lib/__tests__/fs.js index 0b2db4c538fb8..bf7596b012f79 100644 --- a/src/dev/build/lib/__tests__/fs.js +++ b/src/dev/build/lib/__tests__/fs.js @@ -23,11 +23,12 @@ import { chmodSync, statSync } from 'fs'; import del from 'del'; import expect from '@kbn/expect'; -import { mkdirp, write, read, getChildPaths, copyAll, getFileHash, untar } from '../fs'; +import { mkdirp, write, read, getChildPaths, copyAll, getFileHash, untar, gunzip } from '../fs'; const TMP = resolve(__dirname, '__tmp__'); const FIXTURES = resolve(__dirname, 'fixtures'); const FOO_TAR_PATH = resolve(FIXTURES, 'foo_dir.tar.gz'); +const FOO_GZIP_PATH = resolve(FIXTURES, 'foo.txt.gz'); const BAR_TXT_PATH = resolve(FIXTURES, 'foo_dir/bar.txt'); const WORLD_EXECUTABLE = resolve(FIXTURES, 'bin/world_executable'); @@ -323,4 +324,39 @@ describe('dev/build/lib/fs', () => { expect(await read(resolve(destination, 'foo/foo.txt'))).to.be('foo\n'); }); }); + + describe('gunzip()', () => { + it('rejects if source path is not absolute', async () => { + try { + await gunzip('foo/bar', '**/*', __dirname); + throw new Error('Expected gunzip() to reject'); + } catch (error) { + assertNonAbsoluteError(error); + } + }); + + it('rejects if destination path is not absolute', async () => { + try { + await gunzip(__dirname, '**/*', 'foo/bar'); + throw new Error('Expected gunzip() to reject'); + } catch (error) { + assertNonAbsoluteError(error); + } + }); + + it('rejects if neither path is not absolute', async () => { + try { + await gunzip('foo/bar', '**/*', 'foo/bar'); + throw new Error('Expected gunzip() to reject'); + } catch (error) { + assertNonAbsoluteError(error); + } + }); + + it('extracts gzip from source into destination, creating destination if necessary', async () => { + const destination = resolve(TMP, 'z/y/x/v/u/t/foo.txt'); + await gunzip(FOO_GZIP_PATH, destination); + expect(await read(resolve(destination))).to.be('foo\n'); + }); + }); }); diff --git a/src/dev/build/tasks/nodejs/download.js b/src/dev/build/lib/download.js similarity index 98% rename from src/dev/build/tasks/nodejs/download.js rename to src/dev/build/lib/download.js index fb3294e2d1221..fbd2d47ff7b06 100644 --- a/src/dev/build/tasks/nodejs/download.js +++ b/src/dev/build/lib/download.js @@ -24,7 +24,7 @@ import chalk from 'chalk'; import { createHash } from 'crypto'; import Axios from 'axios'; -import { mkdirp } from '../../lib'; +import { mkdirp } from './fs'; function tryUnlink(path) { try { diff --git a/src/dev/build/lib/fs.js b/src/dev/build/lib/fs.js index 864a07e837c3f..b905f40d0de1e 100644 --- a/src/dev/build/lib/fs.js +++ b/src/dev/build/lib/fs.js @@ -195,6 +195,19 @@ export async function untar(source, destination, extractOptions = {}) { ]); } +export async function gunzip(source, destination) { + assertAbsolute(source); + assertAbsolute(destination); + + await mkdirAsync(dirname(destination), { recursive: true }); + + await createPromiseFromStreams([ + fs.createReadStream(source), + createGunzip(), + fs.createWriteStream(destination), + ]); +} + export async function compress(type, options = {}, source, destination) { const output = fs.createWriteStream(destination); const archive = archiver(type, options.archiverOptions); diff --git a/src/dev/build/lib/index.js b/src/dev/build/lib/index.js index afebd090d797d..6540db6f37a72 100644 --- a/src/dev/build/lib/index.js +++ b/src/dev/build/lib/index.js @@ -28,10 +28,12 @@ export { copyAll, getFileHash, untar, + gunzip, deleteAll, deleteEmptyFolders, compress, isFileAccessible, } from './fs'; +export { download } from './download'; export { scanDelete } from './scan_delete'; export { scanCopy } from './scan_copy'; diff --git a/src/dev/build/tasks/index.js b/src/dev/build/tasks/index.js index bafb5a2fe115e..be675b4aa6ca4 100644 --- a/src/dev/build/tasks/index.js +++ b/src/dev/build/tasks/index.js @@ -33,6 +33,7 @@ export * from './nodejs_modules'; export * from './notice_file_task'; export * from './optimize_task'; export * from './os_packages'; +export * from './patch_native_modules_task'; export * from './transpile_babel_task'; export * from './transpile_scss_task'; export * from './verify_env_task'; diff --git a/src/dev/build/tasks/nodejs/__tests__/download_node_builds_task.js b/src/dev/build/tasks/nodejs/__tests__/download_node_builds_task.js index 9357735e3f5a3..c1764d06b43b3 100644 --- a/src/dev/build/tasks/nodejs/__tests__/download_node_builds_task.js +++ b/src/dev/build/tasks/nodejs/__tests__/download_node_builds_task.js @@ -22,7 +22,7 @@ import expect from '@kbn/expect'; import * as NodeShasumsNS from '../node_shasums'; import * as NodeDownloadInfoNS from '../node_download_info'; -import * as DownloadNS from '../download'; +import * as DownloadNS from '../../../lib/download'; // sinon can't stub '../../../lib' properly import { DownloadNodeBuildsTask } from '../download_node_builds_task'; describe('src/dev/build/tasks/nodejs/download_node_builds_task', () => { diff --git a/src/dev/build/tasks/nodejs/download_node_builds_task.js b/src/dev/build/tasks/nodejs/download_node_builds_task.js index 86ddb0506f972..c0907e6c42a97 100644 --- a/src/dev/build/tasks/nodejs/download_node_builds_task.js +++ b/src/dev/build/tasks/nodejs/download_node_builds_task.js @@ -17,7 +17,7 @@ * under the License. */ -import { download } from './download'; +import { download } from '../../lib'; import { getNodeShasums } from './node_shasums'; import { getNodeDownloadInfo } from './node_download_info'; diff --git a/src/dev/build/tasks/patch_native_modules_task.js b/src/dev/build/tasks/patch_native_modules_task.js new file mode 100644 index 0000000000000..fba33442fad10 --- /dev/null +++ b/src/dev/build/tasks/patch_native_modules_task.js @@ -0,0 +1,103 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import fs from 'fs'; +import path from 'path'; +import util from 'util'; +import { deleteAll, download, gunzip, untar } from '../lib'; + +const DOWNLOAD_DIRECTORY = '.native_modules'; + +const packages = [ + { + name: 're2', + version: '1.14.0', + destinationPath: 'node_modules/re2/build/Release/re2.node', + extractMethod: 'gunzip', + archives: { + darwin: { + url: 'https://github.com/uhop/node-re2/releases/download/1.14.0/darwin-x64-64.gz', + sha256: '54c8386cb7cd53895cf379522114bfe82378e300e127e58d392ddd40a77e396f', + }, + linux: { + url: 'https://github.com/uhop/node-re2/releases/download/1.14.0/linux-x64-64.gz', + sha256: 'f54f059035e71a7ccb3fa201080e260c41d228d13a8247974b4bb157691b6757', + }, + windows: { + url: 'https://github.com/uhop/node-re2/releases/download/1.14.0/win32-x64-64.gz', + sha256: 'de708446a8b802f4634c2cfef097c2625a2811fdcd8133dfd7b7c485f966caa9', + }, + }, + }, +]; + +async function getInstalledVersion(config, packageName) { + const packageJSONPath = config.resolveFromRepo( + path.join('node_modules', packageName, 'package.json') + ); + const buffer = await util.promisify(fs.readFile)(packageJSONPath); + const packageJSON = JSON.parse(buffer); + return packageJSON.version; +} + +async function patchModule(config, log, build, platform, pkg) { + const installedVersion = await getInstalledVersion(config, pkg.name); + if (installedVersion !== pkg.version) { + throw new Error( + `Can't patch ${pkg.name}'s native module, we were expecting version ${pkg.version} and found ${installedVersion}` + ); + } + const platformName = platform.getName(); + const archive = pkg.archives[platformName]; + const archiveName = path.basename(archive.url); + const downloadPath = config.resolveFromRepo(DOWNLOAD_DIRECTORY, pkg.name, archiveName); + const extractPath = build.resolvePathForPlatform(platform, pkg.destinationPath); + log.debug(`Patching ${pkg.name} binaries from ${archive.url} to ${extractPath}`); + + await deleteAll([extractPath], log); + await download({ + log, + url: archive.url, + destination: downloadPath, + sha256: archive.sha256, + retries: 3, + }); + switch (pkg.extractMethod) { + case 'gunzip': + await gunzip(downloadPath, extractPath); + break; + case 'untar': + await untar(downloadPath, extractPath); + break; + default: + throw new Error(`Extract method of ${pkg.extractMethod} is not supported`); + } +} + +export const PatchNativeModulesTask = { + description: 'Patching platform-specific native modules', + async run(config, log, build) { + for (const pkg of packages) { + await Promise.all( + config.getTargetPlatforms().map(async (platform) => { + await patchModule(config, log, build, platform, pkg); + }) + ); + } + }, +}; diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 1fbcc6e5f2a6a..ded7606050ba9 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -77,7 +77,7 @@ export default { ], coverageDirectory: '/target/kibana-coverage/jest', coverageReporters: ['html', 'text'], - moduleFileExtensions: ['js', 'json', 'ts', 'tsx'], + moduleFileExtensions: ['js', 'json', 'ts', 'tsx', 'node'], modulePathIgnorePatterns: ['__fixtures__/', 'target/'], testMatch: ['**/*.test.{js,ts,tsx}'], testPathIgnorePatterns: [ diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index c343a46be0c45..2704122d9bead 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -528,7 +528,7 @@ export type ExistsFilter = Filter & { // Warning: (ae-forgotten-export) The symbol "ShorthandFieldMapObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export const expandShorthand: (sh: Record) => Record; +export const expandShorthand: (sh: Record) => MappingObject; // Warning: (ae-forgotten-export) The symbol "SavedObjectReference" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "extractReferences" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/vis_type_timelion/server/series_functions/label.js b/src/plugins/vis_type_timelion/server/series_functions/label.js index 4d9a26c46aaaf..b5282967a62e0 100644 --- a/src/plugins/vis_type_timelion/server/series_functions/label.js +++ b/src/plugins/vis_type_timelion/server/series_functions/label.js @@ -51,7 +51,10 @@ export default new Chainable('label', { const config = args.byName; return alter(args, function (eachSeries) { if (config.regex) { - eachSeries.label = eachSeries.label.replace(new RegExp(config.regex), config.label); + // not using a standard `import` so that if there's an issue with the re2 native module + // that it doesn't prevent Kibana from starting up and we only have an issue using Timelion labels + const RE2 = require('re2'); + eachSeries.label = eachSeries.label.replace(new RE2(config.regex), config.label); } else { eachSeries.label = config.label; } diff --git a/src/plugins/vis_type_vega/public/_vega_vis.scss b/src/plugins/vis_type_vega/public/_vega_vis.scss index 159d0dff50ab4..4fc6fbc326ec1 100644 --- a/src/plugins/vis_type_vega/public/_vega_vis.scss +++ b/src/plugins/vis_type_vega/public/_vega_vis.scss @@ -14,7 +14,6 @@ max-height: 100%; width: 100%; height: 100%; - overflow: hidden; // BUG #23514: Make sure Vega doesn't display the controls in two places .vega-bindings { diff --git a/test/functional/apps/context/_discover_navigation.js b/test/functional/apps/context/_discover_navigation.js index 7c1494fb41ef1..6d7f7165e1b38 100644 --- a/test/functional/apps/context/_discover_navigation.js +++ b/test/functional/apps/context/_discover_navigation.js @@ -31,52 +31,54 @@ export default function ({ getService, getPageObjects }) { const filterBar = getService('filterBar'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); - // FLAKY: https://github.com/elastic/kibana/issues/53308 - describe.skip('context link in discover', function contextSize() { - before(async function () { + describe('context link in discover', () => { + before(async () => { + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await Promise.all( - TEST_COLUMN_NAMES.map((columnName) => - PageObjects.discover.clickFieldListItemAdd(columnName) - ) - ); + + for (const columnName of TEST_COLUMN_NAMES) { + await PageObjects.discover.clickFieldListItemAdd(columnName); + } + for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) { await PageObjects.discover.clickFieldListItem(columnName); await PageObjects.discover.clickFieldListPlusFilter(columnName, value); } }); + after(async () => { + await PageObjects.timePicker.resetDefaultAbsoluteRangeViaUiSettings(); + }); - it('should open the context view with the selected document as anchor', async function () { - // get the timestamp of the first row - const firstTimestamp = (await docTable.getFields())[0][0]; - - // navigate to the context view - await docTable.clickRowToggle({ rowIndex: 0 }); - await (await docTable.getRowActions({ rowIndex: 0 }))[0].click(); - + it('should open the context view with the selected document as anchor', async () => { // check the anchor timestamp in the context view - await retry.try(async () => { - const anchorTimestamp = (await docTable.getFields({ isAnchorRow: true }))[0][0]; - expect(anchorTimestamp).to.equal(firstTimestamp); + await retry.waitFor('selected document timestamp matches anchor timestamp ', async () => { + // get the timestamp of the first row + const discoverFields = await docTable.getFields(); + const firstTimestamp = discoverFields[0][0]; + + // navigate to the context view + await docTable.clickRowToggle({ rowIndex: 0 }); + const rowActions = await docTable.getRowActions({ rowIndex: 0 }); + await rowActions[0].click(); + const contextFields = await docTable.getFields({ isAnchorRow: true }); + const anchorTimestamp = contextFields[0][0]; + return anchorTimestamp === firstTimestamp; }); }); - it('should open the context view with the same columns', async function () { + it('should open the context view with the same columns', async () => { const columnNames = await docTable.getHeaderFields(); expect(columnNames).to.eql(['Time', ...TEST_COLUMN_NAMES]); }); - it('should open the context view with the filters disabled', async function () { - const hasDisabledFilters = ( - await Promise.all( - TEST_FILTER_COLUMN_NAMES.map(([columnName, value]) => - filterBar.hasFilter(columnName, value, false) - ) - ) - ).reduce((result, hasDisabledFilter) => result && hasDisabledFilter, true); - - expect(hasDisabledFilters).to.be(true); + it('should open the context view with the filters disabled', async () => { + let disabledFilterCounter = 0; + for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) { + if (await filterBar.hasFilter(columnName, value, false)) { + disabledFilterCounter++; + } + } + expect(disabledFilterCounter).to.be(TEST_FILTER_COLUMN_NAMES.length); }); }); } diff --git a/test/functional/page_objects/time_picker.ts b/test/functional/page_objects/time_picker.ts index c755a6655e71f..b7c1599fd48c5 100644 --- a/test/functional/page_objects/time_picker.ts +++ b/test/functional/page_objects/time_picker.ts @@ -28,6 +28,7 @@ export function TimePickerProvider({ getService, getPageObjects }: FtrProviderCo const browser = getService('browser'); const testSubjects = getService('testSubjects'); const { header, common } = getPageObjects(['header', 'common']); + const kibanaServer = getService('kibanaServer'); type CommonlyUsed = | 'Today' @@ -44,11 +45,26 @@ export function TimePickerProvider({ getService, getPageObjects }: FtrProviderCo class TimePicker { defaultStartTime = 'Sep 19, 2015 @ 06:31:44.000'; defaultEndTime = 'Sep 23, 2015 @ 18:31:44.000'; + defaultStartTimeUTC = '2015-09-18T06:31:44.000Z'; + defaultEndTimeUTC = '2015-09-23T18:31:44.000Z'; async setDefaultAbsoluteRange() { await this.setAbsoluteRange(this.defaultStartTime, this.defaultEndTime); } + /** + * the provides a quicker way to set the timepicker to the default range, saves a few seconds + */ + async setDefaultAbsoluteRangeViaUiSettings() { + await kibanaServer.uiSettings.update({ + 'timepicker:timeDefaults': `{ "from": "${this.defaultStartTimeUTC}", "to": "${this.defaultEndTimeUTC}"}`, + }); + } + + async resetDefaultAbsoluteRangeViaUiSettings() { + await kibanaServer.uiSettings.replace({}); + } + private async getTimePickerPanel() { return await find.byCssSelector('div.euiPopover__panel-isOpen'); } diff --git a/x-pack/dev-tools/jest/create_jest_config.js b/x-pack/dev-tools/jest/create_jest_config.js index c360711e37e27..52a6c83124f36 100644 --- a/x-pack/dev-tools/jest/create_jest_config.js +++ b/x-pack/dev-tools/jest/create_jest_config.js @@ -9,7 +9,7 @@ export function createJestConfig({ kibanaDirectory, rootDir, xPackKibanaDirector return { rootDir, roots: ['/plugins', '/legacy/plugins', '/legacy/server'], - moduleFileExtensions: ['js', 'json', 'ts', 'tsx'], + moduleFileExtensions: ['js', 'json', 'ts', 'tsx', 'node'], moduleNameMapper: { '@elastic/eui$': `${kibanaDirectory}/node_modules/@elastic/eui/test-env`, '@elastic/eui/lib/(.*)?': `${kibanaDirectory}/node_modules/@elastic/eui/test-env/$1`, diff --git a/x-pack/package.json b/x-pack/package.json index 5b10c5b256969..97a50e7a729a8 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -190,8 +190,8 @@ "@babel/register": "^7.10.1", "@babel/runtime": "^7.10.2", "@elastic/apm-rum-react": "^1.1.1", - "@elastic/datemath": "5.0.2", - "@elastic/ems-client": "7.8.0", + "@elastic/datemath": "5.0.3", + "@elastic/ems-client": "7.9.3", "@elastic/eui": "24.1.0", "@elastic/filesaver": "1.1.2", "@elastic/maki": "6.3.0", @@ -381,4 +381,4 @@ "cypress-multi-reporters" ] } -} \ No newline at end of file +} diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx deleted file mode 100644 index 30031a05304bb..0000000000000 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiCard, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { storiesOf } from '@storybook/react'; -import cytoscape from 'cytoscape'; -import React from 'react'; -import { Cytoscape } from './Cytoscape'; -import serviceMapResponse from './cytoscape-layout-test-response.json'; -import { iconForNode } from './icons'; - -const elementsFromResponses = serviceMapResponse.elements; - -storiesOf('app/ServiceMap/Cytoscape', module).add( - 'example', - () => { - const elements: cytoscape.ElementDefinition[] = [ - { - data: { - id: 'opbeans-python', - 'service.name': 'opbeans-python', - 'agent.name': 'python', - }, - }, - { - data: { - id: 'opbeans-node', - 'service.name': 'opbeans-node', - 'agent.name': 'nodejs', - }, - }, - { - data: { - id: 'opbeans-ruby', - 'service.name': 'opbeans-ruby', - 'agent.name': 'ruby', - }, - }, - { data: { source: 'opbeans-python', target: 'opbeans-node' } }, - { - data: { - bidirectional: true, - source: 'opbeans-python', - target: 'opbeans-ruby', - }, - }, - ]; - const height = 300; - const width = 1340; - const serviceName = 'opbeans-python'; - return ( - - ); - }, - { - info: { - propTables: false, - source: false, - }, - } -); - -storiesOf('app/ServiceMap/Cytoscape', module) - .add( - 'node icons', - () => { - const cy = cytoscape(); - const elements = [ - { data: { id: 'default' } }, - { - data: { - id: 'aws', - 'span.type': 'aws', - 'span.subtype': 'servicename', - }, - }, - { data: { id: 'cache', 'span.type': 'cache' } }, - { data: { id: 'database', 'span.type': 'db' } }, - { - data: { - id: 'cassandra', - 'span.type': 'db', - 'span.subtype': 'cassandra', - }, - }, - { - data: { - id: 'elasticsearch', - 'span.type': 'db', - 'span.subtype': 'elasticsearch', - }, - }, - { - data: { - id: 'mongodb', - 'span.type': 'db', - 'span.subtype': 'mongodb', - }, - }, - { - data: { - id: 'mysql', - 'span.type': 'db', - 'span.subtype': 'mysql', - }, - }, - { - data: { - id: 'postgresql', - 'span.type': 'db', - 'span.subtype': 'postgresql', - }, - }, - { - data: { - id: 'redis', - 'span.type': 'db', - 'span.subtype': 'redis', - }, - }, - { data: { id: 'external', 'span.type': 'external' } }, - { data: { id: 'ext', 'span.type': 'ext' } }, - { - data: { - id: 'graphql', - 'span.type': 'external', - 'span.subtype': 'graphql', - }, - }, - { - data: { - id: 'grpc', - 'span.type': 'external', - 'span.subtype': 'grpc', - }, - }, - { - data: { - id: 'websocket', - 'span.type': 'external', - 'span.subtype': 'websocket', - }, - }, - { data: { id: 'messaging', 'span.type': 'messaging' } }, - { - data: { - id: 'jms', - 'span.type': 'messaging', - 'span.subtype': 'jms', - }, - }, - { - data: { - id: 'kafka', - 'span.type': 'messaging', - 'span.subtype': 'kafka', - }, - }, - { data: { id: 'template', 'span.type': 'template' } }, - { - data: { - id: 'handlebars', - 'span.type': 'template', - 'span.subtype': 'handlebars', - }, - }, - { - data: { - id: 'dark', - 'service.name': 'dark service', - 'agent.name': 'dark', - }, - }, - { - data: { - id: 'dotnet', - 'service.name': 'dotnet service', - 'agent.name': 'dotnet', - }, - }, - { - data: { - id: 'dotNet', - 'service.name': 'dotNet service', - 'agent.name': 'dotNet', - }, - }, - { - data: { - id: 'go', - 'service.name': 'go service', - 'agent.name': 'go', - }, - }, - { - data: { - id: 'java', - 'service.name': 'java service', - 'agent.name': 'java', - }, - }, - { - data: { - id: 'RUM (js-base)', - 'service.name': 'RUM service', - 'agent.name': 'js-base', - }, - }, - { - data: { - id: 'RUM (rum-js)', - 'service.name': 'RUM service', - 'agent.name': 'rum-js', - }, - }, - { - data: { - id: 'nodejs', - 'service.name': 'nodejs service', - 'agent.name': 'nodejs', - }, - }, - { - data: { - id: 'php', - 'service.name': 'php service', - 'agent.name': 'php', - }, - }, - { - data: { - id: 'python', - 'service.name': 'python service', - 'agent.name': 'python', - }, - }, - { - data: { - id: 'ruby', - 'service.name': 'ruby service', - 'agent.name': 'ruby', - }, - }, - ]; - cy.add(elements); - - return ( - - {cy.nodes().map((node) => ( - - - agent.name: {node.data('agent.name') || 'undefined'} -
- span.type: {node.data('span.type') || 'undefined'} -
- span.subtype: {node.data('span.subtype') || 'undefined'} - - } - icon={ - {node.data('label')} - } - title={node.data('id')} - /> -
- ))} -
- ); - }, - { - info: { - propTables: false, - source: false, - }, - } - ) - .add( - 'layout', - () => { - const height = 640; - const width = 1340; - const serviceName = undefined; // global service map - - return ( - - ); - }, - { - info: { - source: false, - }, - } - ) - .addParameters({ options: { showPanel: false } }); - -storiesOf('app/ServiceMap/Cytoscape', module).add( - 'node severity', - () => { - const elements = [ - { data: { id: 'undefined', 'service.name': 'severity: undefined' } }, - { - data: { - id: 'warning', - 'service.name': 'severity: warning', - severity: 'warning', - }, - }, - { - data: { - id: 'minor', - 'service.name': 'severity: minor', - severity: 'minor', - }, - }, - { - data: { - id: 'major', - 'service.name': 'severity: major', - severity: 'major', - }, - }, - { - data: { - id: 'critical', - 'service.name': 'severity: critical', - severity: 'critical', - }, - }, - ]; - return ; - }, - { - info: { propTables: false, source: false }, - } -); diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx new file mode 100644 index 0000000000000..28cb7a6f9d291 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx @@ -0,0 +1,327 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiCard, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { storiesOf } from '@storybook/react'; +import cytoscape from 'cytoscape'; +import React from 'react'; +import { Cytoscape } from '../Cytoscape'; +import { iconForNode } from '../icons'; + +storiesOf('app/ServiceMap/Cytoscape', module).add( + 'example', + () => { + const elements: cytoscape.ElementDefinition[] = [ + { + data: { + id: 'opbeans-python', + 'service.name': 'opbeans-python', + 'agent.name': 'python', + }, + }, + { + data: { + id: 'opbeans-node', + 'service.name': 'opbeans-node', + 'agent.name': 'nodejs', + }, + }, + { + data: { + id: 'opbeans-ruby', + 'service.name': 'opbeans-ruby', + 'agent.name': 'ruby', + }, + }, + { data: { source: 'opbeans-python', target: 'opbeans-node' } }, + { + data: { + bidirectional: true, + source: 'opbeans-python', + target: 'opbeans-ruby', + }, + }, + ]; + const height = 300; + const width = 1340; + const serviceName = 'opbeans-python'; + return ( + + ); + }, + { + info: { + propTables: false, + source: false, + }, + } +); + +storiesOf('app/ServiceMap/Cytoscape', module).add( + 'node icons', + () => { + const cy = cytoscape(); + const elements = [ + { data: { id: 'default' } }, + { + data: { + id: 'aws', + 'span.type': 'aws', + 'span.subtype': 'servicename', + }, + }, + { data: { id: 'cache', 'span.type': 'cache' } }, + { data: { id: 'database', 'span.type': 'db' } }, + { + data: { + id: 'cassandra', + 'span.type': 'db', + 'span.subtype': 'cassandra', + }, + }, + { + data: { + id: 'elasticsearch', + 'span.type': 'db', + 'span.subtype': 'elasticsearch', + }, + }, + { + data: { + id: 'mongodb', + 'span.type': 'db', + 'span.subtype': 'mongodb', + }, + }, + { + data: { + id: 'mysql', + 'span.type': 'db', + 'span.subtype': 'mysql', + }, + }, + { + data: { + id: 'postgresql', + 'span.type': 'db', + 'span.subtype': 'postgresql', + }, + }, + { + data: { + id: 'redis', + 'span.type': 'db', + 'span.subtype': 'redis', + }, + }, + { data: { id: 'external', 'span.type': 'external' } }, + { data: { id: 'ext', 'span.type': 'ext' } }, + { + data: { + id: 'graphql', + 'span.type': 'external', + 'span.subtype': 'graphql', + }, + }, + { + data: { + id: 'grpc', + 'span.type': 'external', + 'span.subtype': 'grpc', + }, + }, + { + data: { + id: 'websocket', + 'span.type': 'external', + 'span.subtype': 'websocket', + }, + }, + { data: { id: 'messaging', 'span.type': 'messaging' } }, + { + data: { + id: 'jms', + 'span.type': 'messaging', + 'span.subtype': 'jms', + }, + }, + { + data: { + id: 'kafka', + 'span.type': 'messaging', + 'span.subtype': 'kafka', + }, + }, + { data: { id: 'template', 'span.type': 'template' } }, + { + data: { + id: 'handlebars', + 'span.type': 'template', + 'span.subtype': 'handlebars', + }, + }, + { + data: { + id: 'dark', + 'service.name': 'dark service', + 'agent.name': 'dark', + }, + }, + { + data: { + id: 'dotnet', + 'service.name': 'dotnet service', + 'agent.name': 'dotnet', + }, + }, + { + data: { + id: 'dotNet', + 'service.name': 'dotNet service', + 'agent.name': 'dotNet', + }, + }, + { + data: { + id: 'go', + 'service.name': 'go service', + 'agent.name': 'go', + }, + }, + { + data: { + id: 'java', + 'service.name': 'java service', + 'agent.name': 'java', + }, + }, + { + data: { + id: 'RUM (js-base)', + 'service.name': 'RUM service', + 'agent.name': 'js-base', + }, + }, + { + data: { + id: 'RUM (rum-js)', + 'service.name': 'RUM service', + 'agent.name': 'rum-js', + }, + }, + { + data: { + id: 'nodejs', + 'service.name': 'nodejs service', + 'agent.name': 'nodejs', + }, + }, + { + data: { + id: 'php', + 'service.name': 'php service', + 'agent.name': 'php', + }, + }, + { + data: { + id: 'python', + 'service.name': 'python service', + 'agent.name': 'python', + }, + }, + { + data: { + id: 'ruby', + 'service.name': 'ruby service', + 'agent.name': 'ruby', + }, + }, + ]; + cy.add(elements); + + return ( + + {cy.nodes().map((node) => ( + + + agent.name: {node.data('agent.name') || 'undefined'} +
+ span.type: {node.data('span.type') || 'undefined'} +
+ span.subtype: {node.data('span.subtype') || 'undefined'} + + } + icon={ + {node.data('label')} + } + title={node.data('id')} + /> +
+ ))} +
+ ); + }, + { + info: { + propTables: false, + source: false, + }, + } +); + +storiesOf('app/ServiceMap/Cytoscape', module).add( + 'node severity', + () => { + const elements = [ + { data: { id: 'undefined', 'service.name': 'severity: undefined' } }, + { + data: { + id: 'warning', + 'service.name': 'severity: warning', + severity: 'warning', + }, + }, + { + data: { + id: 'minor', + 'service.name': 'severity: minor', + severity: 'minor', + }, + }, + { + data: { + id: 'major', + 'service.name': 'severity: major', + severity: 'major', + }, + }, + { + data: { + id: 'critical', + 'service.name': 'severity: critical', + severity: 'critical', + }, + }, + ]; + return ; + }, + { + info: { propTables: false, source: false }, + } +); diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/CytoscapeExampleData.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/CytoscapeExampleData.stories.tsx new file mode 100644 index 0000000000000..33b3fab28f9ac --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/CytoscapeExampleData.stories.tsx @@ -0,0 +1,198 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* eslint-disable no-console */ + +import { + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiFieldNumber, + EuiToolTip, + EuiCodeEditor, +} from '@elastic/eui'; +import { storiesOf } from '@storybook/react'; +import React, { useState, useEffect } from 'react'; +import { Cytoscape } from '../Cytoscape'; +import { generateServiceMapElements } from './generate_service_map_elements'; +import exampleResponseOpbeansBeats from './example_response_opbeans_beats.json'; +import exampleResponseHipsterStore from './example_response_hipster_store.json'; +import exampleResponseTodo from './example_response_todo.json'; + +const STORYBOOK_PATH = 'app/ServiceMap/Cytoscape/Example data'; + +const SESSION_STORAGE_KEY = `${STORYBOOK_PATH}/pre-loaded map`; +function getSessionJson() { + return window.sessionStorage.getItem(SESSION_STORAGE_KEY); +} +function setSessionJson(json: string) { + window.sessionStorage.setItem(SESSION_STORAGE_KEY, json); +} + +storiesOf(STORYBOOK_PATH, module).add( + 'Generate map', + () => { + const [size, setSize] = useState(10); + const [json, setJson] = useState(''); + const [elements, setElements] = useState( + generateServiceMapElements(size) + ); + + return ( +
+ + + { + setElements(generateServiceMapElements(size)); + setJson(''); + }} + > + Generate service map + + + + + setSize(e.target.valueAsNumber)} + /> + + + + { + setJson(JSON.stringify({ elements }, null, 2)); + }} + > + Get JSON + + + + + + + {json && ( + + )} +
+ ); + }, + { + info: { propTables: false, source: false }, + } +); + +storiesOf(STORYBOOK_PATH, module).add( + 'Map from JSON', + () => { + const [json, setJson] = useState( + getSessionJson() || JSON.stringify(exampleResponseTodo, null, 2) + ); + const [elements, setElements] = useState([]); + useEffect(() => { + try { + setElements(JSON.parse(json).elements); + } catch (error) { + console.log(error); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+ + + + + { + setElements(JSON.parse(json).elements); + setSessionJson(json); + }} + > + Render JSON + + + + { + setJson(value); + }} + /> +
+ ); + }, + { + info: { propTables: false, source: false }, + } +); + +storiesOf(STORYBOOK_PATH, module).add( + 'Todo app', + () => { + return ( +
+ +
+ ); + }, + { + info: { propTables: false, source: false }, + } +); + +storiesOf(STORYBOOK_PATH, module).add( + 'Opbeans + beats', + () => { + return ( +
+ +
+ ); + }, + { + info: { propTables: false, source: false }, + } +); + +storiesOf(STORYBOOK_PATH, module).add( + 'Hipster store', + () => { + return ( +
+ +
+ ); + }, + { + info: { propTables: false, source: false }, + } +); diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_hipster_store.json b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_hipster_store.json new file mode 100644 index 0000000000000..4a31d9d24548c --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_hipster_store.json @@ -0,0 +1,355 @@ +{ + "elements": [ + { + "data": { + "source": "advertService", + "target": ">elasticsearch", + "id": "advertService~>elasticsearch", + "sourceData": { + "id": "advertService", + "service.name": "advertService", + "agent.name": "java" + }, + "targetData": { + "span.subtype": "elasticsearch", + "span.destination.service.resource": "elasticsearch", + "span.type": "db", + "id": ">elasticsearch", + "label": "elasticsearch" + } + } + }, + { + "data": { + "source": "checkoutService", + "target": "cartService", + "id": "checkoutService~cartService", + "sourceData": { + "id": "checkoutService", + "service.name": "checkoutService", + "agent.name": "go", + "service.framework.name": "grpc" + }, + "targetData": { + "id": "cartService", + "service.name": "cartService", + "agent.name": "dotnet" + } + } + }, + { + "data": { + "source": "checkoutService", + "target": "currencyService", + "id": "checkoutService~currencyService", + "sourceData": { + "id": "checkoutService", + "service.name": "checkoutService", + "agent.name": "go", + "service.framework.name": "grpc" + }, + "targetData": { + "id": "currencyService", + "service.name": "currencyService", + "agent.name": "nodejs" + } + } + }, + { + "data": { + "source": "checkoutService", + "target": "emailService", + "id": "checkoutService~emailService", + "sourceData": { + "id": "checkoutService", + "service.name": "checkoutService", + "agent.name": "go", + "service.framework.name": "grpc" + }, + "targetData": { + "id": "emailService", + "service.name": "emailService", + "agent.name": "python" + } + } + }, + { + "data": { + "source": "checkoutService", + "target": "paymentService", + "id": "checkoutService~paymentService", + "sourceData": { + "id": "checkoutService", + "service.name": "checkoutService", + "agent.name": "go", + "service.framework.name": "grpc" + }, + "targetData": { + "id": "paymentService", + "service.name": "paymentService", + "agent.name": "nodejs" + } + } + }, + { + "data": { + "source": "checkoutService", + "target": "productCatalogService", + "id": "checkoutService~productCatalogService", + "sourceData": { + "id": "checkoutService", + "service.name": "checkoutService", + "agent.name": "go", + "service.framework.name": "grpc" + }, + "targetData": { + "id": "productCatalogService", + "service.name": "productCatalogService", + "agent.name": "go", + "service.framework.name": "grpc" + } + } + }, + { + "data": { + "source": "checkoutService", + "target": "shippingService", + "id": "checkoutService~shippingService", + "sourceData": { + "id": "checkoutService", + "service.name": "checkoutService", + "agent.name": "go", + "service.framework.name": "grpc" + }, + "targetData": { + "id": "shippingService", + "service.name": "shippingService", + "agent.name": "go", + "service.framework.name": "grpc" + } + } + }, + { + "data": { + "source": "frontend", + "target": "advertService", + "id": "frontend~advertService", + "sourceData": { + "id": "frontend", + "service.name": "frontend", + "agent.name": "go" + }, + "targetData": { + "id": "advertService", + "service.name": "advertService", + "agent.name": "java" + } + } + }, + { + "data": { + "source": "frontend", + "target": "cartService", + "id": "frontend~cartService", + "sourceData": { + "id": "frontend", + "service.name": "frontend", + "agent.name": "go" + }, + "targetData": { + "id": "cartService", + "service.name": "cartService", + "agent.name": "dotnet" + } + } + }, + { + "data": { + "source": "frontend", + "target": "checkoutService", + "id": "frontend~checkoutService", + "sourceData": { + "id": "frontend", + "service.name": "frontend", + "agent.name": "go" + }, + "targetData": { + "id": "checkoutService", + "service.name": "checkoutService", + "agent.name": "go", + "service.framework.name": "grpc" + } + } + }, + { + "data": { + "source": "frontend", + "target": "currencyService", + "id": "frontend~currencyService", + "sourceData": { + "id": "frontend", + "service.name": "frontend", + "agent.name": "go" + }, + "targetData": { + "id": "currencyService", + "service.name": "currencyService", + "agent.name": "nodejs" + } + } + }, + { + "data": { + "source": "frontend", + "target": "productCatalogService", + "id": "frontend~productCatalogService", + "sourceData": { + "id": "frontend", + "service.name": "frontend", + "agent.name": "go" + }, + "targetData": { + "id": "productCatalogService", + "service.name": "productCatalogService", + "agent.name": "go", + "service.framework.name": "grpc" + } + } + }, + { + "data": { + "source": "frontend", + "target": "recommendationService", + "id": "frontend~recommendationService", + "sourceData": { + "id": "frontend", + "service.name": "frontend", + "agent.name": "go" + }, + "targetData": { + "id": "recommendationService", + "service.name": "recommendationService", + "agent.name": "python" + } + } + }, + { + "data": { + "source": "frontend", + "target": "shippingService", + "id": "frontend~shippingService", + "sourceData": { + "id": "frontend", + "service.name": "frontend", + "agent.name": "go" + }, + "targetData": { + "id": "shippingService", + "service.name": "shippingService", + "agent.name": "go", + "service.framework.name": "grpc" + } + } + }, + { + "data": { + "source": "recommendationService", + "target": "productCatalogService", + "id": "recommendationService~productCatalogService", + "sourceData": { + "id": "recommendationService", + "service.name": "recommendationService", + "agent.name": "python" + }, + "targetData": { + "id": "productCatalogService", + "service.name": "productCatalogService", + "agent.name": "go", + "service.framework.name": "grpc" + } + } + }, + { + "data": { + "id": "frontend", + "service.name": "frontend", + "agent.name": "go" + } + }, + { + "data": { + "id": "checkoutService", + "service.name": "checkoutService", + "agent.name": "go", + "service.framework.name": "grpc" + } + }, + { + "data": { + "id": "cartService", + "service.name": "cartService", + "agent.name": "dotnet" + } + }, + { + "data": { + "id": "currencyService", + "service.name": "currencyService", + "agent.name": "nodejs" + } + }, + { + "data": { + "id": "productCatalogService", + "service.name": "productCatalogService", + "agent.name": "go", + "service.framework.name": "grpc" + } + }, + { + "data": { + "id": "advertService", + "service.name": "advertService", + "agent.name": "java" + } + }, + { + "data": { + "span.subtype": "elasticsearch", + "span.destination.service.resource": "elasticsearch", + "span.type": "db", + "id": ">elasticsearch", + "label": "elasticsearch" + } + }, + { + "data": { + "id": "paymentService", + "service.name": "paymentService", + "agent.name": "nodejs" + } + }, + { + "data": { + "id": "shippingService", + "service.name": "shippingService", + "agent.name": "go", + "service.framework.name": "grpc" + } + }, + { + "data": { + "id": "emailService", + "service.name": "emailService", + "agent.name": "python" + } + }, + { + "data": { + "id": "recommendationService", + "service.name": "recommendationService", + "agent.name": "python" + } + } + ] +} diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json similarity index 57% rename from x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json rename to x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json index e55ba65bcbcb9..153fa57bb05e7 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json @@ -2,12 +2,12 @@ "elements": [ { "data": { - "source": "apm-server", + "source": "auditbeat", "target": ">elasticsearch", - "id": "apm-server~>elasticsearch", + "id": "auditbeat~>elasticsearch", "sourceData": { - "id": "apm-server", - "service.name": "apm-server", + "id": "auditbeat", + "service.name": "auditbeat", "agent.name": "go" }, "targetData": { @@ -21,67 +21,78 @@ }, { "data": { - "source": "client", - "target": "opbeans-node", - "id": "client~opbeans-node", + "source": "filebeat", + "target": ">elasticsearch", + "id": "filebeat~>elasticsearch", "sourceData": { - "id": "client", - "service.name": "client", - "agent.name": "rum-js" + "id": "filebeat", + "service.name": "filebeat", + "agent.name": "go" }, "targetData": { - "id": "opbeans-node", - "service.environment": "production", - "service.name": "opbeans-node", - "agent.name": "nodejs", - "service.framework.name": "express" + "span.subtype": "elasticsearch", + "span.destination.service.resource": "elasticsearch", + "span.type": "db", + "id": ">elasticsearch", + "label": "elasticsearch" } } }, { "data": { - "source": "opbeans-go", - "target": ">postgresql", - "id": "opbeans-go~>postgresql", + "source": "heartbeat", + "target": ">elasticsearch", + "id": "heartbeat~>elasticsearch", "sourceData": { - "id": "opbeans-go", - "service.environment": "production", - "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" + "id": "heartbeat", + "service.name": "heartbeat", + "agent.name": "go" }, "targetData": { - "span.subtype": "postgresql", - "span.destination.service.resource": "postgresql", + "span.subtype": "elasticsearch", + "span.destination.service.resource": "elasticsearch", "span.type": "db", - "id": ">postgresql", - "label": "postgresql" + "id": ">elasticsearch", + "label": "elasticsearch" + } + } + }, + { + "data": { + "source": "metricbeat", + "target": ">elasticsearch", + "id": "metricbeat~>elasticsearch", + "sourceData": { + "id": "metricbeat", + "service.name": "metricbeat", + "agent.name": "go" + }, + "targetData": { + "span.subtype": "elasticsearch", + "span.destination.service.resource": "elasticsearch", + "span.type": "db", + "id": ">elasticsearch", + "label": "elasticsearch" } } }, { "data": { "source": "opbeans-go", - "target": "opbeans-dotnet", - "id": "opbeans-go~opbeans-dotnet", + "target": ">postgresql", + "id": "opbeans-go~>postgresql", "sourceData": { "id": "opbeans-go", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" - }, - "targetData": { - "id": "opbeans-dotnet", - "service.name": "opbeans-dotnet", - "agent.name": "dotnet", - "service.framework.name": "ASP.NET Core", - "max_score": 43.63972429875661, - "severity": "minor" + "agent.name": "go" + }, + "targetData": { + "span.subtype": "postgresql", + "span.destination.service.resource": "postgresql", + "span.type": "db", + "id": ">postgresql", + "label": "postgresql" } } }, @@ -92,20 +103,15 @@ "id": "opbeans-go~opbeans-java", "sourceData": { "id": "opbeans-go", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" + "agent.name": "go" }, "targetData": { "id": "opbeans-java", "service.environment": "production", "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" + "agent.name": "java" }, "bidirectional": true } @@ -117,45 +123,15 @@ "id": "opbeans-go~opbeans-node", "sourceData": { "id": "opbeans-go", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" + "agent.name": "go" }, "targetData": { "id": "opbeans-node", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-node", - "agent.name": "nodejs", - "service.framework.name": "express" - }, - "bidirectional": true - } - }, - { - "data": { - "source": "opbeans-go", - "target": "opbeans-python", - "id": "opbeans-go~opbeans-python", - "sourceData": { - "id": "opbeans-go", - "service.environment": "production", - "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" - }, - "targetData": { - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "agent.name": "nodejs" }, "bidirectional": true } @@ -167,19 +143,15 @@ "id": "opbeans-go~opbeans-ruby", "sourceData": { "id": "opbeans-go", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" + "agent.name": "go" }, "targetData": { "id": "opbeans-ruby", "service.environment": "production", "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" + "agent.name": "ruby" }, "bidirectional": true } @@ -193,9 +165,7 @@ "id": "opbeans-java", "service.environment": "production", "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" + "agent.name": "java" }, "targetData": { "span.subtype": "postgresql", @@ -206,29 +176,6 @@ } } }, - { - "data": { - "source": "opbeans-java", - "target": "opbeans-dotnet", - "id": "opbeans-java~opbeans-dotnet", - "sourceData": { - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" - }, - "targetData": { - "id": "opbeans-dotnet", - "service.name": "opbeans-dotnet", - "agent.name": "dotnet", - "service.framework.name": "ASP.NET Core", - "max_score": 43.63972429875661, - "severity": "minor" - } - } - }, { "data": { "source": "opbeans-java", @@ -238,18 +185,13 @@ "id": "opbeans-java", "service.environment": "production", "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" + "agent.name": "java" }, "targetData": { "id": "opbeans-go", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" + "agent.name": "go" }, "isInverseEdge": true } @@ -263,111 +205,92 @@ "id": "opbeans-java", "service.environment": "production", "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" + "agent.name": "java" }, "targetData": { "id": "opbeans-node", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-node", - "agent.name": "nodejs", - "service.framework.name": "express" - }, - "bidirectional": true + "agent.name": "nodejs" + } } }, { "data": { "source": "opbeans-java", - "target": "opbeans-python", - "id": "opbeans-java~opbeans-python", + "target": "opbeans-ruby", + "id": "opbeans-java~opbeans-ruby", "sourceData": { "id": "opbeans-java", "service.environment": "production", "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" + "agent.name": "java" }, "targetData": { - "id": "opbeans-python", + "id": "opbeans-ruby", "service.environment": "production", - "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "service.name": "opbeans-ruby", + "agent.name": "ruby" }, "bidirectional": true } }, { "data": { - "source": "opbeans-java", - "target": "opbeans-ruby", - "id": "opbeans-java~opbeans-ruby", + "source": "opbeans-node", + "target": ">postgresql", + "id": "opbeans-node~>postgresql", "sourceData": { - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" + "id": "opbeans-node", + "service.environment": "testing", + "service.name": "opbeans-node", + "agent.name": "nodejs" }, "targetData": { - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" - }, - "bidirectional": true + "span.subtype": "postgresql", + "span.destination.service.resource": "postgresql", + "span.type": "db", + "id": ">postgresql", + "label": "postgresql" + } } }, { "data": { "source": "opbeans-node", - "target": "opbeans-go", - "id": "opbeans-node~opbeans-go", + "target": ">redis", + "id": "opbeans-node~>redis", "sourceData": { "id": "opbeans-node", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-node", - "agent.name": "nodejs", - "service.framework.name": "express" + "agent.name": "nodejs" }, "targetData": { - "id": "opbeans-go", - "service.environment": "production", - "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" - }, - "isInverseEdge": true + "span.subtype": "redis", + "span.destination.service.resource": "redis", + "span.type": "cache", + "id": ">redis", + "label": "redis" + } } }, { "data": { "source": "opbeans-node", - "target": "opbeans-java", - "id": "opbeans-node~opbeans-java", + "target": "opbeans-go", + "id": "opbeans-node~opbeans-go", "sourceData": { "id": "opbeans-node", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-node", - "agent.name": "nodejs", - "service.framework.name": "express" + "agent.name": "nodejs" }, "targetData": { - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" + "id": "opbeans-go", + "service.environment": "testing", + "service.name": "opbeans-go", + "agent.name": "go" }, "isInverseEdge": true } @@ -379,19 +302,15 @@ "id": "opbeans-node~opbeans-python", "sourceData": { "id": "opbeans-node", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-node", - "agent.name": "nodejs", - "service.framework.name": "express" + "agent.name": "nodejs" }, "targetData": { "id": "opbeans-python", "service.environment": "production", "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "agent.name": "python" }, "bidirectional": true } @@ -403,17 +322,15 @@ "id": "opbeans-node~opbeans-ruby", "sourceData": { "id": "opbeans-node", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-node", - "agent.name": "nodejs", - "service.framework.name": "express" + "agent.name": "nodejs" }, "targetData": { "id": "opbeans-ruby", "service.environment": "production", "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" + "agent.name": "ruby" }, "bidirectional": true } @@ -427,10 +344,7 @@ "id": "opbeans-python", "service.environment": "production", "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "agent.name": "python" }, "targetData": { "span.subtype": "elasticsearch", @@ -450,10 +364,7 @@ "id": "opbeans-python", "service.environment": "production", "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "agent.name": "python" }, "targetData": { "span.subtype": "postgresql", @@ -473,44 +384,17 @@ "id": "opbeans-python", "service.environment": "production", "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "agent.name": "python" }, "targetData": { "span.subtype": "redis", "span.destination.service.resource": "redis", - "span.type": "db", + "span.type": "cache", "id": ">redis", "label": "redis" } } }, - { - "data": { - "source": "opbeans-python", - "target": "opbeans-dotnet", - "id": "opbeans-python~opbeans-dotnet", - "sourceData": { - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" - }, - "targetData": { - "id": "opbeans-dotnet", - "service.name": "opbeans-dotnet", - "agent.name": "dotnet", - "service.framework.name": "ASP.NET Core", - "max_score": 43.63972429875661, - "severity": "minor" - } - } - }, { "data": { "source": "opbeans-python", @@ -520,46 +404,14 @@ "id": "opbeans-python", "service.environment": "production", "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "agent.name": "python" }, "targetData": { "id": "opbeans-go", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" - }, - "isInverseEdge": true - } - }, - { - "data": { - "source": "opbeans-python", - "target": "opbeans-java", - "id": "opbeans-python~opbeans-java", - "sourceData": { - "id": "opbeans-python", - "service.environment": "production", - "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" - }, - "targetData": { - "id": "opbeans-java", - "service.environment": "production", - "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" - }, - "isInverseEdge": true + "agent.name": "go" + } } }, { @@ -571,17 +423,13 @@ "id": "opbeans-python", "service.environment": "production", "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "agent.name": "python" }, "targetData": { "id": "opbeans-node", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-node", - "agent.name": "nodejs", - "service.framework.name": "express" + "agent.name": "nodejs" }, "isInverseEdge": true } @@ -595,17 +443,13 @@ "id": "opbeans-python", "service.environment": "production", "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "agent.name": "python" }, "targetData": { "id": "opbeans-ruby", "service.environment": "production", "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" + "agent.name": "ruby" }, "bidirectional": true } @@ -619,8 +463,7 @@ "id": "opbeans-ruby", "service.environment": "production", "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" + "agent.name": "ruby" }, "targetData": { "span.subtype": "postgresql", @@ -631,28 +474,6 @@ } } }, - { - "data": { - "source": "opbeans-ruby", - "target": "opbeans-dotnet", - "id": "opbeans-ruby~opbeans-dotnet", - "sourceData": { - "id": "opbeans-ruby", - "service.environment": "production", - "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" - }, - "targetData": { - "id": "opbeans-dotnet", - "service.name": "opbeans-dotnet", - "agent.name": "dotnet", - "service.framework.name": "ASP.NET Core", - "max_score": 43.63972429875661, - "severity": "minor" - } - } - }, { "data": { "source": "opbeans-ruby", @@ -662,17 +483,13 @@ "id": "opbeans-ruby", "service.environment": "production", "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" + "agent.name": "ruby" }, "targetData": { "id": "opbeans-go", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" + "agent.name": "go" }, "isInverseEdge": true } @@ -686,16 +503,13 @@ "id": "opbeans-ruby", "service.environment": "production", "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" + "agent.name": "ruby" }, "targetData": { "id": "opbeans-java", "service.environment": "production", "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" + "agent.name": "java" }, "isInverseEdge": true } @@ -709,15 +523,13 @@ "id": "opbeans-ruby", "service.environment": "production", "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" + "agent.name": "ruby" }, "targetData": { "id": "opbeans-node", - "service.environment": "production", + "service.environment": "testing", "service.name": "opbeans-node", - "agent.name": "nodejs", - "service.framework.name": "express" + "agent.name": "nodejs" }, "isInverseEdge": true } @@ -731,29 +543,118 @@ "id": "opbeans-ruby", "service.environment": "production", "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" + "agent.name": "ruby" }, "targetData": { "id": "opbeans-python", "service.environment": "production", "service.name": "opbeans-python", - "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "agent.name": "python" }, "isInverseEdge": true } }, { "data": { - "id": "opbeans-java", + "source": "opbeans-rum", + "target": "opbeans-go", + "id": "opbeans-rum~opbeans-go", + "sourceData": { + "id": "opbeans-rum", + "service.name": "opbeans-rum", + "agent.name": "rum-js" + }, + "targetData": { + "id": "opbeans-go", + "service.environment": "testing", + "service.name": "opbeans-go", + "agent.name": "go" + } + } + }, + { + "data": { + "source": "opbeans-rum", + "target": "opbeans-java", + "id": "opbeans-rum~opbeans-java", + "sourceData": { + "id": "opbeans-rum", + "service.name": "opbeans-rum", + "agent.name": "rum-js" + }, + "targetData": { + "id": "opbeans-java", + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java" + } + } + }, + { + "data": { + "source": "opbeans-rum", + "target": "opbeans-node", + "id": "opbeans-rum~opbeans-node", + "sourceData": { + "id": "opbeans-rum", + "service.name": "opbeans-rum", + "agent.name": "rum-js" + }, + "targetData": { + "id": "opbeans-node", + "service.environment": "testing", + "service.name": "opbeans-node", + "agent.name": "nodejs" + } + } + }, + { + "data": { + "source": "opbeans-rum", + "target": "opbeans-python", + "id": "opbeans-rum~opbeans-python", + "sourceData": { + "id": "opbeans-rum", + "service.name": "opbeans-rum", + "agent.name": "rum-js" + }, + "targetData": { + "id": "opbeans-python", + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python" + } + } + }, + { + "data": { + "source": "opbeans-rum", + "target": "opbeans-ruby", + "id": "opbeans-rum~opbeans-ruby", + "sourceData": { + "id": "opbeans-rum", + "service.name": "opbeans-rum", + "agent.name": "rum-js" + }, + "targetData": { + "id": "opbeans-ruby", + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby" + } + } + }, + { + "data": { + "id": "opbeans-ruby", "service.environment": "production", - "service.name": "opbeans-java", - "agent.name": "java", - "max_score": 31.374423806075157, - "severity": "minor" + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "anomaly_score": 0.9451165434428855, + "anomaly_severity": "warning", + "actual_value": 600594.456140351, + "typical_value": 98407.17630621382, + "ml_job_id": "opbeans-ruby-request-high_mean_response_time" } }, { @@ -762,45 +663,53 @@ "service.environment": "production", "service.name": "opbeans-python", "agent.name": "python", - "service.framework.name": "django", - "max_score": 92.48735, - "severity": "critical" + "anomaly_score": 92.10488496975145, + "anomaly_severity": "critical", + "actual_value": 1596444.9295154181, + "typical_value": 1555919.4721130468, + "ml_job_id": "opbeans-python-celery-high_mean_response_time" } }, { "data": { - "span.subtype": "postgresql", - "span.destination.service.resource": "postgresql", - "span.type": "db", - "id": ">postgresql", - "label": "postgresql" + "id": "opbeans-node", + "service.environment": "testing", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "anomaly_score": 41.31593099784474, + "anomaly_severity": "minor", + "actual_value": 1187598.8214285707, + "typical_value": 1010742.0877798817, + "ml_job_id": "opbeans-node-worker-high_mean_response_time" } }, { "data": { - "id": "opbeans-ruby", + "id": "opbeans-java", "service.environment": "production", - "service.name": "opbeans-ruby", - "agent.name": "ruby", - "service.framework.name": "Ruby on Rails" + "service.name": "opbeans-java", + "agent.name": "java", + "anomaly_score": 0.36530918260427264, + "anomaly_severity": "warning", + "actual_value": 2151205.742857142, + "typical_value": 55528.3821951346, + "ml_job_id": "opbeans-java-request-high_mean_response_time" } }, { "data": { - "id": "opbeans-go", - "service.environment": "production", - "service.name": "opbeans-go", - "agent.name": "go", - "service.framework.name": "gin", - "max_score": 92.40731, - "severity": "critical" + "span.subtype": "postgresql", + "span.destination.service.resource": "postgresql", + "span.type": "db", + "id": ">postgresql", + "label": "postgresql" } }, { "data": { - "id": "apm-server", - "service.name": "apm-server", - "agent.name": "go" + "id": "opbeans-rum", + "service.name": "opbeans-rum", + "agent.name": "rum-js" } }, { @@ -814,37 +723,60 @@ }, { "data": { - "id": "opbeans-node", - "service.environment": "production", - "service.name": "opbeans-node", - "agent.name": "nodejs", - "service.framework.name": "express" + "span.subtype": "redis", + "span.destination.service.resource": "redis", + "span.type": "cache", + "id": ">redis", + "label": "redis" } }, { "data": { - "id": "opbeans-dotnet", - "service.name": "opbeans-dotnet", - "agent.name": "dotnet", - "service.framework.name": "ASP.NET Core", - "max_score": 43.63972429875661, - "severity": "minor" + "id": "opbeans-go", + "service.environment": "testing", + "service.name": "opbeans-go", + "agent.name": "go", + "anomaly_score": 0.2633884161762746, + "anomaly_severity": "warning", + "actual_value": 9247010.450000009, + "typical_value": 94426.04179433428, + "ml_job_id": "opbeans-go-request-high_mean_response_time" } }, { "data": { - "span.subtype": "redis", - "span.destination.service.resource": "redis", - "span.type": "db", - "id": ">redis", - "label": "redis" + "id": "heartbeat", + "service.name": "heartbeat", + "agent.name": "go" } }, { "data": { - "id": "client", - "service.name": "client", - "agent.name": "rum-js" + "id": "filebeat", + "service.name": "filebeat", + "agent.name": "go" + } + }, + { + "data": { + "id": "metricbeat", + "service.name": "metricbeat", + "agent.name": "go" + } + }, + { + "data": { + "id": "auditbeat", + "service.name": "auditbeat", + "agent.name": "go" + } + }, + { + "data": { + "service.name": "opbeans-dotnet", + "agent.name": "dotNet", + "service.environment": null, + "id": "opbeans-dotnet" } } ] diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_todo.json b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_todo.json new file mode 100644 index 0000000000000..c848cd92dd366 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_todo.json @@ -0,0 +1,63 @@ +{ + "elements": [ + { + "data": { + "source": "todo-app", + "target": "task-service", + "id": "todo-app~task-service", + "sourceData": { + "id": "todo-app", + "service.name": "todo-app", + "agent.name": "rum-js" + }, + "targetData": { + "id": "task-service", + "service.name": "task-service", + "agent.name": "nodejs" + } + } + }, + { + "data": { + "source": "task-service", + "target": ">elasticsearch", + "id": "task-service~>elasticsearch", + "sourceData": { + "id": "task-service", + "service.name": "task-service", + "agent.name": "nodejs" + }, + "targetData": { + "span.subtype": "elasticsearch", + "span.destination.service.resource": "elasticsearch", + "span.type": "db", + "id": ">elasticsearch", + "label": "elasticsearch" + } + } + }, + { + "data": { + "id": "todo-app", + "service.name": "todo-app", + "agent.name": "rum-js" + } + }, + { + "data": { + "id": "task-service", + "service.name": "task-service", + "agent.name": "nodejs" + } + }, + { + "data": { + "span.subtype": "elasticsearch", + "span.destination.service.resource": "elasticsearch", + "span.type": "db", + "id": ">elasticsearch", + "label": "elasticsearch" + } + } + ] +} diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/generate_service_map_elements.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/generate_service_map_elements.ts new file mode 100644 index 0000000000000..e7d55cd570710 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/generate_service_map_elements.ts @@ -0,0 +1,222 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getSeverity } from '../../../../../common/ml_job_constants'; + +export function generateServiceMapElements(size: number): any[] { + const services = range(size).map((i) => { + const name = getName(); + const anomalyScore = randn(101); + return { + id: name, + 'service.environment': 'production', + 'service.name': name, + 'agent.name': getAgentName(), + anomaly_score: anomalyScore, + anomaly_severity: getSeverity(anomalyScore), + actual_value: Math.random() * 2000000, + typical_value: Math.random() * 1000000, + ml_job_id: `${name}-request-high_mean_response_time`, + }; + }); + + const connections = range(Math.round(size * 1.5)) + .map((i) => { + const sourceNode = services[randn(size)]; + const targetNode = services[randn(size)]; + return { + id: `${sourceNode.id}~${targetNode.id}`, + source: sourceNode.id, + target: targetNode.id, + ...(probability(0.3) + ? { + bidirectional: true, + } + : null), + }; + }) + .filter(({ source, target }) => source !== target); + + return [ + ...services.map((serviceData) => ({ data: serviceData })), + ...connections.map((connectionData) => ({ data: connectionData })), + ]; +} + +function range(n: number) { + return Array(n) + .fill(0) + .map((e, i) => i); +} + +function randn(n: number) { + return Math.floor(Math.random() * n); +} + +function probability(p: number) { + return Math.random() < p; +} + +function getAgentName() { + return AGENT_NAMES[Math.floor(Math.random() * AGENT_NAMES.length)]; +} + +function getName() { + return NAMES[Math.floor(Math.random() * NAMES.length)]; +} + +const AGENT_NAMES = [ + 'dotnet', + 'go', + 'java', + 'rum-js', + 'nodejs', + 'php', + 'python', + 'ruby', +]; + +const NAMES = [ + 'abomination', + 'anaconda', + 'apocalypse', + 'arcade', + 'angel', + 'asp', + 'beast', + 'beetle', + 'bishop', + 'black-knight', + 'black-mamba', + 'black-widow', + 'blade', + 'blob', + 'boomerang', + 'bullseye', + 'black-panther', + 'cable', + 'cannonball', + 'carnage', + 'callisto', + 'colossus', + 'crimson-dynamo', + 'cyclops', + 'cypher', + 'daredevil', + 'dazzler', + 'deadpool', + 'deathbringer', + 'death', + 'deathlok', + 'deathstrike', + 'destiny', + 'detonator', + 'diablo', + 'doctor-doom', + 'doctor-octopus', + 'doctor-strange', + 'domino', + 'dragonhart,', + 'electro', + 'elektra', + 'falcon', + 'forge', + 'fury', + 'gambit', + 'gladiator', + 'green', + 'grizzly', + 'hammerhead', + 'havok', + 'hawk-owl', + 'hawkeye', + 'hobgoblin', + 'hulk', + 'human-torch', + 'hurricane', + 'iceman', + 'iron-man', + 'invisible-woman', + 'juggernaut', + 'kingpin', + 'ka-zar', + 'leech', + 'loki', + 'longshot', + 'lumpkin,', + 'madame-web', + 'magician', + 'magneto', + 'man-thing', + 'mastermind', + 'mister-fantastic', + 'mister-sinister', + 'mister-nix', + 'modok', + 'mojo', + 'mole-man', + 'morbius', + 'morlocks', + 'moondragon', + 'moon', + 'madrox', + 'mystique', + 'namor', + 'nightmare', + 'nightcrawler', + 'nighthawk', + 'nihil', + 'northstar', + 'omega-red', + 'orb-weaver', + 'ox', + 'polaris', + 'power-man', + 'princess-python', + 'proteus', + 'punisher', + 'pyro', + 'quicksilver', + 'rhino', + 'rogue', + 'ronin', + 'sabretooth', + 'sandman', + 'scorpion', + 'sentinel', + 'shadowcat', + 'shocker', + 'silvermane', + 'silver-surfer', + 'spider-man', + 'spider-woman', + 'spiral', + 'storm', + 'stryfe', + 'sub-zero', + 'sunder', + 'super-skrull', + 'swarm', + 'tarantula', + 'thanos', + 'thor', + 'tinkerer', + 'toad', + 'unus', + 'valkyrie', + 'vanisher', + 'venom', + 'vision', + 'vulture', + 'wasp', + 'whiz-kid', + 'wildpack', + 'wolfsbane', + 'wolverine', + 'wraith', + 'yellowjacket', + 'zero', +]; diff --git a/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/CustomPlot.stories.tsx b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/CustomPlot.stories.tsx new file mode 100644 index 0000000000000..48e83763cb9df --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/CustomPlot.stories.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { storiesOf } from '@storybook/react'; +import React from 'react'; +// @ts-ignore +import CustomPlot from './'; + +storiesOf('shared/charts/CustomPlot', module).add( + 'with annotations but no data', + () => { + const annotations = [ + { + type: 'version', + id: '2020-06-10 04:36:31', + '@timestamp': 1591763925012, + text: '2020-06-10 04:36:31', + }, + { + type: 'version', + id: '2020-06-10 15:23:01', + '@timestamp': 1591802689233, + text: '2020-06-10 15:23:01', + }, + ]; + return ; + }, + { + info: { + source: false, + text: + "When a chart has no data but does have annotations, the annotations shouldn't show up at all.", + }, + } +); diff --git a/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/index.js b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/index.js index 050cb0639ee88..e1ffec3a8d97f 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/index.js +++ b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/index.js @@ -168,7 +168,7 @@ export class InnerCustomPlot extends PureComponent { tickFormatX={this.props.tickFormatX} /> - {this.state.showAnnotations && !isEmpty(annotations) && ( + {this.state.showAnnotations && !isEmpty(annotations) && !noHits && ( { this.setState(({ showAnnotations }) => ({ diff --git a/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/CustomPlot.test.js b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/CustomPlot.test.js index d906e7f5093c2..ad1d73f2b766b 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/CustomPlot.test.js +++ b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/CustomPlot.test.js @@ -255,12 +255,28 @@ describe('when response has no data', () => { const onHover = jest.fn(); const onMouseLeave = jest.fn(); const onSelectionEnd = jest.fn(); + const annotations = [ + { + type: 'version', + id: '2020-06-10 04:36:31', + '@timestamp': 1591763925012, + text: '2020-06-10 04:36:31', + }, + { + type: 'version', + id: '2020-06-10 15:23:01', + '@timestamp': 1591802689233, + text: '2020-06-10 15:23:01', + }, + ]; + let wrapper; beforeEach(() => { const series = getEmptySeries(1451606400000, 1451610000000); wrapper = mount( { expect(wrapper.find('Tooltip').length).toEqual(0); }); + it('should not show annotations', () => { + expect(wrapper.find('AnnotationsPlot')).toHaveLength(0); + }); + it('should have correct markup', () => { expect(toJson(wrapper)).toMatchSnapshot(); }); diff --git a/x-pack/plugins/canvas/public/components/router/index.ts b/x-pack/plugins/canvas/public/components/router/index.ts index 5e014870f5158..fa857c6f0cd3c 100644 --- a/x-pack/plugins/canvas/public/components/router/index.ts +++ b/x-pack/plugins/canvas/public/components/router/index.ts @@ -11,7 +11,6 @@ import { enableAutoplay, setRefreshInterval, setAutoplayInterval, - // @ts-ignore untyped local } from '../../state/actions/workpad'; // @ts-ignore untyped local import { Router as Component } from './router'; diff --git a/x-pack/plugins/canvas/public/components/workpad_color_picker/index.ts b/x-pack/plugins/canvas/public/components/workpad_color_picker/index.ts index c6dddab3b5dd1..abd40731078ec 100644 --- a/x-pack/plugins/canvas/public/components/workpad_color_picker/index.ts +++ b/x-pack/plugins/canvas/public/components/workpad_color_picker/index.ts @@ -5,7 +5,6 @@ */ import { connect } from 'react-redux'; -// @ts-ignore import { addColor, removeColor } from '../../state/actions/workpad'; import { getWorkpadColors } from '../../state/selectors/workpad'; diff --git a/x-pack/plugins/canvas/public/components/workpad_config/index.js b/x-pack/plugins/canvas/public/components/workpad_config/index.ts similarity index 63% rename from x-pack/plugins/canvas/public/components/workpad_config/index.js rename to x-pack/plugins/canvas/public/components/workpad_config/index.ts index 913cf7093e726..e417821fd4f67 100644 --- a/x-pack/plugins/canvas/public/components/workpad_config/index.js +++ b/x-pack/plugins/canvas/public/components/workpad_config/index.ts @@ -7,28 +7,29 @@ import { connect } from 'react-redux'; import { get } from 'lodash'; -import { sizeWorkpad, setName, setWorkpadCSS } from '../../state/actions/workpad'; +import { sizeWorkpad as setSize, setName, setWorkpadCSS } from '../../state/actions/workpad'; import { getWorkpad } from '../../state/selectors/workpad'; import { DEFAULT_WORKPAD_CSS } from '../../../common/lib/constants'; import { WorkpadConfig as Component } from './workpad_config'; +import { State } from '../../../types'; -const mapStateToProps = (state) => { +const mapStateToProps = (state: State) => { const workpad = getWorkpad(state); return { - name: get(workpad, 'name'), + name: get(workpad, 'name'), size: { - width: get(workpad, 'width'), - height: get(workpad, 'height'), + width: get(workpad, 'width'), + height: get(workpad, 'height'), }, - css: get(workpad, 'css', DEFAULT_WORKPAD_CSS), + css: get(workpad, 'css', DEFAULT_WORKPAD_CSS), }; }; const mapDispatchToProps = { - setSize: (size) => sizeWorkpad(size), - setName: (name) => setName(name), - setWorkpadCSS: (css) => setWorkpadCSS(css), + setSize, + setName, + setWorkpadCSS, }; export const WorkpadConfig = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/x-pack/plugins/canvas/public/components/workpad_config/workpad_config.js b/x-pack/plugins/canvas/public/components/workpad_config/workpad_config.js deleted file mode 100644 index 45758c9965653..0000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_config/workpad_config.js +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { - EuiFieldText, - EuiFieldNumber, - EuiBadge, - EuiButtonIcon, - EuiFormRow, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiTitle, - EuiToolTip, - EuiTextArea, - EuiAccordion, - EuiText, - EuiButton, -} from '@elastic/eui'; -import { DEFAULT_WORKPAD_CSS } from '../../../common/lib/constants'; -import { ComponentStrings } from '../../../i18n'; - -const { WorkpadConfig: strings } = ComponentStrings; - -export class WorkpadConfig extends PureComponent { - static propTypes = { - size: PropTypes.object.isRequired, - name: PropTypes.string.isRequired, - css: PropTypes.string, - setSize: PropTypes.func.isRequired, - setName: PropTypes.func.isRequired, - setWorkpadCSS: PropTypes.func.isRequired, - }; - - state = { - css: this.props.css, - }; - - render() { - const { size, name, setSize, setName, setWorkpadCSS } = this.props; - const { css } = this.state; - const rotate = () => setSize({ width: size.height, height: size.width }); - - const badges = [ - { - name: '1080p', - size: { height: 1080, width: 1920 }, - }, - { - name: '720p', - size: { height: 720, width: 1280 }, - }, - { - name: 'A4', - size: { height: 842, width: 590 }, - }, - { - name: strings.getUSLetterButtonLabel(), - size: { height: 792, width: 612 }, - }, - ]; - - return ( -
-
- -

{strings.getTitle()}

-
-
- - - - - setName(e.target.value)} /> - - - - - - - - setSize({ width: Number(e.target.value), height: size.height })} - value={size.width} - /> - - - - - - - - - - - - setSize({ height: Number(e.target.value), width: size.width })} - value={size.height} - /> - - - - - - -
- {badges.map((badge, i) => ( - setSize(badge.size)} - aria-label={strings.getPageSizeBadgeAriaLabel(badge.name)} - onClickAriaLabel={strings.getPageSizeBadgeOnClickAriaLabel(badge.name)} - > - {badge.name} - - ))} -
- - -
- - - {strings.getGlobalCSSLabel()} - - - } - > -
- this.setState({ css: e.target.value })} - rows={10} - /> - - setWorkpadCSS(css || DEFAULT_WORKPAD_CSS)}> - {strings.getApplyStylesheetButtonLabel()} - - -
-
-
-
- ); - } -} diff --git a/x-pack/plugins/canvas/public/components/workpad_config/workpad_config.tsx b/x-pack/plugins/canvas/public/components/workpad_config/workpad_config.tsx new file mode 100644 index 0000000000000..7b7a1e08b2c5d --- /dev/null +++ b/x-pack/plugins/canvas/public/components/workpad_config/workpad_config.tsx @@ -0,0 +1,175 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent, useState } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiFieldText, + EuiFieldNumber, + EuiBadge, + EuiButtonIcon, + EuiFormRow, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiTitle, + EuiToolTip, + EuiTextArea, + EuiAccordion, + EuiText, + EuiButton, +} from '@elastic/eui'; +import { DEFAULT_WORKPAD_CSS } from '../../../common/lib/constants'; +import { ComponentStrings } from '../../../i18n'; + +const { WorkpadConfig: strings } = ComponentStrings; + +interface Props { + size: { + height: number; + width: number; + }; + name: string; + css?: string; + setSize: ({ height, width }: { height: number; width: number }) => void; + setName: (name: string) => void; + setWorkpadCSS: (css: string) => void; +} + +export const WorkpadConfig: FunctionComponent = (props) => { + const [css, setCSS] = useState(props.css); + const { size, name, setSize, setName, setWorkpadCSS } = props; + const rotate = () => setSize({ width: size.height, height: size.width }); + + const badges = [ + { + name: '1080p', + size: { height: 1080, width: 1920 }, + }, + { + name: '720p', + size: { height: 720, width: 1280 }, + }, + { + name: 'A4', + size: { height: 842, width: 590 }, + }, + { + name: strings.getUSLetterButtonLabel(), + size: { height: 792, width: 612 }, + }, + ]; + + return ( +
+
+ +

{strings.getTitle()}

+
+
+ + + + + setName(e.target.value)} /> + + + + + + + + setSize({ width: Number(e.target.value), height: size.height })} + value={size.width} + /> + + + + + + + + + + + + setSize({ height: Number(e.target.value), width: size.width })} + value={size.height} + /> + + + + + + +
+ {badges.map((badge, i) => ( + setSize(badge.size)} + aria-label={strings.getPageSizeBadgeAriaLabel(badge.name)} + onClickAriaLabel={strings.getPageSizeBadgeOnClickAriaLabel(badge.name)} + > + {badge.name} + + ))} +
+ + +
+ + + {strings.getGlobalCSSLabel()} + + + } + > +
+ setCSS(e.target.value)} + rows={10} + /> + + setWorkpadCSS(css || DEFAULT_WORKPAD_CSS)}> + {strings.getApplyStylesheetButtonLabel()} + + +
+
+
+
+ ); +}; + +WorkpadConfig.propTypes = { + size: PropTypes.object.isRequired, + name: PropTypes.string.isRequired, + css: PropTypes.string, + setSize: PropTypes.func.isRequired, + setName: PropTypes.func.isRequired, + setWorkpadCSS: PropTypes.func.isRequired, +}; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/view_menu/index.ts b/x-pack/plugins/canvas/public/components/workpad_header/view_menu/index.ts index e561607cb101e..0765973915f77 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/view_menu/index.ts +++ b/x-pack/plugins/canvas/public/components/workpad_header/view_menu/index.ts @@ -14,13 +14,11 @@ import { State, CanvasWorkpadBoundingBox } from '../../../../types'; import { fetchAllRenderables } from '../../../state/actions/elements'; // @ts-ignore Untyped local import { setZoomScale, setFullscreen, selectToplevelNodes } from '../../../state/actions/transient'; -// @ts-ignore Untyped local import { setWriteable, setRefreshInterval, enableAutoplay, setAutoplayInterval, - // @ts-ignore Untyped local } from '../../../state/actions/workpad'; import { getZoomScale, canUserWrite } from '../../../state/selectors/app'; import { @@ -75,7 +73,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ }, doRefresh: () => dispatch(fetchAllRenderables()), setRefreshInterval: (interval: number) => dispatch(setRefreshInterval(interval)), - enableAutoplay: (autoplay: number) => dispatch(enableAutoplay(autoplay)), + enableAutoplay: (autoplay: number) => dispatch(enableAutoplay(!!autoplay)), setAutoplayInterval: (interval: number) => dispatch(setAutoplayInterval(interval)), }); diff --git a/x-pack/plugins/canvas/public/lib/create_thunk.ts b/x-pack/plugins/canvas/public/lib/create_thunk.ts new file mode 100644 index 0000000000000..cbcaeeccc8b93 --- /dev/null +++ b/x-pack/plugins/canvas/public/lib/create_thunk.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Dispatch, Action } from 'redux'; +// @ts-ignore untyped dependency +import { createThunk as createThunkFn } from 'redux-thunks/cjs'; +import { State } from '../../types'; + +type CreateThunk = ( + type: string, + fn: ( + params: { type: string; dispatch: Dispatch; getState: () => State }, + ...args: Arguments + ) => void +) => (...args: Arguments) => Action; + +// This declaration exists because redux-thunks is not typed, and has a dependency on +// Canvas State. Therefore, creating a wrapper that strongly-types the function-- and creates +// a single point of replacement, should the need arise-- is a nice workaround. +export const createThunk = createThunkFn as CreateThunk; diff --git a/x-pack/plugins/canvas/public/state/actions/elements.js b/x-pack/plugins/canvas/public/state/actions/elements.js index 47fbc782f90d3..e89e62917da39 100644 --- a/x-pack/plugins/canvas/public/state/actions/elements.js +++ b/x-pack/plugins/canvas/public/state/actions/elements.js @@ -5,10 +5,10 @@ */ import { createAction } from 'redux-actions'; -import { createThunk } from 'redux-thunks/cjs'; import immutable from 'object-path-immutable'; import { get, pick, cloneDeep, without } from 'lodash'; import { toExpression, safeElementFromExpression } from '@kbn/interpreter/common'; +import { createThunk } from '../../lib/create_thunk'; import { getPages, getNodeById, getNodes, getSelectedPageIndex } from '../selectors/workpad'; import { getValue as getResolvedArgsValue } from '../selectors/resolved_args'; import { getDefaultElement } from '../defaults'; diff --git a/x-pack/plugins/canvas/public/state/actions/embeddable.ts b/x-pack/plugins/canvas/public/state/actions/embeddable.ts index e2cf588ec20a9..a153cb7f4354d 100644 --- a/x-pack/plugins/canvas/public/state/actions/embeddable.ts +++ b/x-pack/plugins/canvas/public/state/actions/embeddable.ts @@ -6,8 +6,7 @@ import { Dispatch } from 'redux'; import { createAction } from 'redux-actions'; -// @ts-ignore Untyped -import { createThunk } from 'redux-thunks'; +import { createThunk } from '../../lib/create_thunk'; // @ts-ignore Untyped Local import { fetchRenderable } from './elements'; import { State } from '../../../types'; diff --git a/x-pack/plugins/canvas/public/state/actions/workpad.js b/x-pack/plugins/canvas/public/state/actions/workpad.ts similarity index 54% rename from x-pack/plugins/canvas/public/state/actions/workpad.js rename to x-pack/plugins/canvas/public/state/actions/workpad.ts index 167c156dce998..47df38838f890 100644 --- a/x-pack/plugins/canvas/public/state/actions/workpad.js +++ b/x-pack/plugins/canvas/public/state/actions/workpad.ts @@ -5,26 +5,28 @@ */ import { createAction } from 'redux-actions'; -import { createThunk } from 'redux-thunks/cjs'; import { without, includes } from 'lodash'; +import { createThunk } from '../../lib/create_thunk'; import { getWorkpadColors } from '../selectors/workpad'; +// @ts-ignore import { fetchAllRenderables } from './elements'; +import { CanvasWorkpad } from '../../../types'; -export const sizeWorkpad = createAction('sizeWorkpad'); -export const setName = createAction('setName'); -export const setWriteable = createAction('setWriteable'); -export const setColors = createAction('setColors'); -export const setRefreshInterval = createAction('setRefreshInterval'); -export const setWorkpadCSS = createAction('setWorkpadCSS'); -export const enableAutoplay = createAction('enableAutoplay'); -export const setAutoplayInterval = createAction('setAutoplayInterval'); -export const resetWorkpad = createAction('resetWorkpad'); +export const sizeWorkpad = createAction<{ height: number; width: number }>('sizeWorkpad'); +export const setName = createAction('setName'); +export const setWriteable = createAction('setWriteable'); +export const setColors = createAction('setColors'); +export const setRefreshInterval = createAction('setRefreshInterval'); +export const setWorkpadCSS = createAction('setWorkpadCSS'); +export const enableAutoplay = createAction('enableAutoplay'); +export const setAutoplayInterval = createAction('setAutoplayInterval'); +export const resetWorkpad = createAction('resetWorkpad'); export const initializeWorkpad = createThunk('initializeWorkpad', ({ dispatch }) => { dispatch(fetchAllRenderables()); }); -export const addColor = createThunk('addColor', ({ dispatch, getState }, color) => { +export const addColor = createThunk('addColor', ({ dispatch, getState }, color: string) => { const colors = getWorkpadColors(getState()).slice(0); if (!includes(colors, color)) { colors.push(color); @@ -32,16 +34,20 @@ export const addColor = createThunk('addColor', ({ dispatch, getState }, color) dispatch(setColors(colors)); }); -export const removeColor = createThunk('removeColor', ({ dispatch, getState }, color) => { +export const removeColor = createThunk('removeColor', ({ dispatch, getState }, color: string) => { dispatch(setColors(without(getWorkpadColors(getState()), color))); }); export const setWorkpad = createThunk( 'setWorkpad', - ({ dispatch, type }, workpad, { loadPages = true } = {}) => { + ( + { dispatch, type }, + workpad: CanvasWorkpad, + { loadPages = true }: { loadPages?: boolean } = {} + ) => { dispatch(createAction(type)(workpad)); // set the workpad object in state if (loadPages) { dispatch(initializeWorkpad()); - } // load all the elements on the workpad + } } ); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/contants.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/contants.ts new file mode 100644 index 0000000000000..a58aad6dc6bc2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/contants.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const POLICY_NAME = 'my_policy'; + +export const DELETE_PHASE_POLICY = { + version: 1, + modified_date: Date.now(), + policy: { + phases: { + hot: { + min_age: '0ms', + actions: { + rollover: { + max_size: '50gb', + }, + }, + }, + delete: { + min_age: '0ms', + actions: { + wait_for_snapshot: { + policy: 'my_snapshot_policy', + }, + delete: { + delete_searchable_snapshot: true, + }, + }, + }, + }, + }, + name: POLICY_NAME, +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx new file mode 100644 index 0000000000000..a36cd7e35c36f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { act } from 'react-dom/test-utils'; + +import { registerTestBed, TestBed, TestBedConfig } from '../../../../../test_utils'; + +import { POLICY_NAME } from './contants'; +import { TestSubjects } from '../helpers'; + +import { EditPolicy } from '../../../public/application/sections/edit_policy'; +import { indexLifecycleManagementStore } from '../../../public/application/store'; + +const testBedConfig: TestBedConfig = { + store: () => indexLifecycleManagementStore(), + memoryRouter: { + initialEntries: [`/policies/edit/${POLICY_NAME}`], + componentRoutePath: `/policies/edit/:policyName`, + }, +}; + +const initTestBed = registerTestBed(EditPolicy, testBedConfig); + +export interface EditPolicyTestBed extends TestBed { + actions: { + setWaitForSnapshotPolicy: (snapshotPolicyName: string) => void; + savePolicy: () => void; + }; +} + +export const setup = async (): Promise => { + const testBed = await initTestBed(); + + const setWaitForSnapshotPolicy = (snapshotPolicyName: string) => { + const { component, form } = testBed; + form.setInputValue('waitForSnapshotField', snapshotPolicyName, true); + component.update(); + }; + + const savePolicy = async () => { + const { component, find } = testBed; + await act(async () => { + find('savePolicyButton').simulate('click'); + }); + component.update(); + }; + + return { + ...testBed, + actions: { + setWaitForSnapshotPolicy, + savePolicy, + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.test.ts new file mode 100644 index 0000000000000..cc04749af3205 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { act } from 'react-dom/test-utils'; + +import { setupEnvironment } from '../helpers/setup_environment'; + +import { EditPolicyTestBed, setup } from './edit_policy.helpers'; +import { DELETE_PHASE_POLICY } from './contants'; + +import { API_BASE_PATH } from '../../../common/constants'; + +window.scrollTo = jest.fn(); + +describe('', () => { + let testBed: EditPolicyTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + afterAll(() => { + server.restore(); + }); + + describe('delete phase', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadPolicies([DELETE_PHASE_POLICY]); + + await act(async () => { + testBed = await setup(); + }); + + const { component } = testBed; + component.update(); + }); + + test('wait for snapshot policy field should correctly display snapshot policy name', () => { + expect(testBed.find('waitForSnapshotField').props().value).toEqual( + DELETE_PHASE_POLICY.policy.phases.delete.actions.wait_for_snapshot.policy + ); + }); + + test('wait for snapshot field should correctly update snapshot policy name', async () => { + const { actions } = testBed; + + const newPolicyName = 'my_new_snapshot_policy'; + actions.setWaitForSnapshotPolicy(newPolicyName); + await actions.savePolicy(); + + const expected = { + name: DELETE_PHASE_POLICY.name, + phases: { + ...DELETE_PHASE_POLICY.policy.phases, + delete: { + ...DELETE_PHASE_POLICY.policy.phases.delete, + actions: { + ...DELETE_PHASE_POLICY.policy.phases.delete.actions, + wait_for_snapshot: { + policy: newPolicyName, + }, + }, + }, + }, + }; + + const latestRequest = server.requests[server.requests.length - 1]; + expect(latestRequest.url).toBe(`${API_BASE_PATH}/policies`); + expect(latestRequest.method).toBe('POST'); + expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected); + }); + + test('wait for snapshot field should delete action if field is empty', async () => { + const { actions } = testBed; + + actions.setWaitForSnapshotPolicy(''); + await actions.savePolicy(); + + const expected = { + name: DELETE_PHASE_POLICY.name, + phases: { + ...DELETE_PHASE_POLICY.policy.phases, + delete: { + ...DELETE_PHASE_POLICY.policy.phases.delete, + actions: { + ...DELETE_PHASE_POLICY.policy.phases.delete.actions, + }, + }, + }, + }; + delete expected.phases.delete.actions.wait_for_snapshot; + + const latestRequest = server.requests[server.requests.length - 1]; + expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/http_requests.ts new file mode 100644 index 0000000000000..f41742fc104ff --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/http_requests.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SinonFakeServer, fakeServer } from 'sinon'; +import { API_BASE_PATH } from '../../../common/constants'; + +export const init = () => { + const server = fakeServer.create(); + server.respondImmediately = true; + server.respondWith([200, {}, 'DefaultServerResponse']); + + return { + server, + httpRequestsMockHelpers: registerHttpRequestMockHelpers(server), + }; +}; + +const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { + const setLoadPolicies = (response: any = []) => { + server.respondWith('GET', `${API_BASE_PATH}/policies`, [ + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify(response), + ]); + }; + + return { + setLoadPolicies, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts new file mode 100644 index 0000000000000..3cff2e3ab050f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export type TestSubjects = 'waitForSnapshotField' | 'savePolicyButton'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.ts new file mode 100644 index 0000000000000..b3205a9523c62 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/setup_environment.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import axios from 'axios'; +import axiosXhrAdapter from 'axios/lib/adapters/xhr'; + +import { init as initHttp } from '../../../public/application/services/http'; +import { init as initHttpRequests } from './http_requests'; +import { init as initUiMetric } from '../../../public/application/services/ui_metric'; +import { init as initNotification } from '../../../public/application/services/notification'; + +import { usageCollectionPluginMock } from '../../../../../../src/plugins/usage_collection/public/mocks'; + +import { + notificationServiceMock, + fatalErrorsServiceMock, +} from '../../../../../../src/core/public/mocks'; + +const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); + +export const setupEnvironment = () => { + initUiMetric(usageCollectionPluginMock.createSetupContract()); + initNotification( + notificationServiceMock.createSetupContract().toasts, + fatalErrorsServiceMock.createSetupContract() + ); + + mockHttpClient.interceptors.response.use(({ data }) => data); + initHttp(mockHttpClient); + const { server, httpRequestsMockHelpers } = initHttpRequests(); + + return { + server, + httpRequestsMockHelpers, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/constants/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/constants/index.ts index a631a38fbcb7e..6319fc0d68543 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/constants/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/constants/index.ts @@ -37,6 +37,8 @@ export const PHASE_PRIMARY_SHARD_COUNT: string = 'selectedPrimaryShardCount'; export const PHASE_REPLICA_COUNT: string = 'selectedReplicaCount'; export const PHASE_INDEX_PRIORITY: string = 'phaseIndexPriority'; +export const PHASE_WAIT_FOR_SNAPSHOT_POLICY = 'waitForSnapshotPolicy'; + export const PHASE_ATTRIBUTES_THAT_ARE_NUMBERS_VALIDATE: string[] = [ PHASE_ROLLOVER_MINIMUM_AGE, PHASE_FORCE_MERGE_SEGMENTS, diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/delete_phase/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/delete_phase/delete_phase.js index 3b3e489d38f7d..299bf28778ab4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/delete_phase/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/delete_phase/delete_phase.js @@ -7,10 +7,16 @@ import React, { PureComponent, Fragment } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiDescribedFormGroup, EuiSwitch } from '@elastic/eui'; +import { + EuiDescribedFormGroup, + EuiSwitch, + EuiFieldText, + EuiTextColor, + EuiFormRow, +} from '@elastic/eui'; -import { PHASE_DELETE, PHASE_ENABLED } from '../../../../constants'; -import { ActiveBadge, PhaseErrorMessage } from '../../../components'; +import { PHASE_DELETE, PHASE_ENABLED, PHASE_WAIT_FOR_SNAPSHOT_POLICY } from '../../../../constants'; +import { ActiveBadge, LearnMoreLink, OptionalLabel, PhaseErrorMessage } from '../../../components'; import { MinAgeInput } from '../min_age_input'; export class DeletePhase extends PureComponent { @@ -85,6 +91,48 @@ export class DeletePhase extends PureComponent {
)} + {phaseData[PHASE_ENABLED] ? ( + + + + } + description={ + + {' '} + + + } + titleSize="xs" + fullWidth + > + + + + + } + > + setPhaseData(PHASE_WAIT_FOR_SNAPSHOT_POLICY, e.target.value)} + /> + + + ) : null}
); } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/store/defaults/delete_phase.js b/x-pack/plugins/index_lifecycle_management/public/application/store/defaults/delete_phase.js index b5296cd83fabd..8534893e7e3b3 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/store/defaults/delete_phase.js +++ b/x-pack/plugins/index_lifecycle_management/public/application/store/defaults/delete_phase.js @@ -9,6 +9,7 @@ import { PHASE_ROLLOVER_MINIMUM_AGE, PHASE_ROLLOVER_MINIMUM_AGE_UNITS, PHASE_ROLLOVER_ALIAS, + PHASE_WAIT_FOR_SNAPSHOT_POLICY, } from '../../constants'; export const defaultDeletePhase = { @@ -17,5 +18,6 @@ export const defaultDeletePhase = { [PHASE_ROLLOVER_ALIAS]: '', [PHASE_ROLLOVER_MINIMUM_AGE]: 0, [PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: 'd', + [PHASE_WAIT_FOR_SNAPSHOT_POLICY]: '', }; export const defaultEmptyDeletePhase = defaultDeletePhase; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/store/defaults/index.d.ts b/x-pack/plugins/index_lifecycle_management/public/application/store/defaults/index.d.ts index 889e038f9e2c4..abf6db416c7f4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/store/defaults/index.d.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/store/defaults/index.d.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +export declare const defaultDeletePhase: any; export declare const defaultColdPhase: any; export declare const defaultWarmPhase: any; export declare const defaultHotPhase: any; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/store/selectors/policies.js b/x-pack/plugins/index_lifecycle_management/public/application/store/selectors/policies.js index a3aef8679817d..32c6d93383c22 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/store/selectors/policies.js +++ b/x-pack/plugins/index_lifecycle_management/public/application/store/selectors/policies.js @@ -31,6 +31,7 @@ import { PHASE_FREEZE_ENABLED, PHASE_INDEX_PRIORITY, PHASE_ROLLOVER_MAX_DOCUMENTS, + PHASE_WAIT_FOR_SNAPSHOT_POLICY, } from '../../constants'; import { filterItems, sortTable } from '../../services'; @@ -194,6 +195,9 @@ const phaseFromES = (phase, phaseName, defaultEmptyPolicy) => { if (actions.set_priority) { policy[PHASE_INDEX_PRIORITY] = actions.set_priority.priority; } + if (actions.wait_for_snapshot) { + policy[PHASE_WAIT_FOR_SNAPSHOT_POLICY] = actions.wait_for_snapshot.policy; + } } return policy; }; @@ -308,5 +312,13 @@ export const phaseToES = (phase, originalEsPhase) => { priority: phase[PHASE_INDEX_PRIORITY], }; } + + if (phase[PHASE_WAIT_FOR_SNAPSHOT_POLICY]) { + esPhase.actions.wait_for_snapshot = { + policy: phase[PHASE_WAIT_FOR_SNAPSHOT_POLICY], + }; + } else { + delete esPhase.actions.wait_for_snapshot; + } return esPhase; }; diff --git a/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json b/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json new file mode 100644 index 0000000000000..f0ed3ed9a0364 --- /dev/null +++ b/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json @@ -0,0 +1,4397 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Ingest Manager", + "version": "0.2", + "contact": { + "name": "Ingest Team" + }, + "license": { + "name": "Elastic" + } + }, + "servers": [ + { + "url": "http://localhost:5601/api/ingest_manager", + "description": "local" + } + ], + "paths": { + "/agent_configs": { + "get": { + "summary": "Agent Config - List", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AgentConfig" + } + }, + "total": { + "type": "number" + }, + "page": { + "type": "number" + }, + "perPage": { + "type": "number" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "items", + "total", + "page", + "perPage", + "success" + ] + }, + "examples": { + "success": { + "value": { + "items": [ + { + "id": "82da1fc0-8fbf-11ea-b2ce-01c4a6127154", + "name": "Default config", + "namespace": "default", + "description": "Default agent configuration created by Kibana", + "status": "active", + "datasources": [ + "8a5679b0-8fbf-11ea-b2ce-01c4a6127154" + ], + "is_default": true, + "monitoring_enabled": [ + "logs", + "metrics" + ], + "revision": 2, + "updated_on": "2020-05-06T17:32:21.905Z", + "updated_by": "system", + "agents": 0 + } + ], + "total": 1, + "page": 1, + "perPage": 50, + "success": true + } + } + } + } + } + } + }, + "operationId": "agent-config-list", + "parameters": [ + { + "$ref": "#/components/parameters/pageSizeParam" + }, + { + "$ref": "#/components/parameters/pageIndexParam" + }, + { + "$ref": "#/components/parameters/kueryParam" + } + ], + "description": "" + }, + "post": { + "summary": "Agent Config - Create", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "item": { + "$ref": "#/components/schemas/AgentConfig" + }, + "success": { + "type": "boolean" + } + } + } + } + } + } + }, + "operationId": "post-agent_configs", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewAgentConfig" + } + } + } + }, + "security": [], + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/agent_configs/{agentConfigId}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "agentConfigId", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Agent Config - Info", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "item": { + "$ref": "#/components/schemas/AgentConfig" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "item", + "success" + ] + }, + "examples": { + "success": { + "value": { + "item": { + "id": "82da1fc0-8fbf-11ea-b2ce-01c4a6127154", + "name": "Default config", + "namespace": "default", + "description": "Default agent configuration created by Kibana", + "status": "active", + "datasources": [ + { + "id": "8a5679b0-8fbf-11ea-b2ce-01c4a6127154", + "name": "system-1", + "namespace": "default", + "package": { + "name": "system", + "title": "System", + "version": "0.0.3" + }, + "enabled": true, + "config_id": "82da1fc0-8fbf-11ea-b2ce-01c4a6127154", + "output_id": "08adc51c-69f3-4294-80e2-24527c6ff73d", + "inputs": [ + { + "type": "logs", + "enabled": true, + "streams": [ + { + "id": "logs-system.auth", + "enabled": true, + "dataset": "system.auth", + "vars": { + "paths": { + "value": [ + "/var/log/auth.log*", + "/var/log/secure*" + ], + "type": "text" + } + }, + "agent_stream": { + "paths": [ + "/var/log/auth.log*", + "/var/log/secure*" + ], + "exclude_files": [ + ".gz$" + ], + "multiline": { + "pattern": "^\\s", + "match": "after" + }, + "processors": [ + { + "add_locale": null + }, + { + "add_fields": { + "target": "", + "fields": { + "ecs.version": "1.5.0" + } + } + } + ] + } + }, + { + "id": "logs-system.syslog", + "enabled": true, + "dataset": "system.syslog", + "vars": { + "paths": { + "value": [ + "/var/log/messages*", + "/var/log/syslog*" + ], + "type": "text" + } + }, + "agent_stream": { + "paths": [ + "/var/log/messages*", + "/var/log/syslog*" + ], + "exclude_files": [ + ".gz$" + ], + "multiline": { + "pattern": "^\\s", + "match": "after" + }, + "processors": [ + { + "add_locale": null + }, + { + "add_fields": { + "target": "", + "fields": { + "ecs.version": "1.5.0" + } + } + } + ] + } + } + ] + }, + { + "type": "system/metrics", + "enabled": true, + "streams": [ + { + "id": "system/metrics-system.core", + "enabled": true, + "dataset": "system.core", + "vars": { + "core.metrics": { + "value": [ + "percentages" + ], + "type": "text" + } + }, + "agent_stream": { + "metricsets": [ + "core" + ], + "core.metrics": "percentages" + } + }, + { + "id": "system/metrics-system.cpu", + "enabled": true, + "dataset": "system.cpu", + "vars": { + "core.metrics": { + "value": [ + "percentages" + ], + "type": "text" + }, + "cpu.metrics": { + "value": [ + "percentages", + "normalized_percentages" + ], + "type": "text" + }, + "period": { + "value": "10s", + "type": "text" + }, + "process.include_top_n.by_cpu": { + "value": 5, + "type": "integer" + }, + "process.include_top_n.by_memory": { + "value": 5, + "type": "integer" + }, + "processes": { + "value": [ + ".*" + ], + "type": "text" + } + }, + "agent_stream": { + "metricsets": [ + "cpu" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + } + }, + { + "id": "system/metrics-system.diskio", + "enabled": true, + "dataset": "system.diskio", + "agent_stream": { + "metricsets": [ + "diskio" + ] + } + }, + { + "id": "system/metrics-system.entropy", + "enabled": true, + "dataset": "system.entropy", + "agent_stream": { + "metricsets": [ + "entropy" + ] + } + }, + { + "id": "system/metrics-system.filesystem", + "enabled": true, + "dataset": "system.filesystem", + "vars": { + "period": { + "value": "1m", + "type": "text" + }, + "processors": { + "value": "- drop_event.when.regexp:\n system.filesystem.mount_point: ^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)\n", + "type": "yaml" + } + }, + "agent_stream": { + "metricsets": [ + "filesystem" + ], + "period": "1m", + "processors": [ + { + "drop_event.when.regexp": { + "system.filesystem.mount_point": "^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)" + } + } + ] + } + }, + { + "id": "system/metrics-system.fsstat", + "enabled": true, + "dataset": "system.fsstat", + "vars": { + "period": { + "value": "1m", + "type": "text" + }, + "processors": { + "value": "- drop_event.when.regexp:\n system.filesystem.mount_point: ^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)\n", + "type": "yaml" + } + }, + "agent_stream": { + "metricsets": [ + "fsstat" + ], + "period": "1m", + "processors": [ + { + "drop_event.when.regexp": { + "system.filesystem.mount_point": "^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)" + } + } + ] + } + }, + { + "id": "system/metrics-system.load", + "enabled": true, + "dataset": "system.load", + "vars": { + "core.metrics": { + "value": [ + "percentages" + ], + "type": "text" + }, + "cpu.metrics": { + "value": [ + "percentages", + "normalized_percentages" + ], + "type": "text" + }, + "period": { + "value": "10s", + "type": "text" + }, + "process.include_top_n.by_cpu": { + "value": 5, + "type": "integer" + }, + "process.include_top_n.by_memory": { + "value": 5, + "type": "integer" + }, + "processes": { + "value": [ + ".*" + ], + "type": "text" + } + }, + "agent_stream": { + "metricsets": [ + "load" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + } + }, + { + "id": "system/metrics-system.memory", + "enabled": true, + "dataset": "system.memory", + "vars": { + "core.metrics": { + "value": [ + "percentages" + ], + "type": "text" + }, + "cpu.metrics": { + "value": [ + "percentages", + "normalized_percentages" + ], + "type": "text" + }, + "period": { + "value": "10s", + "type": "text" + }, + "process.include_top_n.by_cpu": { + "value": 5, + "type": "integer" + }, + "process.include_top_n.by_memory": { + "value": 5, + "type": "integer" + }, + "processes": { + "value": [ + ".*" + ], + "type": "text" + } + }, + "agent_stream": { + "metricsets": [ + "memory" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + } + }, + { + "id": "system/metrics-system.network", + "enabled": true, + "dataset": "system.network", + "vars": { + "core.metrics": { + "value": [ + "percentages" + ], + "type": "text" + }, + "cpu.metrics": { + "value": [ + "percentages", + "normalized_percentages" + ], + "type": "text" + }, + "period": { + "value": "10s", + "type": "text" + }, + "process.include_top_n.by_cpu": { + "value": 5, + "type": "integer" + }, + "process.include_top_n.by_memory": { + "value": 5, + "type": "integer" + }, + "processes": { + "value": [ + ".*" + ], + "type": "text" + } + }, + "agent_stream": { + "metricsets": [ + "network" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + } + }, + { + "id": "system/metrics-system.network_summary", + "enabled": true, + "dataset": "system.network_summary", + "agent_stream": { + "metricsets": [ + "network_summary" + ] + } + }, + { + "id": "system/metrics-system.process", + "enabled": true, + "dataset": "system.process", + "vars": { + "core.metrics": { + "value": [ + "percentages" + ], + "type": "text" + }, + "cpu.metrics": { + "value": [ + "percentages", + "normalized_percentages" + ], + "type": "text" + }, + "period": { + "value": "10s", + "type": "text" + }, + "process.include_top_n.by_cpu": { + "value": 5, + "type": "integer" + }, + "process.include_top_n.by_memory": { + "value": 5, + "type": "integer" + }, + "processes": { + "value": [ + ".*" + ], + "type": "text" + } + }, + "agent_stream": { + "metricsets": [ + "process" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + } + }, + { + "id": "system/metrics-system.process_summary", + "enabled": true, + "dataset": "system.process_summary", + "vars": { + "core.metrics": { + "value": [ + "percentages" + ], + "type": "text" + }, + "cpu.metrics": { + "value": [ + "percentages", + "normalized_percentages" + ], + "type": "text" + }, + "period": { + "value": "10s", + "type": "text" + }, + "process.include_top_n.by_cpu": { + "value": 5, + "type": "integer" + }, + "process.include_top_n.by_memory": { + "value": 5, + "type": "integer" + }, + "processes": { + "value": [ + ".*" + ], + "type": "text" + } + }, + "agent_stream": { + "metricsets": [ + "process_summary" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + } + }, + { + "id": "system/metrics-system.raid", + "enabled": true, + "dataset": "system.raid", + "agent_stream": { + "metricsets": [ + "raid" + ] + } + }, + { + "id": "system/metrics-system.service", + "enabled": true, + "dataset": "system.service", + "agent_stream": { + "metricsets": [ + "service" + ] + } + }, + { + "id": "system/metrics-system.socket", + "enabled": true, + "dataset": "system.socket", + "agent_stream": { + "metricsets": [ + "socket" + ] + } + }, + { + "id": "system/metrics-system.socket_summary", + "enabled": true, + "dataset": "system.socket_summary", + "vars": { + "core.metrics": { + "value": [ + "percentages" + ], + "type": "text" + }, + "cpu.metrics": { + "value": [ + "percentages", + "normalized_percentages" + ], + "type": "text" + }, + "period": { + "value": "10s", + "type": "text" + }, + "process.include_top_n.by_cpu": { + "value": 5, + "type": "integer" + }, + "process.include_top_n.by_memory": { + "value": 5, + "type": "integer" + }, + "processes": { + "value": [ + ".*" + ], + "type": "text" + } + }, + "agent_stream": { + "metricsets": [ + "socket_summary" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + } + }, + { + "id": "system/metrics-system.uptime", + "enabled": true, + "dataset": "system.uptime", + "vars": { + "core.metrics": { + "value": [ + "percentages" + ], + "type": "text" + }, + "cpu.metrics": { + "value": [ + "percentages", + "normalized_percentages" + ], + "type": "text" + }, + "period": { + "value": "10s", + "type": "text" + }, + "processes": { + "value": [ + ".*" + ], + "type": "text" + } + }, + "agent_stream": { + "metricsets": [ + "uptime" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "processes": ".*" + } + }, + { + "id": "system/metrics-system.users", + "enabled": true, + "dataset": "system.users", + "agent_stream": { + "metricsets": [ + "users" + ] + } + } + ] + } + ], + "revision": 1 + } + ], + "is_default": true, + "monitoring_enabled": [ + "logs", + "metrics" + ], + "revision": 2, + "updated_on": "2020-05-06T17:32:21.905Z", + "updated_by": "system" + }, + "success": true + } + } + } + } + } + } + }, + "operationId": "agent-config-info", + "description": "Get one agent config", + "parameters": [] + }, + "put": { + "summary": "Agent Config - Update", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "item": { + "$ref": "#/components/schemas/AgentConfig" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "item", + "success" + ] + }, + "examples": { + "example-1": { + "value": { + "item": { + "id": "0b7130d0-5a37-11ea-ac2c-25e9ab4ecb2a", + "name": "UPDATED name", + "description": "UPDATED description", + "namespace": "UPDATED namespace", + "updated_on": "Fri Feb 28 2020 16:22:31 GMT-0500 (Eastern Standard Time)", + "updated_by": "elastic", + "datasources": [] + }, + "success": true + } + } + } + } + } + } + }, + "operationId": "put-agent_configs-agentConfigId", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewAgentConfig" + }, + "examples": { + "example-1": { + "value": { + "name": "UPDATED name", + "description": "UPDATED description", + "namespace": "UPDATED namespace" + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/agent_configs/delete": { + "post": { + "summary": "Agent Config - Delete", + "operationId": "post-agent_config-delete", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "id", + "success" + ] + } + }, + "examples": { + "success": { + "value": [ + { + "id": "df7d2540-5a47-11ea-80da-89b5a66da347", + "success": true + } + ] + }, + "fail": { + "value": [ + { + "id": "df7d2540-5a47-11ea-80da-89b5a66da347", + "success": false + } + ] + } + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "agentConfigIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "examples": { + "example-1": { + "value": { + "agentConfigIds": [ + "df7d2540-5a47-11ea-80da-89b5a66da347" + ] + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + }, + "parameters": [] + }, + "/datasources": { + "get": { + "summary": "Datasources - List", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Datasource" + } + }, + "total": { + "type": "number" + }, + "page": { + "type": "number" + }, + "perPage": { + "type": "number" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "items", + "success" + ] + }, + "examples": { + "example-1": { + "value": { + "items": [ + { + "id": "5d273cf0-5a44-11ea-80da-89b5a66da347", + "use_output": "default", + "inputs": [ + { + "type": "docker/metrics", + "streams": [ + { + "metricset": "status", + "dataset": "docker.status" + } + ] + }, + { + "type": "logs", + "streams": [ + { + "paths": [ + "/var/log/hello1.log", + "/var/log/hello2.log" + ] + } + ] + } + ] + }, + { + "id": "66490980-5a44-11ea-80da-89b5a66da347", + "namespace": "testing", + "use_output": "default", + "inputs": [ + { + "type": "apache/metrics", + "streams": [ + { + "enabled": true, + "metricset": "info" + } + ] + } + ] + }, + { + "id": "df1ccae0-5a49-11ea-94a6-81affd263f47", + "enabled": true, + "title": "This is a nice title for human", + "package": { + "name": "epm/nginx", + "version": "1.7.0" + }, + "namespace": "prod", + "use_output": "long_term_storage", + "inputs": [ + { + "type": "logs", + "streams": [ + { + "enabled": true, + "dataset": "nginx.acccess", + "paths": [ + "/var/log/nginx/access.log" + ] + }, + { + "enabled": true, + "dataset": "nginx.error", + "paths": [ + "/var/log/nginx/error.log" + ] + } + ] + }, + { + "type": "nginx/metrics", + "streams": [ + { + "id": "id string", + "enabled": true, + "dataset": "nginx.stub_status", + "metricset": "stub_status" + } + ] + } + ] + }, + { + "id": "f96a09d0-5a49-11ea-94a6-81affd263f47", + "enabled": true, + "title": "This is a nice title for human", + "package": { + "name": "epm/nginx", + "version": "1.7.0" + }, + "namespace": "prod", + "use_output": "long_term_storage", + "inputs": [ + { + "type": "logs", + "streams": [ + { + "enabled": true, + "dataset": "nginx.acccess", + "paths": [ + "/var/log/nginx/access.log" + ] + }, + { + "enabled": true, + "dataset": "nginx.error", + "paths": [ + "/var/log/nginx/error.log" + ] + } + ] + }, + { + "type": "nginx/metrics", + "streams": [ + { + "id": "id string", + "enabled": true, + "dataset": "nginx.stub_status", + "metricset": "stub_status" + } + ] + } + ] + }, + { + "id": "9ca403a0-5a66-11ea-9468-c911a41ab4f5", + "enabled": true, + "title": "This is a nice title for human", + "package": { + "name": "epm/nginx", + "version": "1.7.0" + }, + "namespace": "prod", + "use_output": "long_term_storage", + "inputs": [ + { + "type": "logs", + "streams": [ + { + "enabled": true, + "dataset": "nginx.acccess", + "paths": [ + "/var/log/nginx/access.log" + ] + }, + { + "enabled": true, + "dataset": "nginx.error", + "paths": [ + "/var/log/nginx/error.log" + ] + } + ] + }, + { + "type": "nginx/metrics", + "streams": [ + { + "id": "id string", + "enabled": true, + "dataset": "nginx.stub_status", + "metricset": "stub_status" + } + ] + } + ] + }, + { + "id": "27925980-5a44-11ea-80da-89b5a66da347", + "enabled": true, + "title": "UPDATED title for human", + "package": { + "name": "epm/nginx", + "version": "1.7.0" + }, + "namespace": "prod", + "use_output": "long_term_storage", + "inputs": [ + { + "streams": [ + { + "paths": [ + "/var/log/nginx/access.log" + ], + "dataset": "nginx.acccess", + "enabled": true + }, + { + "paths": [ + "/var/log/nginx/error.log" + ], + "dataset": "nginx.error", + "enabled": true + } + ], + "type": "logs" + }, + { + "streams": [ + { + "metricset": "stub_status", + "id": "id string", + "dataset": "nginx.stub_status", + "enabled": true + } + ], + "type": "nginx/metrics" + } + ] + } + ], + "total": 6, + "page": 1, + "perPage": 20, + "success": true + } + } + } + } + } + } + }, + "operationId": "get-datasources", + "security": [], + "parameters": [] + }, + "parameters": [], + "post": { + "summary": "Datasources - Create", + "operationId": "post-datasources", + "responses": { + "200": { + "description": "OK" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewDatasource" + }, + "examples": { + "example-1": { + "value": { + "enabled": true, + "title": "This is a nice title for human", + "package": { + "name": "epm/nginx", + "version": "1.7.0" + }, + "namespace": "prod", + "use_output": "long_term_storage", + "inputs": [ + { + "type": "logs", + "streams": [ + { + "enabled": true, + "dataset": "nginx.acccess", + "paths": [ + "/var/log/nginx/access.log" + ] + }, + { + "enabled": true, + "dataset": "nginx.error", + "paths": [ + "/var/log/nginx/error.log" + ] + } + ] + }, + { + "type": "nginx/metrics", + "streams": [ + { + "id": "id string", + "enabled": true, + "dataset": "nginx.stub_status", + "metricset": "stub_status" + } + ] + } + ] + } + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/datasources/{datasourceId}": { + "get": { + "summary": "Datasources - Info", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "item": { + "$ref": "#/components/schemas/Datasource" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "item", + "success" + ] + } + } + } + } + }, + "operationId": "get-datasources-datasourceId" + }, + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "datasourceId", + "in": "path", + "required": true + } + ], + "put": { + "summary": "Datasources - Update", + "operationId": "put-datasources-datasourceId", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "item": { + "$ref": "#/components/schemas/Datasource" + }, + "sucess": { + "type": "boolean" + } + }, + "required": [ + "item", + "sucess" + ] + } + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/fleet/setup": { + "get": { + "summary": "Fleet setup - Info", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "isInitialized": { + "type": "boolean" + } + }, + "required": [ + "isInitialized" + ] + }, + "examples": { + "success": { + "value": { + "isInitialized": true + } + }, + "failure": { + "value": { + "isInitialized": false + } + } + } + } + } + } + }, + "operationId": "get-fleet-setup", + "security": [ + { + "basicAuth": [] + } + ] + }, + "post": { + "summary": "Fleet setup - Create", + "operationId": "post-fleet-setup", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "isInitialized": { + "type": "boolean" + } + }, + "required": [ + "isInitialized" + ] + }, + "examples": { + "success": { + "value": { + "isInitialized": true + } + } + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "admin_username": { + "type": "string" + }, + "admin_password": { + "type": "string" + } + }, + "required": [ + "admin_username", + "admin_password" + ] + } + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/epm/packages/{pkgkey}": { + "get": { + "summary": "EPM - Packages - Info", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "response": { + "$ref": "#/components/schemas/PackageInfo" + }, + "success": { + "type": "boolean" + } + } + }, + { + "properties": { + "status": { + "type": "string", + "enum": [ + "installed", + "not_installed" + ] + }, + "savedObject": { + "type": "string" + } + }, + "required": [ + "status", + "savedObject" + ] + } + ] + }, + "examples": { + "example-1": { + "value": { + "response": { + "name": "coredns", + "title": "CoreDNS", + "version": "1.0.1", + "readme": "/package/coredns-1.0.1/docs/README.md", + "description": "CoreDNS logs and metrics integration.\nThe CoreDNS integrations allows to gather logs and metrics from the CoreDNS DNS server to get better insights.\n", + "type": "integration", + "categories": [ + "logs", + "metrics" + ], + "requirement": { + "kibana": { + "versions": ">6.7.0" + } + }, + "icons": [ + { + "src": "/package/coredns-1.0.1/img/icon.png", + "size": "1800x1800" + }, + { + "src": "/package/coredns-1.0.1/img/icon.svg", + "size": "255x144", + "type": "image/svg+xml" + } + ], + "assets": { + "kibana": { + "dashboard": [ + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "dashboard", + "file": "53aa1f70-443e-11e9-8548-ab7fbe04f038.json", + "path": "coredns-1.0.1/kibana/dashboard/53aa1f70-443e-11e9-8548-ab7fbe04f038.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "dashboard", + "file": "Metricbeat-CoreDNS-Dashboard-ecs.json", + "path": "coredns-1.0.1/kibana/dashboard/Metricbeat-CoreDNS-Dashboard-ecs.json" + } + ], + "visualization": [ + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "277fc650-67a9-11e9-a534-715561d0bf42.json", + "path": "coredns-1.0.1/kibana/visualization/277fc650-67a9-11e9-a534-715561d0bf42.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "27da53f0-53d5-11e9-b466-9be470bbd327-ecs.json", + "path": "coredns-1.0.1/kibana/visualization/27da53f0-53d5-11e9-b466-9be470bbd327-ecs.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "36e08510-53c4-11e9-b466-9be470bbd327-ecs.json", + "path": "coredns-1.0.1/kibana/visualization/36e08510-53c4-11e9-b466-9be470bbd327-ecs.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "3ad75810-4429-11e9-8548-ab7fbe04f038.json", + "path": "coredns-1.0.1/kibana/visualization/3ad75810-4429-11e9-8548-ab7fbe04f038.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "4804eaa0-7315-11e9-b0d0-414c3011ddbb.json", + "path": "coredns-1.0.1/kibana/visualization/4804eaa0-7315-11e9-b0d0-414c3011ddbb.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "57c74300-7308-11e9-b0d0-414c3011ddbb.json", + "path": "coredns-1.0.1/kibana/visualization/57c74300-7308-11e9-b0d0-414c3011ddbb.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "75743f70-443c-11e9-8548-ab7fbe04f038.json", + "path": "coredns-1.0.1/kibana/visualization/75743f70-443c-11e9-8548-ab7fbe04f038.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "86177430-728d-11e9-b0d0-414c3011ddbb.json", + "path": "coredns-1.0.1/kibana/visualization/86177430-728d-11e9-b0d0-414c3011ddbb.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "9dc640e0-4432-11e9-8548-ab7fbe04f038.json", + "path": "coredns-1.0.1/kibana/visualization/9dc640e0-4432-11e9-8548-ab7fbe04f038.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "a19df590-53c4-11e9-b466-9be470bbd327-ecs.json", + "path": "coredns-1.0.1/kibana/visualization/a19df590-53c4-11e9-b466-9be470bbd327-ecs.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "a58345f0-7298-11e9-b0d0-414c3011ddbb.json", + "path": "coredns-1.0.1/kibana/visualization/a58345f0-7298-11e9-b0d0-414c3011ddbb.json" + }, + { + "pkgkey": "coredns-1.0.1", + "service": "kibana", + "type": "visualization", + "file": "cfde7fb0-443d-11e9-8548-ab7fbe04f038.json", + "path": "coredns-1.0.1/kibana/visualization/cfde7fb0-443d-11e9-8548-ab7fbe04f038.json" + } + ] + } + }, + "format_version": "1.0.0", + "datasets": [ + { + "title": "CoreDNS logs", + "name": "log", + "release": "ga", + "type": "logs", + "ingest_pipeline": "pipeline-entry", + "vars": [ + { + "default": [ + "/var/log/coredns.log" + ], + "name": "paths", + "type": "textarea" + }, + { + "default": [ + "coredns" + ], + "name": "tags", + "type": "text" + } + ], + "package": "coredns" + }, + { + "title": "CoreDNS stats metrics", + "name": "stats", + "release": "ga", + "type": "metrics", + "vars": [ + { + "default": [ + "http://localhost:9153" + ], + "description": "CoreDNS hosts", + "name": "hosts", + "required": true + }, + { + "default": "10s", + "description": "Collection period. Valid values: 10s, 5m, 2h", + "name": "period" + }, + { + "name": "username", + "type": "text" + }, + { + "name": "password", + "type": "password" + } + ], + "package": "coredns" + } + ], + "download": "/epr/coredns/coredns-1.0.1.tar.gz", + "path": "/package/coredns-1.0.1", + "status": "installed", + "savedObject": { + "id": "coredns-1.0.1", + "type": "epm-package", + "updated_at": "2020-02-27T16:25:43.652Z", + "version": "WzU2LDFd", + "attributes": { + "installed": [ + { + "id": "53aa1f70-443e-11e9-8548-ab7fbe04f038", + "type": "dashboard" + }, + { + "id": "Metricbeat-CoreDNS-Dashboard-ecs", + "type": "dashboard" + }, + { + "id": "75743f70-443c-11e9-8548-ab7fbe04f038", + "type": "visualization" + }, + { + "id": "36e08510-53c4-11e9-b466-9be470bbd327-ecs", + "type": "visualization" + }, + { + "id": "277fc650-67a9-11e9-a534-715561d0bf42", + "type": "visualization" + }, + { + "id": "cfde7fb0-443d-11e9-8548-ab7fbe04f038", + "type": "visualization" + }, + { + "id": "a19df590-53c4-11e9-b466-9be470bbd327-ecs", + "type": "visualization" + }, + { + "id": "a58345f0-7298-11e9-b0d0-414c3011ddbb", + "type": "visualization" + }, + { + "id": "9dc640e0-4432-11e9-8548-ab7fbe04f038", + "type": "visualization" + }, + { + "id": "3ad75810-4429-11e9-8548-ab7fbe04f038", + "type": "visualization" + }, + { + "id": "57c74300-7308-11e9-b0d0-414c3011ddbb", + "type": "visualization" + }, + { + "id": "27da53f0-53d5-11e9-b466-9be470bbd327-ecs", + "type": "visualization" + }, + { + "id": "86177430-728d-11e9-b0d0-414c3011ddbb", + "type": "visualization" + }, + { + "id": "4804eaa0-7315-11e9-b0d0-414c3011ddbb", + "type": "visualization" + }, + { + "id": "logs-log-1.0.1-pipeline-plaintext", + "type": "ingest-pipeline" + }, + { + "id": "logs-log-1.0.1-pipeline-json", + "type": "ingest-pipeline" + }, + { + "id": "logs-log-1.0.1", + "type": "ingest-pipeline" + }, + { + "id": "logs-log", + "type": "index-template" + }, + { + "id": "metrics-stats", + "type": "index-template" + } + ] + }, + "references": [] + } + }, + "success": true + } + } + } + } + } + } + }, + "operationId": "get-epm-package-pkgkey", + "security": [ + { + "basicAuth": [] + } + ] + }, + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "pkgkey", + "in": "path", + "required": true + } + ], + "post": { + "summary": "EPM - Packages - Install", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "response": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ] + } + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "response", + "success" + ] + } + } + } + } + }, + "operationId": "post-epm-install-pkgkey", + "description": "", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + }, + "delete": { + "summary": "EPM - Packages - Delete", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "response": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ] + } + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "response", + "success" + ] + } + } + } + } + }, + "operationId": "post-epm-delete-pkgkey", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/epm/packages": { + "get": { + "summary": "EPM - Packages - List", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SearchResult" + } + }, + "examples": { + "success": { + "value": { + "response": [ + { + "description": "aws Integration", + "download": "/epr/aws/aws-0.0.3.tar.gz", + "icons": [ + { + "src": "/package/aws/0.0.3/img/logo_aws.svg", + "title": "logo aws", + "size": "32x32", + "type": "image/svg+xml" + } + ], + "name": "aws", + "path": "/package/aws/0.0.3", + "title": "aws", + "type": "integration", + "version": "0.0.3", + "status": "not_installed" + }, + { + "description": "This is the Elastic Endpoint package.", + "download": "/epr/endpoint/endpoint-0.1.0.tar.gz", + "icons": [ + { + "src": "/package/endpoint/0.1.0/img/logo-endpoint-64-color.svg", + "size": "16x16", + "type": "image/svg+xml" + } + ], + "name": "endpoint", + "path": "/package/endpoint/0.1.0", + "title": "Elastic Endpoint", + "type": "solution", + "version": "0.1.0", + "status": "installed", + "savedObject": { + "type": "epm-packages", + "id": "endpoint", + "attributes": { + "installed": [ + { + "id": "826759f0-7074-11ea-9bc8-6b38f4d29a16", + "type": "dashboard" + }, + { + "id": "55387750-729c-11ea-9bc8-6b38f4d29a16", + "type": "visualization" + }, + { + "id": "92b1edc0-706a-11ea-9bc8-6b38f4d29a16", + "type": "visualization" + }, + { + "id": "1cfceda0-728b-11ea-9bc8-6b38f4d29a16", + "type": "visualization" + }, + { + "id": "1e525190-7074-11ea-9bc8-6b38f4d29a16", + "type": "visualization" + }, + { + "id": "a3a3bd10-706b-11ea-9bc8-6b38f4d29a16", + "type": "map" + }, + { + "id": "events-endpoint", + "type": "index-template" + }, + { + "id": "metrics-endpoint", + "type": "index-template" + } + ], + "es_index_patterns": { + "events": "events-endpoint-*", + "metadata": "metrics-endpoint-*" + }, + "name": "endpoint", + "version": "0.1.0", + "internal": false, + "removable": false + }, + "references": [], + "updated_at": "2020-05-15T20:08:11.739Z", + "version": "WzEwOCwxXQ==" + } + }, + { + "description": "The log package should be used to create data sources for all type of logs for which an package doesn't exist yet.\n", + "download": "/epr/log/log-0.9.0.tar.gz", + "icons": [ + { + "src": "/package/log/0.9.0/img/icon.svg", + "type": "image/svg+xml" + } + ], + "name": "log", + "path": "/package/log/0.9.0", + "title": "Log Package", + "type": "integration", + "version": "0.9.0", + "status": "not_installed" + }, + { + "description": "This integration contains pretty long documentation.\nIt is used to show the different visualisations inside a documentation to test how we handle it.\nThe integration does not contain any assets except the documentation page.\n", + "download": "/epr/longdocs/longdocs-1.0.4.tar.gz", + "icons": [ + { + "src": "/package/longdocs/1.0.4/img/icon.svg", + "type": "image/svg+xml" + } + ], + "name": "longdocs", + "path": "/package/longdocs/1.0.4", + "title": "Long Docs", + "type": "integration", + "version": "1.0.4", + "status": "not_installed" + }, + { + "description": "This is an integration with only the metrics category.\n", + "download": "/epr/metricsonly/metricsonly-2.0.1.tar.gz", + "icons": [ + { + "src": "/package/metricsonly/2.0.1/img/icon.svg", + "type": "image/svg+xml" + } + ], + "name": "metricsonly", + "path": "/package/metricsonly/2.0.1", + "title": "Metrics Only", + "type": "integration", + "version": "2.0.1", + "status": "not_installed" + }, + { + "description": "Multiple versions of this integration exist.\n", + "download": "/epr/multiversion/multiversion-1.1.0.tar.gz", + "icons": [ + { + "src": "/package/multiversion/1.1.0/img/icon.svg", + "type": "image/svg+xml" + } + ], + "name": "multiversion", + "path": "/package/multiversion/1.1.0", + "title": "Multi Version", + "type": "integration", + "version": "1.1.0", + "status": "not_installed" + }, + { + "description": "MySQL Integration", + "download": "/epr/mysql/mysql-0.1.0.tar.gz", + "icons": [ + { + "src": "/package/mysql/0.1.0/img/logo_mysql.svg", + "title": "logo mysql", + "size": "32x32", + "type": "image/svg+xml" + } + ], + "name": "mysql", + "path": "/package/mysql/0.1.0", + "title": "MySQL", + "type": "integration", + "version": "0.1.0", + "status": "not_installed" + }, + { + "description": "Nginx Integration", + "download": "/epr/nginx/nginx-0.1.0.tar.gz", + "icons": [ + { + "src": "/package/nginx/0.1.0/img/logo_nginx.svg", + "title": "logo nginx", + "size": "32x32", + "type": "image/svg+xml" + } + ], + "name": "nginx", + "path": "/package/nginx/0.1.0", + "title": "Nginx", + "type": "integration", + "version": "0.1.0", + "status": "not_installed" + }, + { + "description": "Redis Integration", + "download": "/epr/redis/redis-0.1.0.tar.gz", + "icons": [ + { + "src": "/package/redis/0.1.0/img/logo_redis.svg", + "title": "logo redis", + "size": "32x32", + "type": "image/svg+xml" + } + ], + "name": "redis", + "path": "/package/redis/0.1.0", + "title": "Redis", + "type": "integration", + "version": "0.1.0", + "status": "not_installed" + }, + { + "description": "This package is used for defining all the properties of a package, the possible assets etc. It serves as a reference on all the config options which are possible.\n", + "download": "/epr/reference/reference-1.0.0.tar.gz", + "icons": [ + { + "src": "/package/reference/1.0.0/img/icon.svg", + "size": "32x32", + "type": "image/svg+xml" + } + ], + "name": "reference", + "path": "/package/reference/1.0.0", + "title": "Reference package", + "type": "integration", + "version": "1.0.0", + "status": "not_installed" + }, + { + "description": "System Integration", + "download": "/epr/system/system-0.1.0.tar.gz", + "icons": [ + { + "src": "/package/system/0.1.0/img/system.svg", + "title": "system", + "size": "1000x1000", + "type": "image/svg+xml" + } + ], + "name": "system", + "path": "/package/system/0.1.0", + "title": "System", + "type": "integration", + "version": "0.1.0", + "status": "installed", + "savedObject": { + "type": "epm-packages", + "id": "system", + "attributes": { + "installed": [ + { + "id": "c431f410-f9ac-11e9-90e8-1fb18e796788", + "type": "dashboard" + }, + { + "id": "Metricbeat-system-overview-ecs", + "type": "dashboard" + }, + { + "id": "277876d0-fa2c-11e6-bbd3-29c986c96e5a-ecs", + "type": "dashboard" + }, + { + "id": "0d3f2380-fa78-11e6-ae9b-81e5311e8cab-ecs", + "type": "dashboard" + }, + { + "id": "CPU-slash-Memory-per-container-ecs", + "type": "dashboard" + }, + { + "id": "79ffd6e0-faa0-11e6-947f-177f697178b8-ecs", + "type": "dashboard" + }, + { + "id": "Filebeat-syslog-dashboard-ecs", + "type": "dashboard" + }, + { + "id": "5517a150-f9ce-11e6-8115-a7c18106d86a-ecs", + "type": "dashboard" + }, + { + "id": "9c69cad0-f9b0-11e9-90e8-1fb18e796788", + "type": "visualization" + }, + { + "id": "855899e0-1b1c-11e7-b09e-037021c4f8df-ecs", + "type": "visualization" + }, + { + "id": "a30871f0-f98f-11e9-90e8-1fb18e796788", + "type": "visualization" + }, + { + "id": "e121b140-fa78-11e6-a1df-a78bd7504d38-ecs", + "type": "visualization" + }, + { + "id": "f398d2f0-fa77-11e6-ae9b-81e5311e8cab-ecs", + "type": "visualization" + }, + { + "id": "c5e3cf90-4d60-11e7-9a4c-ed99bbcaa42b-ecs", + "type": "visualization" + }, + { + "id": "d3166e80-1b91-11e7-bec4-a5e9ec5cab8b-ecs", + "type": "visualization" + }, + { + "id": "346bb290-fa80-11e6-a1df-a78bd7504d38-ecs", + "type": "visualization" + }, + { + "id": "Container-Block-IO-ecs", + "type": "visualization" + }, + { + "id": "590a60f0-5d87-11e7-8884-1bb4c3b890e4-ecs", + "type": "visualization" + }, + { + "id": "341ffe70-f9ce-11e6-8115-a7c18106d86a-ecs", + "type": "visualization" + }, + { + "id": "System-Navigation-ecs", + "type": "visualization" + }, + { + "id": "089b85d0-1b16-11e7-b09e-037021c4f8df-ecs", + "type": "visualization" + }, + { + "id": "99381c80-4d60-11e7-9a4c-ed99bbcaa42b-ecs", + "type": "visualization" + }, + { + "id": "c6f2ffd0-4d17-11e7-a196-69b9a7a020a9-ecs", + "type": "visualization" + }, + { + "id": "d56ee420-fa79-11e6-a1df-a78bd7504d38-ecs", + "type": "visualization" + }, + { + "id": "1aae9140-1b93-11e7-8ada-3df93aab833e-ecs", + "type": "visualization" + }, + { + "id": "e0f001c0-1b18-11e7-b09e-037021c4f8df-ecs", + "type": "visualization" + }, + { + "id": "dc589770-fa2b-11e6-bbd3-29c986c96e5a-ecs", + "type": "visualization" + }, + { + "id": "96976150-4d5d-11e7-aa29-87a97a796de6-ecs", + "type": "visualization" + }, + { + "id": "8c071e20-f999-11e9-90e8-1fb18e796788", + "type": "visualization" + }, + { + "id": "d3f51850-f9b6-11e9-90e8-1fb18e796788", + "type": "visualization" + }, + { + "id": "5c7af030-fa2a-11e6-bbd3-29c986c96e5a-ecs", + "type": "visualization" + }, + { + "id": "e6e639e0-f992-11e9-90e8-1fb18e796788", + "type": "visualization" + }, + { + "id": "bfa5e400-1b16-11e7-b09e-037021c4f8df-ecs", + "type": "visualization" + }, + { + "id": "7cdb1330-4d1a-11e7-a196-69b9a7a020a9-ecs", + "type": "visualization" + }, + { + "id": "78b74f30-f9cd-11e6-8115-a7c18106d86a-ecs", + "type": "visualization" + }, + { + "id": "Syslog-events-by-hostname-ecs", + "type": "visualization" + }, + { + "id": "3d65d450-a9c3-11e7-af20-67db8aecb295-ecs", + "type": "visualization" + }, + { + "id": "ab2d1e90-1b1a-11e7-b09e-037021c4f8df-ecs", + "type": "visualization" + }, + { + "id": "825fdb80-4d1d-11e7-b5f2-2b7c1895bf32-ecs", + "type": "visualization" + }, + { + "id": "26732e20-1b91-11e7-bec4-a5e9ec5cab8b-ecs", + "type": "visualization" + }, + { + "id": "Syslog-hostnames-and-processes-ecs", + "type": "visualization" + }, + { + "id": "522ee670-1b92-11e7-bec4-a5e9ec5cab8b-ecs", + "type": "visualization" + }, + { + "id": "51164310-fa2b-11e6-bbd3-29c986c96e5a-ecs", + "type": "visualization" + }, + { + "id": "bb3a8720-f991-11e9-90e8-1fb18e796788", + "type": "visualization" + }, + { + "id": "Container-Memory-stats-ecs", + "type": "visualization" + }, + { + "id": "5dd15c00-fa78-11e6-ae9b-81e5311e8cab-ecs", + "type": "visualization" + }, + { + "id": "327417e0-8462-11e7-bab8-bd2f0fb42c54-ecs", + "type": "visualization" + }, + { + "id": "d2e80340-4d5c-11e7-aa29-87a97a796de6-ecs", + "type": "visualization" + }, + { + "id": "19e123b0-4d5a-11e7-aee5-fdc812cc3bec-ecs", + "type": "visualization" + }, + { + "id": "3cec3eb0-f9d3-11e6-8a3e-2b904044ea1d-ecs", + "type": "visualization" + }, + { + "id": "2e224660-1b19-11e7-b09e-037021c4f8df-ecs", + "type": "visualization" + }, + { + "id": "12667040-fa80-11e6-a1df-a78bd7504d38-ecs", + "type": "visualization" + }, + { + "id": "d16bb400-f9cc-11e6-8115-a7c18106d86a-ecs", + "type": "visualization" + }, + { + "id": "34f97ee0-1b96-11e7-8ada-3df93aab833e-ecs", + "type": "visualization" + }, + { + "id": "fe064790-1b1f-11e7-bec4-a5e9ec5cab8b-ecs", + "type": "visualization" + }, + { + "id": "83e12df0-1b91-11e7-bec4-a5e9ec5cab8b-ecs", + "type": "visualization" + }, + { + "id": "4e4bb1e0-1b1b-11e7-b09e-037021c4f8df-ecs", + "type": "visualization" + }, + { + "id": "4b254630-f998-11e9-90e8-1fb18e796788", + "type": "visualization" + }, + { + "id": "6b7b9a40-faa1-11e6-86b1-cd7735ff7e23-ecs", + "type": "visualization" + }, + { + "id": "4d546850-1b15-11e7-b09e-037021c4f8df-ecs", + "type": "visualization" + }, + { + "id": "Container-CPU-usage-ecs", + "type": "visualization" + }, + { + "id": "b6f321e0-fa25-11e6-bbd3-29c986c96e5a-ecs", + "type": "search" + }, + { + "id": "62439dc0-f9c9-11e6-a747-6121780e0414-ecs", + "type": "search" + }, + { + "id": "8030c1b0-fa77-11e6-ae9b-81e5311e8cab-ecs", + "type": "search" + }, + { + "id": "Syslog-system-logs-ecs", + "type": "search" + }, + { + "id": "eb0039f0-fa7f-11e6-a1df-a78bd7504d38-ecs", + "type": "search" + }, + { + "id": "logs-system.auth-0.1.0", + "type": "ingest-pipeline" + }, + { + "id": "logs-system.auth-0.1.0", + "type": "ingest-pipeline" + }, + { + "id": "logs-system.syslog-0.1.0", + "type": "ingest-pipeline" + }, + { + "id": "logs-system.syslog-0.1.0", + "type": "ingest-pipeline" + }, + { + "id": "logs-system.auth", + "type": "index-template" + }, + { + "id": "metrics-system.core", + "type": "index-template" + }, + { + "id": "metrics-system.cpu", + "type": "index-template" + }, + { + "id": "metrics-system.diskio", + "type": "index-template" + }, + { + "id": "metrics-system.entropy", + "type": "index-template" + }, + { + "id": "metrics-system.filesystem", + "type": "index-template" + }, + { + "id": "metrics-system.fsstat", + "type": "index-template" + }, + { + "id": "metrics-system.load", + "type": "index-template" + }, + { + "id": "metrics-system.memory", + "type": "index-template" + }, + { + "id": "metrics-system.network", + "type": "index-template" + }, + { + "id": "metrics-system.network_summary", + "type": "index-template" + }, + { + "id": "metrics-system.process", + "type": "index-template" + }, + { + "id": "metrics-system.process_summary", + "type": "index-template" + }, + { + "id": "metrics-system.raid", + "type": "index-template" + }, + { + "id": "metrics-system.service", + "type": "index-template" + }, + { + "id": "metrics-system.socket", + "type": "index-template" + }, + { + "id": "metrics-system.socket_summary", + "type": "index-template" + }, + { + "id": "logs-system.syslog", + "type": "index-template" + }, + { + "id": "metrics-system.uptime", + "type": "index-template" + }, + { + "id": "metrics-system.users", + "type": "index-template" + } + ], + "es_index_patterns": { + "auth": "logs-system.auth-*", + "core": "metrics-system.core-*", + "cpu": "metrics-system.cpu-*", + "diskio": "metrics-system.diskio-*", + "entropy": "metrics-system.entropy-*", + "filesystem": "metrics-system.filesystem-*", + "fsstat": "metrics-system.fsstat-*", + "load": "metrics-system.load-*", + "memory": "metrics-system.memory-*", + "network": "metrics-system.network-*", + "network_summary": "metrics-system.network_summary-*", + "process": "metrics-system.process-*", + "process_summary": "metrics-system.process_summary-*", + "raid": "metrics-system.raid-*", + "service": "metrics-system.service-*", + "socket": "metrics-system.socket-*", + "socket_summary": "metrics-system.socket_summary-*", + "syslog": "logs-system.syslog-*", + "uptime": "metrics-system.uptime-*", + "users": "metrics-system.users-*" + }, + "name": "system", + "version": "0.1.0", + "internal": false, + "removable": false + }, + "references": [], + "updated_at": "2020-05-15T20:08:08.708Z", + "version": "Wzk4LDFd" + } + }, + { + "description": "This package contains a yaml pipeline.\n", + "download": "/epr/yamlpipeline/yamlpipeline-1.0.0.tar.gz", + "name": "yamlpipeline", + "path": "/package/yamlpipeline/1.0.0", + "title": "Yaml Pipeline package", + "type": "integration", + "version": "1.0.0", + "status": "not_installed" + } + ], + "success": true + } + } + } + } + } + } + }, + "operationId": "get-epm-list" + }, + "parameters": [] + }, + "/epm/categories": { + "get": { + "summary": "EPM - Categories", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "count": { + "type": "number" + } + }, + "required": [ + "id", + "title", + "count" + ] + } + } + } + } + } + }, + "operationId": "get-epm-categories" + } + }, + "/fleet/agents": { + "get": { + "summary": "Fleet - Agent - List", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "type": "object" + } + }, + "success": { + "type": "boolean" + }, + "total": { + "type": "number" + }, + "page": { + "type": "number" + }, + "perPage": { + "type": "number" + } + }, + "required": [ + "list", + "success", + "total", + "page", + "perPage" + ] + }, + "examples": { + "example-1": { + "value": { + "list": [ + { + "id": "205661d0-5e53-11ea-ad31-4f31c06bd9a4", + "active": true, + "config_id": "ae556400-5e39-11ea-8b49-f9747e466f7b", + "type": "PERMANENT", + "enrolled_at": "2020-03-04T20:02:50.605Z", + "user_provided_metadata": { + "dev_agent_version": "0.0.1", + "region": "us-east" + }, + "local_metadata": { + "host": "localhost", + "ip": "127.0.0.1", + "system": "Darwin 18.7.0", + "memory": 34359738368 + }, + "actions": [ + { + "data": "{\"config\":{\"id\":\"ae556400-5e39-11ea-8b49-f9747e466f7b\",\"outputs\":{\"default\":{\"type\":\"elasticsearch\",\"hosts\":[\"http://localhost:9200\"],\"api_key\":\"\",\"api_token\":\"6ckkp3ABz7e_XRqr3LM8:gQuDfUNSRgmY0iziYqP9Hw\"}},\"datasources\":[]}}", + "created_at": "2020-03-04T20:02:56.149Z", + "id": "6a95c00a-d76d-4931-97c3-0bf935272d7d", + "type": "CONFIG_CHANGE" + } + ], + "access_api_key_id": "6Mkkp3ABz7e_XRqrzLNJ", + "default_api_key": "6ckkp3ABz7e_XRqr3LM8:gQuDfUNSRgmY0iziYqP9Hw", + "current_error_events": [], + "last_checkin": "2020-03-04T20:03:05.700Z", + "status": "online" + } + ], + "success": true, + "total": 1, + "page": 1, + "perPage": 20 + } + } + } + } + } + } + }, + "operationId": "get-fleet-agents", + "security": [ + { + "basicAuth": [] + } + ] + } + }, + "/fleet/agents/{agentId}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "agentId", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Fleet - Agent - Info", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "item": { + "type": "object" + }, + "success": { + "type": "string" + } + }, + "required": [ + "item", + "success" + ] + } + } + } + } + }, + "operationId": "get-fleet-agents-agentId" + }, + "put": { + "summary": "Fleet - Agent - Update", + "tags": [], + "responses": {}, + "operationId": "put-fleet-agents-agentId", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + }, + "delete": { + "summary": "Fleet - Agent - Delete", + "tags": [], + "responses": {}, + "operationId": "delete-fleet-agents-agentId", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/fleet/agents/{agentId}/events": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "agentId", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Fleet - Agent - Events", + "tags": [], + "responses": {}, + "operationId": "get-fleet-agents-agentId-events" + } + }, + "/fleet/agents/{agentId}/checkin": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "agentId", + "in": "path", + "required": true + } + ], + "post": { + "summary": "Fleet - Agent - Check In", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": [ + "checkin" + ] + }, + "success": { + "type": "string" + }, + "actions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "agent_id": { + "type": "string" + }, + "data": { + "type": "object" + }, + "id": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "type": { + "type": "string" + } + }, + "required": [ + "agent_id", + "data", + "id", + "created_at", + "type" + ] + } + } + } + }, + "examples": { + "success": { + "value": { + "action": "checkin", + "success": true, + "actions": [ + { + "agent_id": "a6f14bd2-1a2a-481c-9212-9494d064ffdf", + "type": "CONFIG_CHANGE", + "data": { + "config": { + "id": "2fe89350-a5e0-11ea-a587-5f886c8a849f", + "outputs": { + "default": { + "type": "elasticsearch", + "hosts": [ + "http://localhost:9200" + ], + "api_key": "Z-XkgHIBvwtjzIKtSCTh:AejRqdKpQx6z-6dqSI1LHg" + } + }, + "datasources": [ + { + "id": "33d6bd70-a5e0-11ea-a587-5f886c8a849f", + "name": "system-1", + "namespace": "default", + "enabled": true, + "use_output": "default", + "inputs": [ + { + "type": "logs", + "enabled": true, + "streams": [ + { + "id": "logs-system.auth", + "enabled": true, + "dataset": "system.auth", + "paths": [ + "/var/log/auth.log*", + "/var/log/secure*" + ], + "exclude_files": [ + ".gz$" + ], + "multiline": { + "pattern": "^\\s", + "match": "after" + }, + "processors": [ + { + "add_locale": null + }, + { + "add_fields": { + "target": "", + "fields": { + "ecs.version": "1.5.0" + } + } + } + ] + }, + { + "id": "logs-system.syslog", + "enabled": true, + "dataset": "system.syslog", + "paths": [ + "/var/log/messages*", + "/var/log/syslog*" + ], + "exclude_files": [ + ".gz$" + ], + "multiline": { + "pattern": "^\\s", + "match": "after" + }, + "processors": [ + { + "add_locale": null + }, + { + "add_fields": { + "target": "", + "fields": { + "ecs.version": "1.5.0" + } + } + } + ] + } + ] + }, + { + "type": "system/metrics", + "enabled": true, + "streams": [ + { + "id": "system/metrics-system.core", + "enabled": true, + "dataset": "system.core", + "metricsets": [ + "core" + ], + "core.metrics": "percentages" + }, + { + "id": "system/metrics-system.cpu", + "enabled": true, + "dataset": "system.cpu", + "metricsets": [ + "cpu" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + }, + { + "id": "system/metrics-system.diskio", + "enabled": true, + "dataset": "system.diskio", + "metricsets": [ + "diskio" + ] + }, + { + "id": "system/metrics-system.entropy", + "enabled": true, + "dataset": "system.entropy", + "metricsets": [ + "entropy" + ] + }, + { + "id": "system/metrics-system.filesystem", + "enabled": true, + "dataset": "system.filesystem", + "metricsets": [ + "filesystem" + ], + "period": "1m", + "processors": [ + { + "drop_event.when.regexp": { + "system.filesystem.mount_point": "^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)" + } + } + ] + }, + { + "id": "system/metrics-system.fsstat", + "enabled": true, + "dataset": "system.fsstat", + "metricsets": [ + "fsstat" + ], + "period": "1m", + "processors": [ + { + "drop_event.when.regexp": { + "system.filesystem.mount_point": "^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)" + } + } + ] + }, + { + "id": "system/metrics-system.load", + "enabled": true, + "dataset": "system.load", + "metricsets": [ + "load" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + }, + { + "id": "system/metrics-system.memory", + "enabled": true, + "dataset": "system.memory", + "metricsets": [ + "memory" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + }, + { + "id": "system/metrics-system.network", + "enabled": true, + "dataset": "system.network", + "metricsets": [ + "network" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + }, + { + "id": "system/metrics-system.network_summary", + "enabled": true, + "dataset": "system.network_summary", + "metricsets": [ + "network_summary" + ] + }, + { + "id": "system/metrics-system.process", + "enabled": true, + "dataset": "system.process", + "metricsets": [ + "process" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + }, + { + "id": "system/metrics-system.process_summary", + "enabled": true, + "dataset": "system.process_summary", + "metricsets": [ + "process_summary" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + }, + { + "id": "system/metrics-system.raid", + "enabled": true, + "dataset": "system.raid", + "metricsets": [ + "raid" + ] + }, + { + "id": "system/metrics-system.service", + "enabled": true, + "dataset": "system.service", + "metricsets": [ + "service" + ] + }, + { + "id": "system/metrics-system.socket", + "enabled": true, + "dataset": "system.socket", + "metricsets": [ + "socket" + ] + }, + { + "id": "system/metrics-system.socket_summary", + "enabled": true, + "dataset": "system.socket_summary", + "metricsets": [ + "socket_summary" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "process.include_top_n.by_cpu": 5, + "process.include_top_n.by_memory": 5, + "processes": ".*" + }, + { + "id": "system/metrics-system.uptime", + "enabled": true, + "dataset": "system.uptime", + "metricsets": [ + "uptime" + ], + "core.metrics": "percentages", + "cpu.metrics": "percentages,normalized_percentages", + "period": "10s", + "processes": ".*" + }, + { + "id": "system/metrics-system.users", + "enabled": true, + "dataset": "system.users", + "metricsets": [ + "users" + ] + } + ] + } + ], + "package": { + "name": "system", + "version": "0.1.0" + } + }, + { + "id": "fdb1fea0-a5f6-11ea-ad52-534e35d3cd6f", + "name": "endpoint-1", + "namespace": "default", + "enabled": true, + "use_output": "default", + "inputs": [], + "package": { + "name": "endpoint", + "version": "0.2.0" + } + }, + { + "id": "2d792280-a5f7-11ea-ad52-534e35d3cd6f", + "name": "endpoint-2", + "namespace": "default", + "enabled": true, + "use_output": "default", + "inputs": [], + "package": { + "name": "endpoint", + "version": "0.2.0" + } + } + ], + "revision": 4, + "settings": { + "monitoring": { + "use_output": "default", + "enabled": true, + "logs": true, + "metrics": true + } + } + } + }, + "id": "51c6ad1e-a9c0-4c70-80da-99a5c51eedaf", + "created_at": "2020-06-04T19:52:24.667Z" + } + ] + } + } + } + } + } + } + }, + "operationId": "post-fleet-agents-agentId-checkin", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ], + "security": [ + { + "Access API Key": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "local_metadata": { + "$ref": "#/components/schemas/AgentMetadata" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NewAgentEvent" + } + } + } + }, + "examples": { + "stoped to starting": { + "value": { + "events": [ + { + "type": "STATE", + "subtype": "STARTING", + "message": "state changed from STOPPED to STARTING", + "timestamp": "2019-10-01T13:42:54.323Z", + "payload": {}, + "agent_id": "bee40627-8cbd-45df-add9-98c390f9db10" + } + ] + } + }, + "running": { + "value": { + "events": [ + { + "type": "STATE", + "subtype": "RUNNING", + "message": "state changed from STOPPED to RUNNING", + "timestamp": "2020-05-26T20:44:57.480Z", + "payload": { + "random": "data", + "state": "RUNNING", + "previous_state": "STOPPED" + }, + "agent_id": "bee40627-8cbd-45df-add9-98c390f9db10" + } + ] + } + } + } + } + } + } + } + }, + "/fleet/agents/{agentId}/acks": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "agentId", + "in": "path", + "required": true + } + ], + "post": { + "summary": "Fleet - Agent - Acks", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "action": { + "type": "string", + "enum": [ + "acks" + ] + } + }, + "required": [ + "success", + "action" + ] + }, + "examples": { + "success": { + "value": { + "action": "checkin", + "success": true + } + } + } + } + } + } + }, + "operationId": "post-fleet-agents-agentId-acks", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {} + }, + "examples": { + "example-1": { + "value": { + "events": [ + { + "type": "ACTION_RESULT", + "subtype": "CONFIG", + "timestamp": "2019-01-04T14:32:03.36764-05:00", + "action_id": "51c6ad1e-a9c0-4c70-80da-99a5c51eedaf", + "agent_id": "a6f14bd2-1a2a-481c-9212-9494d064ffdf", + "message": "acknowledge" + } + ] + } + } + } + } + } + } + } + }, + "/fleet/agents/enroll": { + "post": { + "summary": "Fleet - Agent - Enroll", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "success": { + "type": "boolean" + }, + "item": { + "$ref": "#/components/schemas/Agent" + } + } + }, + "examples": { + "success": { + "value": { + "action": "created", + "success": true, + "item": { + "id": "8086fb1a-72ca-4a67-8533-09300c1639fa", + "active": true, + "config_id": "2fe89350-a5e0-11ea-a587-5f886c8a849f", + "type": "PERMANENT", + "enrolled_at": "2020-06-04T13:03:57.856Z", + "user_provided_metadata": { + "dev_agent_version": "0.0.1", + "region": "us-east" + }, + "local_metadata": { + "host": "localhost", + "ip": "127.0.0.1", + "system": "Darwin 18.7.0", + "memory": 34359738368 + }, + "current_error_events": [], + "access_api_key": "cU9KdWYzSUJ2d3RqeklLdFdnNF86ZW05ZjFrMThUWW1GRW13OHMwRGZvdw==", + "status": "error" + } + } + } + } + } + } + } + }, + "operationId": "post-fleet-agents-enroll", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "PERMANENT", + "EPHEMERAL", + "TEMPORARY" + ] + }, + "shared_id": { + "type": "string" + }, + "metadata": { + "type": "object", + "required": [ + "local", + "user_provided" + ], + "properties": { + "local": { + "$ref": "#/components/schemas/AgentMetadata" + }, + "user_provided": { + "$ref": "#/components/schemas/AgentMetadata" + } + } + } + }, + "required": [ + "type", + "metadata" + ] + }, + "examples": { + "good": { + "value": { + "type": "PERMANENT", + "metadata": { + "local": { + "host": "localhost", + "ip": "127.0.0.1", + "system": "Darwin 18.7.0", + "memory": 34359738368 + }, + "user_provided": { + "dev_agent_version": "0.0.1", + "region": "us-east" + } + } + } + } + } + } + } + }, + "security": [ + { + "Enrollment API Key": [] + } + ] + } + }, + "/fleet/agents/unenroll": { + "post": { + "summary": "Fleet - Agent - Unenroll", + "tags": [], + "responses": {}, + "operationId": "post-fleet-agents-unenroll", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/fleet/config/{configId}/agent-status": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "configId", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Fleet - Agent - Status for config", + "tags": [], + "responses": {}, + "operationId": "get-fleet-config-configId-agent-status" + } + }, + "/fleet/enrollment-api-keys": { + "get": { + "summary": "Enrollment - List", + "tags": [], + "responses": {}, + "operationId": "get-fleet-enrollment-api-keys", + "parameters": [] + }, + "post": { + "summary": "Enrollment - Create", + "tags": [], + "responses": {}, + "operationId": "post-fleet-enrollment-api-keys", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/fleet/enrollment-api-keys/{keyId}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "keyId", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Enrollment - Info", + "tags": [], + "responses": {}, + "operationId": "get-fleet-enrollment-api-keys-keyId" + }, + "delete": { + "summary": "Enrollment - Delete", + "tags": [], + "responses": {}, + "operationId": "delete-fleet-enrollment-api-keys-keyId", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/setup": { + "post": { + "summary": "Ingest Manager - Setup", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "isInitialized": { + "type": "boolean" + } + } + }, + "examples": { + "success": { + "value": { + "isInitialized": true + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "examples": {} + } + } + } + }, + "operationId": "post-setup", + "parameters": [ + { + "$ref": "#/components/parameters/xsrfHeader" + } + ] + } + }, + "/fleet/install/{osType}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "osType", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Fleet - Get OS install script", + "tags": [], + "responses": {}, + "operationId": "get-fleet-install-osType" + } + } + }, + "components": { + "schemas": { + "AgentConfig": { + "allOf": [ + { + "$ref": "#/components/schemas/NewAgentConfig" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "datasources": { + "oneOf": [ + { + "items": { + "type": "string" + } + }, + { + "items": { + "$ref": "#/components/schemas/Datasource" + } + } + ], + "type": "array" + }, + "updated_on": { + "type": "string", + "format": "date-time" + }, + "updated_by": { + "type": "string" + }, + "revision": { + "type": "number" + }, + "agents": { + "type": "number" + } + }, + "required": [ + "id", + "status" + ] + } + ] + }, + "Datasource": { + "title": "Datasource", + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "revision": { + "type": "number" + }, + "inputs": { + "type": "array", + "items": {} + } + }, + "required": [ + "id", + "revision" + ] + }, + { + "$ref": "#/components/schemas/NewDatasource" + } + ], + "x-examples": { + "example-1": {} + } + }, + "NewAgentConfig": { + "title": "NewAgentConfig", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "NewDatasource": { + "title": "NewDatasource", + "type": "object", + "x-examples": { + "example-1": { + "enabled": true, + "title": "This is a nice title for human", + "package": { + "name": "epm/nginx", + "version": "1.7.0" + }, + "namespace": "prod", + "use_output": "long_term_storage", + "inputs": [ + { + "type": "logs", + "streams": [ + { + "enabled": true, + "dataset": "nginx.acccess", + "paths": [ + "/var/log/nginx/access.log" + ] + }, + { + "enabled": true, + "dataset": "nginx.error", + "paths": [ + "/var/log/nginx/error.log" + ] + } + ] + }, + { + "type": "nginx/metrics", + "streams": [ + { + "id": "id string", + "enabled": true, + "dataset": "nginx.stub_status", + "metricset": "stub_status" + } + ] + } + ] + } + }, + "description": "", + "properties": { + "enabled": { + "type": "boolean" + }, + "package": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "name", + "version", + "title" + ] + }, + "namespace": { + "type": "string" + }, + "output_id": { + "type": "string" + }, + "inputs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "processors": { + "type": "array", + "items": { + "type": "string" + } + }, + "streams": { + "type": "array", + "items": {} + }, + "config": { + "type": "object" + }, + "vars": { + "type": "object" + } + }, + "required": [ + "type", + "enabled", + "streams" + ] + } + }, + "config_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "output_id", + "inputs", + "config_id", + "name" + ] + }, + "PackageInfo": { + "title": "PackageInfo", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "title": { + "type": "string" + }, + "version": { + "type": "string" + }, + "readme": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string" + }, + "categories": { + "type": "array", + "items": { + "type": "string" + } + }, + "requirement": { + "oneOf": [ + { + "properties": { + "kibana": { + "type": "object", + "properties": { + "versions": { + "type": "string" + } + } + } + } + }, + { + "properties": { + "elasticsearch": { + "type": "object", + "properties": { + "versions": { + "type": "string" + } + } + } + } + } + ], + "type": "object" + }, + "screenshots": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "src" + ] + } + }, + "icons": { + "type": "array", + "items": { + "type": "string" + } + }, + "assets": { + "type": "array", + "items": { + "type": "string" + } + }, + "internal": { + "type": "boolean" + }, + "format_version": { + "type": "string" + }, + "datasets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "name": { + "type": "string" + }, + "release": { + "type": "string" + }, + "ingeset_pipeline": { + "type": "string" + }, + "vars": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "default": { + "type": "string" + } + }, + "required": [ + "name", + "default" + ] + } + }, + "type": { + "type": "string" + }, + "package": { + "type": "string" + } + }, + "required": [ + "title", + "name", + "release", + "ingeset_pipeline", + "type", + "package" + ] + } + }, + "download": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "name", + "title", + "version", + "description", + "type", + "categories", + "requirement", + "assets", + "format_version", + "download", + "path" + ] + }, + "SearchResult": { + "title": "SearchResult", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "download": { + "type": "string" + }, + "icons": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string" + }, + "version": { + "type": "string" + }, + "status": { + "type": "string" + }, + "savedObject": { + "type": "object" + } + }, + "required": [ + "description", + "download", + "icons", + "name", + "path", + "title", + "type", + "version", + "status" + ] + }, + "AgentStatus": { + "type": "string", + "title": "AgentStatus", + "enum": [ + "offline", + "error", + "online", + "inactive", + "warning" + ] + }, + "Agent": { + "title": "Agent", + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/AgentType" + }, + "active": { + "type": "boolean" + }, + "enrolled_at": { + "type": "string" + }, + "shared_id": { + "type": "string" + }, + "access_api_key_id": { + "type": "string" + }, + "default_api_key_id": { + "type": "string" + }, + "config_id": { + "type": "string" + }, + "config_revision": { + "type": [ + "number", + "null" + ] + }, + "config_newest_revision": { + "type": "number" + }, + "last_checkin": { + "type": "string" + }, + "user_provided_metadata": { + "$ref": "#/components/schemas/AgentMetadata" + }, + "local_metadata": { + "$ref": "#/components/schemas/AgentMetadata" + }, + "id": { + "type": "string" + }, + "current_error_events": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AgentEvent" + } + }, + "access_api_key": { + "type": "string" + }, + "status": { + "$ref": "#/components/schemas/AgentStatus" + }, + "default_api_key": { + "type": "string" + } + }, + "required": [ + "type", + "active", + "enrolled_at", + "id", + "current_error_events", + "status" + ] + }, + "AgentType": { + "type": "string", + "title": "AgentType", + "enum": [ + "PERMANENT", + "EPHEMERAL", + "TEMPORARY" + ] + }, + "AgentMetadata": { + "title": "AgentMetadata", + "type": "object" + }, + "NewAgentEvent": { + "title": "NewAgentEvent", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "STATE", + "ERROR", + "ACTION_RESULT", + "ACTION" + ] + }, + "subtype": { + "type": "string", + "enum": [ + "RUNNING", + "STARTING", + "IN_PROGRESS", + "CONFIG", + "FAILED", + "STOPPING", + "STOPPED", + "DATA_DUMP", + "ACKNOWLEDGED", + "UNKNOWN" + ] + }, + "timestamp": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "type": "string" + }, + "agent_id": { + "type": "string" + }, + "config_id": { + "type": "string" + }, + "stream_id": { + "type": "string" + }, + "action_id": { + "type": "string" + } + }, + "required": [ + "type", + "subtype", + "timestamp", + "message", + "agent_id" + ] + }, + "AgentEvent": { + "title": "AgentEvent", + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + { + "$ref": "#/components/schemas/NewAgentEvent" + } + ] + }, + "AccessApiKey": { + "type": "string", + "title": "AccessApiKey", + "format": "byte" + }, + "EnrollmentApiKey": { + "type": "string", + "title": "EnrollmentApiKey", + "format": "byte" + } + }, + "parameters": { + "pageSizeParam": { + "name": "perPage", + "in": "query", + "description": "The number of items to return", + "required": false, + "schema": { + "type": "integer", + "default": 50 + } + }, + "pageIndexParam": { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + "kueryParam": { + "name": "kuery", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + "xsrfHeader": { + "schema": { + "type": "string" + }, + "in": "header", + "name": "kbn-xsrf", + "required": true + } + }, + "securitySchemes": { + "basicAuth": { + "type": "http", + "scheme": "basic" + }, + "Enrollment API Key": { + "name": "Authorization", + "type": "apiKey", + "in": "header", + "description": "e.g. Authorization: ApiKey base64EnrollmentApiKey" + }, + "Access API Key": { + "name": "Authorization", + "type": "apiKey", + "in": "header", + "description": "e.g. Authorization: ApiKey base64AccessApiKey" + } + } + }, + "security": [ + { + "basicAuth": [] + } + ] +} diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index d357f11f5e3e1..ad99780a7d32f 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -99,6 +99,9 @@ export enum ES_GEO_FIELD_TYPE { GEO_SHAPE = 'geo_shape', } +// Using strings instead of ES_GEO_FIELD_TYPE enum to avoid typeing errors where IFieldType.type is compared to value +export const ES_GEO_FIELD_TYPES = ['geo_point', 'geo_shape']; + export enum ES_SPATIAL_RELATIONS { INTERSECTS = 'INTERSECTS', DISJOINT = 'DISJOINT', diff --git a/x-pack/plugins/maps/public/classes/fields/ems_file_field.ts b/x-pack/plugins/maps/public/classes/fields/ems_file_field.ts index 73d6c1ef9f790..2e9c5c9fe60c2 100644 --- a/x-pack/plugins/maps/public/classes/fields/ems_file_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/ems_file_field.ts @@ -30,12 +30,6 @@ export class EMSFileField extends AbstractField implements IField { } async getLabel(): Promise { - const emsFileLayer = await this._source.getEMSFileLayer(); - // TODO remove any and @ts-ignore when emsFileLayer type defined - // @ts-ignore - const emsFields: any[] = emsFileLayer.getFieldsInLanguage(); - // Map EMS field name to language specific label - const emsField = emsFields.find((field) => field.name === this.getName()); - return emsField ? emsField.description : this.getName(); + return this._source.getEmsFieldLabel(this.getName()); } } diff --git a/x-pack/plugins/maps/public/classes/sources/ems_file_source/create_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/ems_file_source/create_source_editor.tsx index e398af4acea3b..a78a49032503b 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_file_source/create_source_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_file_source/create_source_editor.tsx @@ -8,8 +8,8 @@ import React, { Component } from 'react'; import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -// @ts-ignore -import { getEMSClient } from '../../../meta'; +import { FileLayer } from '@elastic/ems-client'; +import { getEmsFileLayers } from '../../../meta'; import { getEmsUnavailableMessage } from '../ems_unavailable_message'; import { EMSFileSourceDescriptor } from '../../../../common/descriptor_types'; @@ -33,15 +33,10 @@ export class EMSFileCreateSourceEditor extends Component { }; _loadFileOptions = async () => { - // @ts-ignore - const emsClient = getEMSClient(); - // @ts-ignore - const fileLayers: unknown[] = await emsClient.getFileLayers(); + const fileLayers: FileLayer[] = await getEmsFileLayers(); const options = fileLayers.map((fileLayer) => { return { - // @ts-ignore value: fileLayer.getId(), - // @ts-ignore label: fileLayer.getDisplayName(), }; }); diff --git a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.test.tsx b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.test.tsx index 24c111a72ac05..52524d0c9a5fa 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.test.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.test.tsx @@ -11,17 +11,8 @@ jest.mock('../../layers/vector_layer/vector_layer', () => {}); function makeEMSFileSource(tooltipProperties: string[]) { const emsFileSource = new EMSFileSource({ tooltipProperties }); - emsFileSource.getEMSFileLayer = async () => { - return { - getFieldsInLanguage() { - return [ - { - name: 'iso2', - description: 'ISO 2 CODE', - }, - ]; - }, - }; + emsFileSource.getEmsFieldLabel = async (name: string) => { + return name === 'iso2' ? 'ISO 2 CODE' : name; }; return emsFileSource; } diff --git a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx index 94f5bb0d2ba07..f7fb0078764c4 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx @@ -8,12 +8,12 @@ import React, { ReactElement } from 'react'; import { i18n } from '@kbn/i18n'; import { Feature } from 'geojson'; import { Adapters } from 'src/plugins/inspector/public'; +import { FileLayer } from '@elastic/ems-client'; import { Attribution, ImmutableSourceProperty, SourceEditorArgs } from '../source'; import { AbstractVectorSource, GeoJsonWithMeta, IVectorSource } from '../vector_source'; import { VECTOR_SHAPE_TYPES } from '../vector_feature_types'; import { SOURCE_TYPES, FIELD_ORIGIN } from '../../../../common/constants'; -// @ts-ignore -import { getEMSClient } from '../../../meta'; +import { getEmsFileLayers } from '../../../meta'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { UpdateSourceEditor } from './update_source_editor'; import { EMSFileField } from '../../fields/ems_file_field'; @@ -23,7 +23,7 @@ import { EMSFileSourceDescriptor } from '../../../../common/descriptor_types'; import { ITooltipProperty } from '../../tooltips/tooltip_property'; export interface IEmsFileSource extends IVectorSource { - getEMSFileLayer(): Promise; + getEmsFieldLabel(emsFieldName: string): Promise; createField({ fieldName }: { fieldName: string }): IField; } @@ -72,13 +72,9 @@ export class EMSFileSource extends AbstractVectorSource implements IEmsFileSourc ); } - async getEMSFileLayer(): Promise { - // @ts-ignore - const emsClient = getEMSClient(); - // @ts-ignore - const emsFileLayers = await emsClient.getFileLayers(); + async getEMSFileLayer(): Promise { + const emsFileLayers = await getEmsFileLayers(); const emsFileLayer = emsFileLayers.find( - // @ts-ignore (fileLayer) => fileLayer.getId() === this._descriptor.id ); if (!emsFileLayer) { @@ -94,19 +90,25 @@ export class EMSFileSource extends AbstractVectorSource implements IEmsFileSourc return emsFileLayer; } + // Map EMS field name to language specific label + async getEmsFieldLabel(emsFieldName: string): Promise { + const emsFileLayer = await this.getEMSFileLayer(); + const emsFields = emsFileLayer.getFieldsInLanguage(); + + const emsField = emsFields.find((field) => field.name === emsFieldName); + return emsField ? emsField.description : emsFieldName; + } + async getGeoJsonWithMeta(): Promise { const emsFileLayer = await this.getEMSFileLayer(); // @ts-ignore const featureCollection = await AbstractVectorSource.getGeoJson({ - // @ts-ignore format: emsFileLayer.getDefaultFormatType(), featureCollectionPath: 'data', - // @ts-ignore fetchUrl: emsFileLayer.getDefaultFormatUrl(), }); - // @ts-ignore - const emsIdField = emsFileLayer._config.fields.find((field) => { + const emsIdField = emsFileLayer.getFields().find((field) => { return field.type === 'id'; }); featureCollection.features.forEach((feature: Feature, index: number) => { @@ -123,7 +125,6 @@ export class EMSFileSource extends AbstractVectorSource implements IEmsFileSourc let emsLink; try { const emsFileLayer = await this.getEMSFileLayer(); - // @ts-ignore emsLink = emsFileLayer.getEMSHotLink(); } catch (error) { // ignore error if EMS layer id could not be found @@ -147,7 +148,6 @@ export class EMSFileSource extends AbstractVectorSource implements IEmsFileSourc async getDisplayName(): Promise { try { const emsFileLayer = await this.getEMSFileLayer(); - // @ts-ignore return emsFileLayer.getDisplayName(); } catch (error) { return this._descriptor.id; @@ -156,15 +156,12 @@ export class EMSFileSource extends AbstractVectorSource implements IEmsFileSourc async getAttributions(): Promise { const emsFileLayer = await this.getEMSFileLayer(); - // @ts-ignore return emsFileLayer.getAttributions(); } async getLeftJoinFields() { const emsFileLayer = await this.getEMSFileLayer(); - // @ts-ignore const fields = emsFileLayer.getFieldsInLanguage(); - // @ts-ignore return fields.map((f) => this.createField({ fieldName: f.name })); } diff --git a/x-pack/plugins/maps/public/classes/sources/ems_file_source/update_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/ems_file_source/update_source_editor.tsx index daeb1f8bc6b2b..ac69505a9bed5 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_file_source/update_source_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_file_source/update_source_editor.tsx @@ -8,8 +8,7 @@ import React, { Component, Fragment } from 'react'; import { EuiTitle, EuiPanel, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { TooltipSelector } from '../../../components/tooltip_selector'; -// @ts-ignore -import { getEMSClient } from '../../../meta'; +import { getEmsFileLayers } from '../../../meta'; import { IEmsFileSource } from './ems_file_source'; import { IField } from '../../fields/field'; import { OnSourceChangeArgs } from '../../../connected_components/layer_panel/view'; @@ -42,22 +41,18 @@ export class UpdateSourceEditor extends Component { } async loadFields() { - let fields; + let fields: IField[] = []; try { - // @ts-ignore - const emsClient = getEMSClient(); - // @ts-ignore - const emsFiles = await emsClient.getFileLayers(); - // @ts-ignore - const taregetEmsFile = emsFiles.find((emsFile) => emsFile.getId() === this.props.layerId); - // @ts-ignore - const emsFields = taregetEmsFile.getFieldsInLanguage(); - // @ts-ignore - fields = emsFields.map((field) => this.props.source.createField({ fieldName: field.name })); + const emsFiles = await getEmsFileLayers(); + const targetEmsFile = emsFiles.find((emsFile) => emsFile.getId() === this.props.layerId); + if (targetEmsFile) { + fields = targetEmsFile + .getFieldsInLanguage() + .map((field) => this.props.source.createField({ fieldName: field.name })); + } } catch (e) { // When a matching EMS-config cannot be found, the source already will have thrown errors during the data request. // This will propagate to the vector-layer and be displayed in the UX - fields = []; } if (this._isMounted) { diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js index 36c9e424a8b22..83c87eb53d4fe 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js @@ -7,7 +7,7 @@ import _ from 'lodash'; import React from 'react'; import { AbstractTMSSource } from '../tms_source'; -import { getEMSClient } from '../../../meta'; +import { getEmsTmsServices } from '../../../meta'; import { UpdateSourceEditor } from './update_source_editor'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; @@ -66,8 +66,7 @@ export class EMSTMSSource extends AbstractTMSSource { } async _getEMSTMSService() { - const emsClient = getEMSClient(); - const emsTMSServices = await emsClient.getTMSServices(); + const emsTMSServices = await getEmsTmsServices(); const emsTileLayerId = this.getTileLayerId(); const tmsService = emsTMSServices.find((tmsService) => tmsService.getId() === emsTileLayerId); if (!tmsService) { diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js index 08c54299d721b..2f466add28262 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js @@ -6,7 +6,7 @@ jest.mock('../../../meta', () => { return { - getEMSClient: () => { + getEmsTmsServices: () => { class MockTMSService { constructor(config) { this._config = config; @@ -19,20 +19,16 @@ jest.mock('../../../meta', () => { } } - return { - async getTMSServices() { - return [ - new MockTMSService({ - id: 'road_map', - attributionMarkdown: '[foobar](http://foobar.org) | [foobaz](http://foobaz.org)', - }), - new MockTMSService({ - id: 'satellite', - attributionMarkdown: '[satellite](http://satellite.org)', - }), - ]; - }, - }; + return [ + new MockTMSService({ + id: 'road_map', + attributionMarkdown: '[foobar](http://foobar.org) | [foobaz](http://foobaz.org)', + }), + new MockTMSService({ + id: 'satellite', + attributionMarkdown: '[satellite](http://satellite.org)', + }), + ]; }, }; }); diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.js index 4d5d6655609c1..3931e441ff254 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.js +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.js @@ -7,7 +7,7 @@ import React from 'react'; import { EuiSelect, EuiFormRow } from '@elastic/eui'; -import { getEMSClient } from '../../../meta'; +import { getEmsTmsServices } from '../../../meta'; import { getEmsUnavailableMessage } from '../ems_unavailable_message'; import { i18n } from '@kbn/i18n'; @@ -29,8 +29,7 @@ export class TileServiceSelect extends React.Component { } _loadTmsOptions = async () => { - const emsClient = getEMSClient(); - const emsTMSServices = await emsClient.getTMSServices(); + const emsTMSServices = await getEmsTmsServices(); if (!this._isMounted) { return; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js index 91dcb057dd837..24edf0251c153 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js @@ -8,15 +8,25 @@ import _ from 'lodash'; import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; +import { ES_GEO_FIELD_TYPES } from '../../../../common/constants'; import { SingleFieldSelect } from '../../../components/single_field_select'; import { getIndexPatternService, getIndexPatternSelectComponent } from '../../../kibana_services'; import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSpacer } from '@elastic/eui'; -import { getAggregatableGeoFieldTypes, getFieldsWithGeoTileAgg } from '../../../index_pattern_util'; +import { + getFieldsWithGeoTileAgg, + getGeoFields, + getGeoTileAggNotSupportedReason, + supportsGeoTileAgg, +} from '../../../index_pattern_util'; import { RenderAsSelect } from './render_as_select'; +function doesNotSupportGeoTileAgg(field) { + return !supportsGeoTileAgg(field); +} + export class CreateSourceEditor extends Component { static propTypes = { onSourceConfigChange: PropTypes.func.isRequired, @@ -87,9 +97,9 @@ export class CreateSourceEditor extends Component { }); //make default selection - const geoFields = getFieldsWithGeoTileAgg(indexPattern.fields); - if (geoFields[0]) { - this._onGeoFieldSelect(geoFields[0].name); + const geoFieldsWithGeoTileAgg = getFieldsWithGeoTileAgg(indexPattern.fields); + if (geoFieldsWithGeoTileAgg[0]) { + this._onGeoFieldSelect(geoFieldsWithGeoTileAgg[0].name); } }, 300); @@ -141,10 +151,10 @@ export class CreateSourceEditor extends Component { value={this.state.geoField} onChange={this._onGeoFieldSelect} fields={ - this.state.indexPattern - ? getFieldsWithGeoTileAgg(this.state.indexPattern.fields) - : undefined + this.state.indexPattern ? getGeoFields(this.state.indexPattern.fields) : undefined } + isFieldDisabled={doesNotSupportGeoTileAgg} + getFieldDisabledReason={getGeoTileAggNotSupportedReason} /> ); @@ -176,7 +186,7 @@ export class CreateSourceEditor extends Component { placeholder={i18n.translate('xpack.maps.source.esGeoGrid.indexPatternPlaceholder', { defaultMessage: 'Select index pattern', })} - fieldTypes={getAggregatableGeoFieldTypes()} + fieldTypes={ES_GEO_FIELD_TYPES} onNoIndexPatterns={this._onNoIndexPatterns} /> diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap index 967225d6f0fdc..2b04da9251756 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should not render clusters option when clustering is not supported 1`] = ` +exports[`should disable clusters option when clustering is not supported 1`] = ` - +
+ + + + + +
- +
+ + + +
- +
+ + + +
{ - return ( - !indexPatterns.isNestedField(field) && - [ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE].includes(field.type) - ); - }); -} +import { + getGeoFields, + getTermsFields, + getGeoTileAggNotSupportedReason, + supportsGeoTileAgg, +} from '../../../index_pattern_util'; function doesGeoFieldSupportGeoTileAgg(indexPattern, geoFieldName) { return indexPattern ? supportsGeoTileAgg(indexPattern.fields.getByName(geoFieldName)) : false; @@ -217,6 +212,13 @@ export class CreateSourceEditor extends Component { this.state.indexPattern, this.state.geoFieldName )} + clusteringDisabledReason={ + this.state.indexPattern + ? getGeoTileAggNotSupportedReason( + this.state.indexPattern.fields.getByName(this.state.geoFieldName) + ) + : null + } termFields={getTermsFields(this.state.indexPattern.fields)} topHitsSplitField={this.state.topHitsSplitField} topHitsSize={this.state.topHitsSize} @@ -260,7 +262,7 @@ export class CreateSourceEditor extends Component { defaultMessage: 'Select index pattern', } )} - fieldTypes={[ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE]} + fieldTypes={ES_GEO_FIELD_TYPES} onNoIndexPatterns={this._onNoIndexPatterns} /> diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.test.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.test.tsx index 03f29685891ec..3ec746223c7cf 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.test.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.test.tsx @@ -34,8 +34,14 @@ test('should render', async () => { expect(component).toMatchSnapshot(); }); -test('should not render clusters option when clustering is not supported', async () => { - const component = shallow(); +test('should disable clusters option when clustering is not supported', async () => { + const component = shallow( + + ); expect(component).toMatchSnapshot(); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx index 829c9a1ce439d..a998fe3569835 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx @@ -12,11 +12,11 @@ import { EuiTitle, EuiSpacer, EuiHorizontalRule, - EuiRadioGroup, + EuiRadio, + EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -// @ts-ignore import { SingleFieldSelect } from '../../../components/single_field_select'; import { getIndexPatternService } from '../../../kibana_services'; // @ts-ignore @@ -38,6 +38,7 @@ interface Props { onChange: (args: OnSourceChangeArgs) => void; scalingType: SCALING_TYPES; supportsClustering: boolean; + clusteringDisabledReason?: string | null; termFields: IFieldType[]; topHitsSplitField?: string; topHitsSize: number; @@ -88,7 +89,7 @@ export class ScalingForm extends Component { this.props.onChange({ propName: 'filterByMapBounds', value: event.target.checked }); }; - _onTopHitsSplitFieldChange = (topHitsSplitField: string) => { + _onTopHitsSplitFieldChange = (topHitsSplitField?: string) => { this.props.onChange({ propName: 'topHitsSplitField', value: topHitsSplitField }); }; @@ -149,32 +150,30 @@ export class ScalingForm extends Component { ); } - render() { - const scalingOptions = [ - { - id: SCALING_TYPES.LIMIT, - label: i18n.translate('xpack.maps.source.esSearch.limitScalingLabel', { - defaultMessage: 'Limit results to {maxResultWindow}.', - values: { maxResultWindow: this.state.maxResultWindow }, - }), - }, - { - id: SCALING_TYPES.TOP_HITS, - label: i18n.translate('xpack.maps.source.esSearch.useTopHitsLabel', { - defaultMessage: 'Show top hits per entity.', - }), - }, - ]; - if (this.props.supportsClustering) { - scalingOptions.push({ - id: SCALING_TYPES.CLUSTERS, - label: i18n.translate('xpack.maps.source.esSearch.clusterScalingLabel', { + _renderClusteringRadio() { + const clusteringRadio = ( + this._onScalingTypeChange(SCALING_TYPES.CLUSTERS)} + disabled={!this.props.supportsClustering} + /> + ); + return this.props.clusteringDisabledReason ? ( + + {clusteringRadio} + + ) : ( + clusteringRadio + ); + } + + render() { let filterByBoundsSwitch; if (this.props.scalingType !== SCALING_TYPES.CLUSTERS) { filterByBoundsSwitch = ( @@ -212,11 +211,26 @@ export class ScalingForm extends Component { - +
+ this._onScalingTypeChange(SCALING_TYPES.LIMIT)} + /> + this._onScalingTypeChange(SCALING_TYPES.TOP_HITS)} + /> + {this._renderClusteringRadio()} +
{filterByBoundsSwitch} diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/update_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_search_source/update_source_editor.js index 95e48c9629f57..0701dbbaecdd5 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/update_source_editor.js @@ -12,7 +12,12 @@ import { TooltipSelector } from '../../../components/tooltip_selector'; import { getIndexPatternService } from '../../../kibana_services'; import { i18n } from '@kbn/i18n'; -import { getTermsFields, getSourceFields, supportsGeoTileAgg } from '../../../index_pattern_util'; +import { + getGeoTileAggNotSupportedReason, + getTermsFields, + getSourceFields, + supportsGeoTileAgg, +} from '../../../index_pattern_util'; import { SORT_ORDER } from '../../../../common/constants'; import { ESDocField } from '../../fields/es_doc_field'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -91,6 +96,7 @@ export class UpdateSourceEditor extends Component { this.setState({ supportsClustering: supportsGeoTileAgg(geoField), + clusteringDisabledReason: getGeoTileAggNotSupportedReason(geoField), sourceFields: sourceFields, termFields: getTermsFields(indexPattern.fields), //todo change term fields to use fields sortFields: indexPattern.fields.filter( @@ -201,6 +207,7 @@ export class UpdateSourceEditor extends Component { onChange={this.props.onChange} scalingType={this.props.scalingType} supportsClustering={this.state.supportsClustering} + clusteringDisabledReason={this.state.clusteringDisabledReason} termFields={this.state.termFields} topHitsSplitField={this.props.topHitsSplitField} topHitsSize={this.props.topHitsSize} diff --git a/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_layer_wizard.tsx index b778dc0076459..ca78aaefe404f 100644 --- a/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_layer_wizard.tsx @@ -12,13 +12,12 @@ import { KibanaRegionmapSource, sourceTitle } from './kibana_regionmap_source'; import { VectorLayer } from '../../layers/vector_layer/vector_layer'; // @ts-ignore import { CreateSourceEditor } from './create_source_editor'; -// @ts-ignore import { getKibanaRegionList } from '../../../meta'; export const kibanaRegionMapLayerWizardConfig: LayerWizard = { - checkVisibility: () => { + checkVisibility: async () => { const regions = getKibanaRegionList(); - return regions.length; + return regions.length > 0; }, description: i18n.translate('xpack.maps.source.kbnRegionMapDescription', { defaultMessage: 'Vector data from hosted GeoJSON configured in kibana.yml', diff --git a/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx index 227c0182b98de..84d2e5e74fa9a 100644 --- a/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx @@ -12,12 +12,12 @@ import { CreateSourceEditor } from './create_source_editor'; // @ts-ignore import { KibanaTilemapSource, sourceTitle } from './kibana_tilemap_source'; import { TileLayer } from '../../layers/tile_layer/tile_layer'; -// @ts-ignore import { getKibanaTileMap } from '../../../meta'; export const kibanaBasemapLayerWizardConfig: LayerWizard = { checkVisibility: async () => { const tilemap = getKibanaTileMap(); + // @ts-ignore return !!tilemap.url; }, description: i18n.translate('xpack.maps.source.kbnTMSDescription', { diff --git a/x-pack/plugins/maps/public/components/single_field_select.js b/x-pack/plugins/maps/public/components/single_field_select.js deleted file mode 100644 index a4db361da9c62..0000000000000 --- a/x-pack/plugins/maps/public/components/single_field_select.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import { EuiComboBox, EuiHighlight, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { FieldIcon } from '../../../../../src/plugins/kibana_react/public'; - -function fieldsToOptions(fields) { - if (!fields) { - return []; - } - - return fields - .map((field) => { - return { - value: field, - label: 'label' in field ? field.label : field.name, - }; - }) - .sort((a, b) => { - return a.label.toLowerCase().localeCompare(b.label.toLowerCase()); - }); -} - -function renderOption(option, searchValue, contentClassName) { - return ( - - - - - - {option.label} - - - ); -} - -export function SingleFieldSelect({ fields, onChange, value, placeholder, ...rest }) { - const onSelection = (selectedOptions) => { - onChange(_.get(selectedOptions, '0.value.name')); - }; - - return ( - - ); -} - -SingleFieldSelect.propTypes = { - placeholder: PropTypes.string, - fields: PropTypes.array, - onChange: PropTypes.func.isRequired, - value: PropTypes.string, // fieldName -}; diff --git a/x-pack/plugins/maps/public/components/single_field_select.tsx b/x-pack/plugins/maps/public/components/single_field_select.tsx new file mode 100644 index 0000000000000..eb3a28be0efc0 --- /dev/null +++ b/x-pack/plugins/maps/public/components/single_field_select.tsx @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import React from 'react'; + +import { + EuiComboBox, + EuiComboBoxProps, + EuiComboBoxOptionOption, + EuiHighlight, + EuiFlexGroup, + EuiFlexItem, + EuiToolTip, +} from '@elastic/eui'; +import { IFieldType } from 'src/plugins/data/public'; +import { FieldIcon } from '../../../../../src/plugins/kibana_react/public'; + +function fieldsToOptions( + fields?: IFieldType[], + isFieldDisabled?: (field: IFieldType) => boolean +): Array> { + if (!fields) { + return []; + } + + return fields + .map((field) => { + const option: EuiComboBoxOptionOption = { + value: field, + label: field.name, + }; + if (isFieldDisabled && isFieldDisabled(field)) { + option.disabled = true; + } + return option; + }) + .sort((a, b) => { + return a.label.toLowerCase().localeCompare(b.label.toLowerCase()); + }); +} + +type Props = Omit< + EuiComboBoxProps, + 'isDisabled' | 'onChange' | 'options' | 'renderOption' | 'selectedOptions' | 'singleSelection' +> & { + fields?: IFieldType[]; + onChange: (fieldName?: string) => void; + value?: string; // index pattern field name + isFieldDisabled?: (field: IFieldType) => boolean; + getFieldDisabledReason?: (field: IFieldType) => string | null; +}; + +export function SingleFieldSelect({ + fields, + getFieldDisabledReason, + isFieldDisabled, + onChange, + value, + ...rest +}: Props) { + function renderOption( + option: EuiComboBoxOptionOption, + searchValue: string, + contentClassName: string + ) { + const content = ( + + + + + + {option.label} + + + ); + + const disabledReason = + option.disabled && getFieldDisabledReason ? getFieldDisabledReason(option.value!) : null; + + return disabledReason ? ( + + {content} + + ) : ( + content + ); + } + + const onSelection = (selectedOptions: Array>) => { + onChange(_.get(selectedOptions, '0.value.name')); + }; + + const selectedOptions: Array> = []; + if (value && fields) { + const selectedField = fields.find((field: IFieldType) => { + return field.name === value; + }); + if (selectedField) { + selectedOptions.push({ value: selectedField, label: value }); + } + } + + return ( + + ); +} diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 2da3205730638..b27f66ea085de 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -12,15 +12,10 @@ import 'mapbox-gl/dist/mapbox-gl.css'; import { Subscription } from 'rxjs'; import { Unsubscribe } from 'redux'; import { EuiLoadingSpinner } from '@elastic/eui'; -import { - Embeddable, - IContainer, - EmbeddableOutput, -} from '../../../../../src/plugins/embeddable/public'; +import { Embeddable, IContainer } from '../../../../../src/plugins/embeddable/public'; import { APPLY_FILTER_TRIGGER } from '../../../../../src/plugins/ui_actions/public'; import { esFilters, - IIndexPattern, TimeRange, Filter, Query, @@ -57,13 +52,9 @@ import { RenderToolTipContent } from '../classes/tooltips/tooltip_property'; import { getUiActions, getCoreI18n } from '../kibana_services'; import { LayerDescriptor } from '../../common/descriptor_types'; -import { MapEmbeddableInput, MapEmbeddableConfig } from './types'; +import { MapEmbeddableConfig, MapEmbeddableInput, MapEmbeddableOutput } from './types'; export { MapEmbeddableInput, MapEmbeddableConfig }; -export interface MapEmbeddableOutput extends EmbeddableOutput { - indexPatterns: IIndexPattern[]; -} - const GisMap = lazy(() => import('../connected_components/gis_map')); export class MapEmbeddable extends Embeddable { type = MAP_SAVED_OBJECT_TYPE; diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts index c6103bcc73fc5..c73225fc4285b 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -6,71 +6,16 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import { AnyAction } from 'redux'; import { IIndexPattern } from 'src/plugins/data/public'; import { - Embeddable, EmbeddableFactoryDefinition, IContainer, } from '../../../../../src/plugins/embeddable/public'; import '../index.scss'; import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants'; import { LayerDescriptor } from '../../common/descriptor_types'; -import { MapStore, MapStoreState } from '../reducers/store'; -import { MapEmbeddableConfig, MapEmbeddableInput } from './types'; -import { MapEmbeddableOutput } from './map_embeddable'; -import { RenderToolTipContent } from '../classes/tooltips/tooltip_property'; -import { EventHandlers } from '../reducers/non_serializable_instances'; - -let whenModulesLoadedPromise: Promise; - -let getMapsSavedObjectLoader: any; -let MapEmbeddable: new ( - config: MapEmbeddableConfig, - initialInput: MapEmbeddableInput, - parent?: IContainer, - renderTooltipContent?: RenderToolTipContent, - eventHandlers?: EventHandlers -) => Embeddable; - -let getIndexPatternService: () => { - get: (id: string) => IIndexPattern | undefined; -}; -let getHttp: () => any; -let getMapsCapabilities: () => any; -let createMapStore: () => MapStore; -let addLayerWithoutDataSync: (layerDescriptor: LayerDescriptor) => AnyAction; -let getQueryableUniqueIndexPatternIds: (state: MapStoreState) => string[]; -let getInitialLayers: ( - layerListJSON?: string, - initialLayers?: LayerDescriptor[] -) => LayerDescriptor[]; -let mergeInputWithSavedMap: any; - -async function waitForMapDependencies(): Promise { - if (typeof whenModulesLoadedPromise !== 'undefined') { - return whenModulesLoadedPromise; - } - - whenModulesLoadedPromise = new Promise(async (resolve) => { - ({ - // @ts-ignore - getMapsSavedObjectLoader, - getQueryableUniqueIndexPatternIds, - MapEmbeddable, - getIndexPatternService, - getHttp, - getMapsCapabilities, - createMapStore, - addLayerWithoutDataSync, - getInitialLayers, - mergeInputWithSavedMap, - } = await import('./lazy')); - - resolve(true); - }); - return whenModulesLoadedPromise; -} +import { MapEmbeddableInput } from './types'; +import { lazyLoadMapModules } from '../lazy_load_bundle'; export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { type = MAP_SAVED_OBJECT_TYPE; @@ -83,7 +28,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { }; async isEditable() { - await waitForMapDependencies(); + const { getMapsCapabilities } = await lazyLoadMapModules(); return getMapsCapabilities().save as boolean; } @@ -100,6 +45,12 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { async _getIndexPatterns(layerList: LayerDescriptor[]): Promise { // Need to extract layerList from store to get queryable index pattern ids + const { + addLayerWithoutDataSync, + createMapStore, + getIndexPatternService, + getQueryableUniqueIndexPatternIds, + } = await lazyLoadMapModules(); const store = createMapStore(); let queryableIndexPatternIds: string[]; try { @@ -130,6 +81,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { } async _fetchSavedMap(savedObjectId: string) { + const { getMapsSavedObjectLoader } = await lazyLoadMapModules(); const savedObjectLoader = getMapsSavedObjectLoader(); return await savedObjectLoader.get(savedObjectId); } @@ -139,7 +91,12 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { input: MapEmbeddableInput, parent?: IContainer ) => { - await waitForMapDependencies(); + const { + getInitialLayers, + getHttp, + MapEmbeddable, + mergeInputWithSavedMap, + } = await lazyLoadMapModules(); const savedMap = await this._fetchSavedMap(savedObjectId); const layerList = getInitialLayers(savedMap.layerListJSON); const indexPatterns = await this._getIndexPatterns(layerList); @@ -179,7 +136,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { }; create = async (input: MapEmbeddableInput, parent?: IContainer) => { - await waitForMapDependencies(); + const { getInitialLayers, MapEmbeddable } = await lazyLoadMapModules(); const layerList = getInitialLayers(); const indexPatterns = await this._getIndexPatterns(layerList); diff --git a/x-pack/plugins/maps/public/embeddable/types.ts b/x-pack/plugins/maps/public/embeddable/types.ts index fc614a4a6040c..9120f891453de 100644 --- a/x-pack/plugins/maps/public/embeddable/types.ts +++ b/x-pack/plugins/maps/public/embeddable/types.ts @@ -6,8 +6,11 @@ import { IIndexPattern } from '../../../../../src/plugins/data/common/index_patterns'; import { MapSettings } from '../reducers/map'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { EmbeddableInput } from '../../../../../src/plugins/embeddable/public/lib/embeddables'; +import { + EmbeddableInput, + EmbeddableOutput, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../src/plugins/embeddable/public/lib/embeddables'; import { Filter, Query, RefreshInterval, TimeRange } from '../../../../../src/plugins/data/common'; import { LayerDescriptor, MapCenterAndZoom } from '../../common/descriptor_types'; @@ -36,3 +39,7 @@ export interface MapEmbeddableInput extends EmbeddableInput { hiddenLayers?: string[]; hideFilterActions?: boolean; } + +export interface MapEmbeddableOutput extends EmbeddableOutput { + indexPatterns: IIndexPattern[]; +} diff --git a/x-pack/plugins/maps/public/index_pattern_util.js b/x-pack/plugins/maps/public/index_pattern_util.js deleted file mode 100644 index 514feeaa22072..0000000000000 --- a/x-pack/plugins/maps/public/index_pattern_util.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getIndexPatternService, getIsGoldPlus } from './kibana_services'; -import { indexPatterns } from '../../../../src/plugins/data/public'; -import { ES_GEO_FIELD_TYPE } from '../common/constants'; - -export async function getIndexPatternsFromIds(indexPatternIds = []) { - const promises = []; - indexPatternIds.forEach((id) => { - const indexPatternPromise = getIndexPatternService().get(id); - if (indexPatternPromise) { - promises.push(indexPatternPromise); - } - }); - - return await Promise.all(promises); -} - -export function getTermsFields(fields) { - return fields.filter((field) => { - return ( - field.aggregatable && - !indexPatterns.isNestedField(field) && - ['number', 'boolean', 'date', 'ip', 'string'].includes(field.type) - ); - }); -} - -export function getAggregatableGeoFieldTypes() { - const aggregatableFieldTypes = [ES_GEO_FIELD_TYPE.GEO_POINT]; - if (getIsGoldPlus()) { - aggregatableFieldTypes.push(ES_GEO_FIELD_TYPE.GEO_SHAPE); - } - return aggregatableFieldTypes; -} - -export function getFieldsWithGeoTileAgg(fields) { - return fields.filter(supportsGeoTileAgg); -} - -export function supportsGeoTileAgg(field) { - return ( - field && - field.aggregatable && - !indexPatterns.isNestedField(field) && - getAggregatableGeoFieldTypes().includes(field.type) - ); -} - -// Returns filtered fields list containing only fields that exist in _source. -export function getSourceFields(fields) { - return fields.filter((field) => { - // Multi fields are not stored in _source and only exist in index. - const isMultiField = field.subType && field.subType.multi; - return !isMultiField && !indexPatterns.isNestedField(field); - }); -} diff --git a/x-pack/plugins/maps/public/index_pattern_util.test.js b/x-pack/plugins/maps/public/index_pattern_util.test.ts similarity index 93% rename from x-pack/plugins/maps/public/index_pattern_util.test.js rename to x-pack/plugins/maps/public/index_pattern_util.test.ts index 4fa9eb3cadb49..27b0a4aac9bf7 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.test.js +++ b/x-pack/plugins/maps/public/index_pattern_util.test.ts @@ -18,6 +18,7 @@ describe('getSourceFields', () => { const fields = [ { name: 'agent', + type: 'string', }, { name: 'agent.keyword', @@ -26,10 +27,11 @@ describe('getSourceFields', () => { parent: 'agent', }, }, + type: 'string', }, ]; const sourceFields = getSourceFields(fields); - expect(sourceFields).toEqual([{ name: 'agent' }]); + expect(sourceFields).toEqual([{ name: 'agent', type: 'string' }]); }); }); @@ -37,6 +39,7 @@ describe('Gold+ licensing', () => { const testStubs = [ { field: { + name: 'location', type: 'geo_point', aggregatable: true, }, @@ -45,6 +48,7 @@ describe('Gold+ licensing', () => { }, { field: { + name: 'location', type: 'geo_shape', aggregatable: false, }, @@ -53,6 +57,7 @@ describe('Gold+ licensing', () => { }, { field: { + name: 'location', type: 'geo_shape', aggregatable: true, }, diff --git a/x-pack/plugins/maps/public/index_pattern_util.ts b/x-pack/plugins/maps/public/index_pattern_util.ts new file mode 100644 index 0000000000000..e65e37ef19809 --- /dev/null +++ b/x-pack/plugins/maps/public/index_pattern_util.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IFieldType, IndexPattern } from 'src/plugins/data/public'; +import { i18n } from '@kbn/i18n'; +import { getIndexPatternService, getIsGoldPlus } from './kibana_services'; +import { indexPatterns } from '../../../../src/plugins/data/public'; +import { ES_GEO_FIELD_TYPE, ES_GEO_FIELD_TYPES } from '../common/constants'; + +export function getGeoTileAggNotSupportedReason(field: IFieldType): string | null { + if (!field.aggregatable) { + return i18n.translate('xpack.maps.geoTileAgg.disabled.docValues', { + defaultMessage: + 'Clustering requires aggregations. Enable aggregations by setting doc_values to true.', + }); + } + + if (field.type === ES_GEO_FIELD_TYPE.GEO_SHAPE && !getIsGoldPlus()) { + return i18n.translate('xpack.maps.geoTileAgg.disabled.license', { + defaultMessage: 'Geo_shape clustering requires a Gold license.', + }); + } + + return null; +} + +export async function getIndexPatternsFromIds( + indexPatternIds: string[] = [] +): Promise { + const promises: Array> = []; + indexPatternIds.forEach((id) => { + promises.push(getIndexPatternService().get(id)); + }); + + return await Promise.all(promises); +} + +export function getTermsFields(fields: IFieldType[]): IFieldType[] { + return fields.filter((field) => { + return ( + field.aggregatable && + !indexPatterns.isNestedField(field) && + ['number', 'boolean', 'date', 'ip', 'string'].includes(field.type) + ); + }); +} + +export function getAggregatableGeoFieldTypes(): string[] { + const aggregatableFieldTypes = [ES_GEO_FIELD_TYPE.GEO_POINT]; + if (getIsGoldPlus()) { + aggregatableFieldTypes.push(ES_GEO_FIELD_TYPE.GEO_SHAPE); + } + return aggregatableFieldTypes; +} + +export function getGeoFields(fields: IFieldType[]): IFieldType[] { + return fields.filter((field) => { + return !indexPatterns.isNestedField(field) && ES_GEO_FIELD_TYPES.includes(field.type); + }); +} + +export function getFieldsWithGeoTileAgg(fields: IFieldType[]): IFieldType[] { + return fields.filter(supportsGeoTileAgg); +} + +export function supportsGeoTileAgg(field?: IFieldType): boolean { + return ( + !!field && + !!field.aggregatable && + !indexPatterns.isNestedField(field) && + getAggregatableGeoFieldTypes().includes(field.type) + ); +} + +// Returns filtered fields list containing only fields that exist in _source. +export function getSourceFields(fields: IFieldType[]): IFieldType[] { + return fields.filter((field) => { + // Multi fields are not stored in _source and only exist in index. + const isMultiField = field.subType && field.subType.multi; + return !isMultiField && !indexPatterns.isNestedField(field); + }); +} diff --git a/x-pack/plugins/maps/public/kibana_services.d.ts b/x-pack/plugins/maps/public/kibana_services.d.ts index d45d7286ebf68..8fa52500fb16e 100644 --- a/x-pack/plugins/maps/public/kibana_services.d.ts +++ b/x-pack/plugins/maps/public/kibana_services.d.ts @@ -3,7 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IIndexPattern, DataPublicPluginStart } from 'src/plugins/data/public'; +import { DataPublicPluginStart } from 'src/plugins/data/public'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { IndexPatternsService } from 'src/plugins/data/public/index_patterns'; import { MapsConfigType } from '../config'; import { MapsLegacyConfigType } from '../../../../src/plugins/maps_legacy/public'; @@ -14,9 +16,7 @@ export function getIndexPatternSelectComponent(): any; export function getHttp(): any; export function getTimeFilter(): any; export function getToasts(): any; -export function getIndexPatternService(): { - get: (id: string) => IIndexPattern | undefined; -}; +export function getIndexPatternService(): IndexPatternsService; export function getAutocompleteService(): any; export function getSavedObjectsClient(): any; export function getMapsCapabilities(): any; diff --git a/x-pack/plugins/maps/public/lazy_load_bundle/index.ts b/x-pack/plugins/maps/public/lazy_load_bundle/index.ts new file mode 100644 index 0000000000000..92cefd76aa047 --- /dev/null +++ b/x-pack/plugins/maps/public/lazy_load_bundle/index.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AnyAction } from 'redux'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { IndexPatternsService } from 'src/plugins/data/public/index_patterns'; +import { Embeddable, IContainer } from '../../../../../src/plugins/embeddable/public'; +import { LayerDescriptor } from '../../common/descriptor_types'; +import { MapStore, MapStoreState } from '../reducers/store'; +import { EventHandlers } from '../reducers/non_serializable_instances'; +import { RenderToolTipContent } from '../classes/tooltips/tooltip_property'; +import { MapEmbeddableConfig, MapEmbeddableInput, MapEmbeddableOutput } from '../embeddable/types'; + +let loadModulesPromise: Promise; + +interface LazyLoadedMapModules { + getMapsSavedObjectLoader: any; + MapEmbeddable: new ( + config: MapEmbeddableConfig, + initialInput: MapEmbeddableInput, + parent?: IContainer, + renderTooltipContent?: RenderToolTipContent, + eventHandlers?: EventHandlers + ) => Embeddable; + getIndexPatternService: () => IndexPatternsService; + getHttp: () => any; + getMapsCapabilities: () => any; + createMapStore: () => MapStore; + addLayerWithoutDataSync: (layerDescriptor: LayerDescriptor) => AnyAction; + getQueryableUniqueIndexPatternIds: (state: MapStoreState) => string[]; + getInitialLayers: ( + layerListJSON?: string, + initialLayers?: LayerDescriptor[] + ) => LayerDescriptor[]; + mergeInputWithSavedMap: any; +} + +export async function lazyLoadMapModules(): Promise { + if (typeof loadModulesPromise !== 'undefined') { + return loadModulesPromise; + } + + loadModulesPromise = new Promise(async (resolve) => { + const { + // @ts-ignore + getMapsSavedObjectLoader, + getQueryableUniqueIndexPatternIds, + MapEmbeddable, + getIndexPatternService, + getHttp, + getMapsCapabilities, + createMapStore, + addLayerWithoutDataSync, + getInitialLayers, + mergeInputWithSavedMap, + } = await import('./lazy'); + + resolve({ + getMapsSavedObjectLoader, + getQueryableUniqueIndexPatternIds, + MapEmbeddable, + getIndexPatternService, + getHttp, + getMapsCapabilities, + createMapStore, + addLayerWithoutDataSync, + getInitialLayers, + mergeInputWithSavedMap, + }); + }); + return loadModulesPromise; +} diff --git a/x-pack/plugins/maps/public/embeddable/lazy/index.ts b/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts similarity index 86% rename from x-pack/plugins/maps/public/embeddable/lazy/index.ts rename to x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts index 80a50061102ad..b650678b3105c 100644 --- a/x-pack/plugins/maps/public/embeddable/lazy/index.ts +++ b/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts @@ -9,10 +9,10 @@ // @ts-ignore export * from '../../angular/services/gis_map_saved_object_loader'; -export * from '../map_embeddable'; +export * from '../../embeddable/map_embeddable'; export * from '../../kibana_services'; export * from '../../reducers/store'; export * from '../../actions'; export * from '../../selectors/map_selectors'; export * from '../../angular/get_initial_layers'; -export * from './../merge_input_with_saved_map'; +export * from '../../embeddable/merge_input_with_saved_map'; diff --git a/x-pack/plugins/maps/public/meta.js b/x-pack/plugins/maps/public/meta.js deleted file mode 100644 index 46c5e5cda3617..0000000000000 --- a/x-pack/plugins/maps/public/meta.js +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - getHttp, - getLicenseId, - getIsEmsEnabled, - getRegionmapLayers, - getTilemap, - getEmsFileApiUrl, - getEmsTileApiUrl, - getEmsLandingPageUrl, - getEmsFontLibraryUrl, - getProxyElasticMapsServiceInMaps, - getKibanaVersion, -} from './kibana_services'; -import { - GIS_API_PATH, - EMS_FILES_CATALOGUE_PATH, - EMS_TILES_CATALOGUE_PATH, - EMS_GLYPHS_PATH, - EMS_APP_NAME, - FONTS_API_PATH, -} from '../common/constants'; -import { i18n } from '@kbn/i18n'; -import { EMSClient } from '@elastic/ems-client'; - -import fetch from 'node-fetch'; - -const GIS_API_RELATIVE = `../${GIS_API_PATH}`; - -export function getKibanaRegionList() { - return getRegionmapLayers(); -} - -export function getKibanaTileMap() { - return getTilemap(); -} - -function relativeToAbsolute(url) { - const a = document.createElement('a'); - a.setAttribute('href', url); - return a.href; -} - -function fetchFunction(...args) { - return fetch(...args); -} - -let emsClient = null; -let latestLicenseId = null; -export function getEMSClient() { - if (!emsClient) { - const isEmsEnabled = getIsEmsEnabled(); - if (isEmsEnabled) { - const proxyElasticMapsServiceInMaps = getProxyElasticMapsServiceInMaps(); - const proxyPath = ''; - const tileApiUrl = proxyElasticMapsServiceInMaps - ? relativeToAbsolute(`${GIS_API_RELATIVE}/${EMS_TILES_CATALOGUE_PATH}`) - : getEmsTileApiUrl(); - const fileApiUrl = proxyElasticMapsServiceInMaps - ? relativeToAbsolute(`${GIS_API_RELATIVE}/${EMS_FILES_CATALOGUE_PATH}`) - : getEmsFileApiUrl(); - - emsClient = new EMSClient({ - language: i18n.getLocale(), - appVersion: getKibanaVersion(), - appName: EMS_APP_NAME, - tileApiUrl, - fileApiUrl, - landingPageUrl: getEmsLandingPageUrl(), - fetchFunction: fetchFunction, //import this from client-side, so the right instance is returned (bootstrapped from common/* would not work - proxyPath, - }); - } else { - //EMS is turned off. Mock API. - emsClient = { - async getFileLayers() { - return []; - }, - async getTMSServices() { - return []; - }, - addQueryParams() {}, - }; - } - } - const licenseId = getLicenseId(); - if (latestLicenseId !== licenseId) { - latestLicenseId = licenseId; - emsClient.addQueryParams({ license: licenseId }); - } - return emsClient; -} - -export function getGlyphUrl() { - if (!getIsEmsEnabled()) { - return getHttp().basePath.prepend(`/${FONTS_API_PATH}/{fontstack}/{range}`); - } - return getProxyElasticMapsServiceInMaps() - ? relativeToAbsolute(`../${GIS_API_PATH}/${EMS_TILES_CATALOGUE_PATH}/${EMS_GLYPHS_PATH}`) + - `/{fontstack}/{range}` - : getEmsFontLibraryUrl(); -} - -export function isRetina() { - return window.devicePixelRatio === 2; -} diff --git a/x-pack/plugins/maps/public/meta.ts b/x-pack/plugins/maps/public/meta.ts new file mode 100644 index 0000000000000..54c5eac7fe1b0 --- /dev/null +++ b/x-pack/plugins/maps/public/meta.ts @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { EMSClient, FileLayer, TMSService } from '@elastic/ems-client'; + +import fetch from 'node-fetch'; +import { + GIS_API_PATH, + EMS_FILES_CATALOGUE_PATH, + EMS_TILES_CATALOGUE_PATH, + EMS_GLYPHS_PATH, + EMS_APP_NAME, + FONTS_API_PATH, +} from '../common/constants'; +import { + getHttp, + getLicenseId, + getIsEmsEnabled, + getRegionmapLayers, + getTilemap, + getEmsFileApiUrl, + getEmsTileApiUrl, + getEmsLandingPageUrl, + getEmsFontLibraryUrl, + getProxyElasticMapsServiceInMaps, + getKibanaVersion, +} from './kibana_services'; + +const GIS_API_RELATIVE = `../${GIS_API_PATH}`; + +export function getKibanaRegionList(): unknown[] { + return getRegionmapLayers(); +} + +export function getKibanaTileMap(): unknown { + return getTilemap(); +} + +export async function getEmsFileLayers(): Promise { + if (!getIsEmsEnabled()) { + return []; + } + + return getEMSClient().getFileLayers(); +} + +export async function getEmsTmsServices(): Promise { + if (!getIsEmsEnabled()) { + return []; + } + + return getEMSClient().getTMSServices(); +} + +function relativeToAbsolute(url: string): string { + const a = document.createElement('a'); + a.setAttribute('href', url); + return a.href; +} + +let emsClient: EMSClient | null = null; +let latestLicenseId: string | null = null; +export function getEMSClient(): EMSClient { + if (!emsClient) { + const proxyElasticMapsServiceInMaps = getProxyElasticMapsServiceInMaps(); + const proxyPath = ''; + const tileApiUrl = proxyElasticMapsServiceInMaps + ? relativeToAbsolute(`${GIS_API_RELATIVE}/${EMS_TILES_CATALOGUE_PATH}`) + : getEmsTileApiUrl(); + const fileApiUrl = proxyElasticMapsServiceInMaps + ? relativeToAbsolute(`${GIS_API_RELATIVE}/${EMS_FILES_CATALOGUE_PATH}`) + : getEmsFileApiUrl(); + + emsClient = new EMSClient({ + language: i18n.getLocale(), + appVersion: getKibanaVersion(), + appName: EMS_APP_NAME, + tileApiUrl, + fileApiUrl, + landingPageUrl: getEmsLandingPageUrl(), + fetchFunction(url: string) { + return fetch(url); + }, + proxyPath, + }); + } + const licenseId = getLicenseId(); + if (latestLicenseId !== licenseId) { + latestLicenseId = licenseId; + emsClient.addQueryParams({ license: licenseId }); + } + return emsClient; +} + +export function getGlyphUrl(): string { + if (!getIsEmsEnabled()) { + return getHttp().basePath.prepend(`/${FONTS_API_PATH}/{fontstack}/{range}`); + } + return getProxyElasticMapsServiceInMaps() + ? relativeToAbsolute(`../${GIS_API_PATH}/${EMS_TILES_CATALOGUE_PATH}/${EMS_GLYPHS_PATH}`) + + `/{fontstack}/{range}` + : getEmsFontLibraryUrl(); +} + +export function isRetina(): boolean { + return window.devicePixelRatio === 2; +} diff --git a/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/anomaly_results_view_selector.test.tsx b/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/anomaly_results_view_selector.test.tsx new file mode 100644 index 0000000000000..4a63a8cd7e716 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/anomaly_results_view_selector.test.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { Router } from 'react-router-dom'; +import { render, fireEvent } from '@testing-library/react'; +import { createBrowserHistory } from 'history'; + +import { I18nProvider } from '@kbn/i18n/react'; + +import { AnomalyResultsViewSelector } from './index'; + +describe('AnomalyResultsViewSelector', () => { + test('should create selector with correctly selected value', () => { + const history = createBrowserHistory(); + + const { getByTestId } = render( + + + + + + ); + + // Check the Single Metric Viewer element exists in the selector, and that it is checked. + expect(getByTestId('mlAnomalyResultsViewSelectorSingleMetricViewer')).toBeInTheDocument(); + expect( + getByTestId('mlAnomalyResultsViewSelectorSingleMetricViewer').hasAttribute('checked') + ).toBe(true); + }); + + test('should open window to other results view when clicking on non-checked input', () => { + // Create mock for window.open + const mockedOpen = jest.fn(); + const originalOpen = window.open; + window.open = mockedOpen; + + const history = createBrowserHistory(); + + const { getByTestId } = render( + + + + + + ); + + fireEvent.click(getByTestId('mlAnomalyResultsViewSelectorExplorer')); + expect(mockedOpen).toHaveBeenCalledWith('#/explorer', '_self'); + + // Clean-up window.open. + window.open = originalOpen; + }); +}); diff --git a/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/anomaly_results_view_selector.tsx b/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/anomaly_results_view_selector.tsx new file mode 100644 index 0000000000000..b9eaf6c5d56ec --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/anomaly_results_view_selector.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, useMemo } from 'react'; +import { encode } from 'rison-node'; + +import { EuiButtonGroup } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { useUrlState } from '../../util/url_state'; + +interface Props { + viewId: 'timeseriesexplorer' | 'explorer'; +} + +// Component for rendering a set of buttons for switching between the Anomaly Detection results views. + +export const AnomalyResultsViewSelector: FC = ({ viewId }) => { + const toggleButtonsIcons = useMemo( + () => [ + { + id: 'timeseriesexplorer', + label: i18n.translate('xpack.ml.anomalyResultsViewSelector.singleMetricViewerLabel', { + defaultMessage: 'View results in the Single Metric Viewer', + }), + iconType: 'stats', + value: 'timeseriesexplorer', + 'data-test-subj': 'mlAnomalyResultsViewSelectorSingleMetricViewer', + }, + { + id: 'explorer', + label: i18n.translate('xpack.ml.anomalyResultsViewSelector.anomalyExplorerLabel', { + defaultMessage: 'View results in the Anomaly Explorer', + }), + iconType: 'tableOfContents', + value: 'explorer', + 'data-test-subj': 'mlAnomalyResultsViewSelectorExplorer', + }, + ], + [] + ); + + const [globalState] = useUrlState('_g'); + + const onChangeView = (newViewId: string) => { + const fullGlobalStateString = globalState !== undefined ? `?_g=${encode(globalState)}` : ''; + window.open(`#/${newViewId}${fullGlobalStateString}`, '_self'); + }; + + return ( + + ); +}; diff --git a/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/index.tsx b/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/index.tsx new file mode 100644 index 0000000000000..37366bc6511a7 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/index.tsx @@ -0,0 +1,6 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export { AnomalyResultsViewSelector } from './anomaly_results_view_selector'; diff --git a/x-pack/plugins/ml/public/application/components/job_selector/_job_selector.scss b/x-pack/plugins/ml/public/application/components/job_selector/_job_selector.scss index 99fda2570ba0e..d8d0f87a589a0 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/_job_selector.scss +++ b/x-pack/plugins/ml/public/application/components/job_selector/_job_selector.scss @@ -1,8 +1,8 @@ .mlJobSelectorBar { - padding: $euiSize; + padding: $euiSizeS; } -.mlJobSelectorFlyoutBody > .euiFlyoutBody__overflow { +.mlJobSelectorFlyoutBody>.euiFlyoutBody__overflow { padding-top: $euiSizeS; } @@ -26,54 +26,14 @@ } .mlJobSelector__ganttBarRunning { - background-image:-webkit-gradient(linear, - 0 100%, 100% 0, - color-stop(0.25, rgba(255, 255, 255, 0.15)), - color-stop(0.25, transparent), - color-stop(0.5, transparent), - color-stop(0.5, rgba(255, 255, 255, 0.15)), - color-stop(0.75, rgba(255, 255, 255, 0.15)), - color-stop(0.75, transparent), - to(transparent)); - background-image:-webkit-linear-gradient(45deg, - rgba(255, 255, 255, 0.15) 25%, - transparent 25%, transparent 50%, - rgba(255, 255, 255, 0.15) 50%, - rgba(255, 255, 255, 0.15) 75%, - transparent 75%, - transparent); - background-image:-moz-linear-gradient(45deg, - rgba(255, 255, 255, 0.15) 25%, - transparent 25%, - transparent 50%, - rgba(255, 255, 255, 0.15) 50%, - rgba(255, 255, 255, 0.15) 75%, - transparent 75%, - transparent); - background-image:-o-linear-gradient(45deg, - rgba(255, 255, 255, 0.15) 25%, - transparent 25%, - transparent 50%, - rgba(255, 255, 255, 0.15) 50%, - rgba(255, 255, 255, 0.15) 75%, - transparent 75%, - transparent); - background-image:linear-gradient(45deg, - rgba(255, 255, 255, 0.15) 25%, - transparent 25%, - transparent 50%, - rgba(255, 255, 255, 0.15) 50%, - rgba(255, 255, 255, 0.15) 75%, - transparent 75%, - transparent); - -webkit-background-size:40px 40px; - -moz-background-size:40px 40px; - -o-background-size:40px 40px; - background-size:40px 40px; - - -webkit-animation:progress-bar-stripes 2s linear infinite; - -moz-animation:progress-bar-stripes 2s linear infinite; - -ms-animation:progress-bar-stripes 2s linear infinite; - -o-animation:progress-bar-stripes 2s linear infinite; - animation:progress-bar-stripes 2s linear infinite; + background-image: linear-gradient(45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent); + background-size: $euiSizeXXL $euiSizeXXL; + animation: progress-bar-stripes 2s linear infinite; } diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/_navigation_menu.scss b/x-pack/plugins/ml/public/application/components/navigation_menu/_navigation_menu.scss index 86a3a3502f273..0d14bb46e8deb 100644 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/_navigation_menu.scss +++ b/x-pack/plugins/ml/public/application/components/navigation_menu/_navigation_menu.scss @@ -1,25 +1,5 @@ -.mlNavigationMenu__tab { - padding-bottom: 0; - padding-left: 0px; - padding-right: 0px; - margin-left: $euiSizeM; -} - -.mlNavigationMenu__mainTab { - margin-left: $euiSizeM; - padding-bottom: 0; - font-weight: normal; -} - -.mlNavigationMenu__topNav { - padding-top: $euiSizeS; - margin-right: $euiSizeS; -} - -.mlNavHorizontalRule { - margin: $euiSizeM 0 0 0; -} - -.mlSubTabs { - margin-top: $euiSizeS; +.mlNavigationMenu { + padding: 0 $euiSizeM; + border-bottom: $euiBorderThin; + background-color: $euiColorEmptyShade; } diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/top_nav/top_nav.test.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx similarity index 91% rename from x-pack/plugins/ml/public/application/components/navigation_menu/top_nav/top_nav.test.tsx rename to x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx index b03281bf30399..e2aa13f6019ed 100644 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/top_nav/top_nav.test.tsx +++ b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx @@ -12,7 +12,7 @@ import { EuiSuperDatePicker } from '@elastic/eui'; import { mlTimefilterRefresh$ } from '../../../services/timefilter_refresh_service'; -import { TopNav } from './top_nav'; +import { DatePickerWrapper } from './date_picker_wrapper'; jest.mock('../../../contexts/kibana', () => ({ useMlKibana: () => { @@ -43,7 +43,7 @@ jest.mock('../../../contexts/kibana', () => ({ const noop = () => {}; -describe('Navigation Menu: ', () => { +describe('Navigation Menu: ', () => { beforeEach(() => { jest.useFakeTimers(); }); @@ -58,17 +58,17 @@ describe('Navigation Menu: ', () => { const wrapper = mount( - + ); - expect(wrapper.find(TopNav)).toHaveLength(1); + expect(wrapper.find(DatePickerWrapper)).toHaveLength(1); expect(refreshListener).toBeCalledTimes(0); refreshSubscription.unsubscribe(); }); // The following tests are written against EuiSuperDatePicker - // instead of TopNav. TopNav uses hooks and we cannot write tests + // instead of DatePickerWrapper. DatePickerWrapper uses hooks and we cannot write tests // with async hook updates yet until React 16.9 is available. test('Listen for consecutive super date picker refreshs.', async () => { const onRefresh = jest.fn(); diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/top_nav/top_nav.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx similarity index 97% rename from x-pack/plugins/ml/public/application/components/navigation_menu/top_nav/top_nav.tsx rename to x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx index abaaf2cc3a185..27f8c822d68e3 100644 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/top_nav/top_nav.tsx +++ b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx @@ -43,7 +43,7 @@ function updateLastRefresh(timeRange: OnRefreshProps) { mlTimefilterRefresh$.next({ lastRefresh: Date.now(), timeRange }); } -export const TopNav: FC = () => { +export const DatePickerWrapper: FC = () => { const { services } = useMlKibana(); const config = services.uiSettings; const { timefilter, history } = services.data.query.timefilter; @@ -124,7 +124,7 @@ export const TopNav: FC = () => { return ( {(isAutoRefreshSelectorEnabled || isTimeRangeSelectorEnabled) && ( -
+
= { - overview: { testSubject: 'mlMainTab overview', pathId: 'overview' }, + overview: { testSubject: 'mlMainTab overview' }, + // Note that anomaly detection jobs list is mapped to ml#/jobs. anomaly_detection: { testSubject: 'mlMainTab anomalyDetection', pathId: 'jobs' }, data_frame_analytics: { testSubject: 'mlMainTab dataFrameAnalytics' }, datavisualizer: { testSubject: 'mlMainTab dataVisualizer' }, + settings: { testSubject: 'mlMainTab settings' }, + 'access-denied': { testSubject: 'mlMainTab overview' }, }; export const MainTabs: FC = ({ tabId, disableLinks }) => { const [globalState] = useUrlState('_g'); const [selectedTabId, setSelectedTabId] = useState(tabId); - function onSelectedTabChanged(id: string) { + function onSelectedTabChanged(id: TabId) { setSelectedTabId(id); } @@ -93,20 +103,23 @@ export const MainTabs: FC = ({ tabId, disableLinks }) => { {tab.name} ) : ( - - onSelectedTabChanged(id)} - isSelected={id === selectedTabId} +
+ - {tab.name} - - + onSelectedTabChanged(id)} + isSelected={id === selectedTabId} + key={`tab-${id}-key`} + > + {tab.name} + + +
); })} diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/navigation_menu.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/navigation_menu.tsx index 6be2d18e59741..5a1c08bf9b7bf 100644 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/navigation_menu.tsx +++ b/x-pack/plugins/ml/public/application/components/navigation_menu/navigation_menu.tsx @@ -5,28 +5,19 @@ */ import React, { Fragment, FC } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { isFullLicense } from '../../license'; -import { TopNav } from './top_nav'; import { MainTabs } from './main_tabs'; -import { Tabs } from './tabs'; -export type TabId = string; -type TabSupport = Record; - -const tabSupport: TabSupport = { - overview: null, - jobs: 'anomaly_detection', - settings: 'anomaly_detection', - data_frame_analytics: null, - datavisualizer: null, - filedatavisualizer: null, - timeseriesexplorer: 'anomaly_detection', - 'access-denied': null, - explorer: 'anomaly_detection', -}; +export type TabId = + | 'access-denied' + | 'anomaly_detection' + | 'data_frame_analytics' + | 'datavisualizer' + | 'overview' + | 'settings'; interface Props { tabId: TabId; @@ -34,23 +25,14 @@ interface Props { export const NavigationMenu: FC = ({ tabId }) => { const disableLinks = isFullLicense() === false; - const showTabs = typeof tabSupport[tabId] !== 'undefined'; - const mainTabId = tabSupport[tabId] || tabId; - // show horizontal rule if there are no subtabs - const showHorizontalRule = tabSupport[tabId] === null; return ( - - - {showTabs && } - + - + - {showHorizontalRule && } - {showTabs && } ); }; diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/tabs.test.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/tabs.test.tsx deleted file mode 100644 index 74360ce8c788c..0000000000000 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/tabs.test.tsx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getTabs } from './tabs'; - -describe('Navigation Menu: Tabs', () => { - test('getTabs() always returns an array', () => { - const tabs1 = getTabs('anomaly_detection', false); - expect(Array.isArray(tabs1)).toBeTruthy(); - expect(tabs1).toHaveLength(4); - - const tabs2 = getTabs('access-denied', false); - expect(Array.isArray(tabs2)).toBeTruthy(); - expect(tabs2).toHaveLength(0); - - const tabs3 = getTabs('datavisualizer', false); - expect(Array.isArray(tabs3)).toBeTruthy(); - expect(tabs3).toHaveLength(0); - }); -}); diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/tabs.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/tabs.tsx deleted file mode 100644 index 774fe2d742834..0000000000000 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/tabs.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { FC, useState } from 'react'; -import { encode } from 'rison-node'; - -import { EuiTabs, EuiTab, EuiLink } from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; - -import { useUrlState } from '../../util/url_state'; - -import { Tab } from './main_tabs'; -import { TabId } from './navigation_menu'; - -interface Props { - disableLinks: boolean; - mainTabId: TabId; - tabId: TabId; -} - -export function getTabs(tabId: TabId, disableLinks: boolean): Tab[] { - const TAB_MAP: Partial> = { - overview: [], - datavisualizer: [], - data_frame_analytics: [], - anomaly_detection: [ - { - id: 'jobs', - name: i18n.translate('xpack.ml.navMenu.jobManagementTabLinkText', { - defaultMessage: 'Job Management', - }), - disabled: disableLinks, - }, - { - id: 'explorer', - name: i18n.translate('xpack.ml.navMenu.anomalyExplorerTabLinkText', { - defaultMessage: 'Anomaly Explorer', - }), - disabled: disableLinks, - }, - { - id: 'timeseriesexplorer', - name: i18n.translate('xpack.ml.navMenu.singleMetricViewerTabLinkText', { - defaultMessage: 'Single Metric Viewer', - }), - disabled: disableLinks, - }, - { - id: 'settings', - name: i18n.translate('xpack.ml.navMenu.settingsTabLinkText', { - defaultMessage: 'Settings', - }), - disabled: disableLinks, - }, - ], - }; - - return TAB_MAP[tabId] || []; -} - -enum TAB_TEST_SUBJECT { - overview = 'mlOverview', - jobs = 'mlSubTab jobManagement', - explorer = 'mlSubTab anomalyExplorer', - timeseriesexplorer = 'mlSubTab singleMetricViewer', - settings = 'mlSubTab settings', -} - -type TAB_TEST_SUBJECTS = keyof typeof TAB_TEST_SUBJECT; - -export const Tabs: FC = ({ tabId, mainTabId, disableLinks }) => { - const [globalState] = useUrlState('_g'); - const [selectedTabId, setSelectedTabId] = useState(tabId); - function onSelectedTabChanged(id: string) { - setSelectedTabId(id); - } - - const tabs = getTabs(mainTabId, disableLinks); - - if (tabs.length === 0) return null; - - return ( - - {tabs.map((tab: Tab) => { - const id = tab.id; - // globalState (e.g. selected jobs and time range) should be retained when changing pages. - // appState will not be considered. - const fullGlobalStateString = globalState !== undefined ? `?_g=${encode(globalState)}` : ''; - - return ( - - onSelectedTabChanged(id)} - isSelected={id === selectedTabId} - disabled={tab.disabled} - > - {tab.name} - - - ); - })} - - ); -}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx index 25e3a2808fc61..dac0de4c7a533 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx @@ -48,7 +48,6 @@ import { SORT_DIRECTION, } from '../../../../../components/ml_in_memory_table'; import { AnalyticStatsBarStats, StatsBar } from '../../../../../components/stats_bar'; -import { RefreshAnalyticsListButton } from '../refresh_analytics_list_button'; import { CreateAnalyticsButton } from '../create_analytics_button'; import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; import { getSelectedJobIdFromUrl } from '../../../../../jobs/jobs_list/components/utils'; @@ -398,9 +397,6 @@ export const DataFrameAnalyticsList: FC = ({ - - - {!isManagementTable && createAnalyticsForm && ( = ({ - +
', () => { ); - expect(wrapper.find('EuiButton').text()).toBe('Create analytics job'); + expect(wrapper.find('EuiButton').text()).toBe('Create job'); }); }); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx index 595a1cf73e189..c7c33787c37bc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_button/create_analytics_button.tsx @@ -35,7 +35,7 @@ export const CreateAnalyticsButton: FC = ({ data-test-subj="mlAnalyticsButtonCreate" > {i18n.translate('xpack.ml.dataframe.analyticsList.createDataFrameAnalyticsButton', { - defaultMessage: 'Create analytics job', + defaultMessage: 'Create job', })} ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/page.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/page.tsx index 4c8039009767f..c0b7d63e623ce 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/page.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/page.tsx @@ -11,16 +11,21 @@ import { i18n } from '@kbn/i18n'; import { EuiBetaBadge, + EuiFlexGroup, + EuiFlexItem, EuiPage, EuiPageBody, - EuiTitle, + EuiPageContent, EuiPageHeader, EuiPageHeaderSection, + EuiTitle, } from '@elastic/eui'; import { NavigationMenu } from '../../../components/navigation_menu'; +import { DatePickerWrapper } from '../../../components/navigation_menu/date_picker_wrapper'; import { DataFrameAnalyticsList } from './components/analytics_list'; import { useRefreshInterval } from './components/analytics_list/use_refresh_interval'; +import { RefreshAnalyticsListButton } from './components/refresh_analytics_list_button'; import { useCreateAnalyticsForm } from './hooks/use_create_analytics_form'; import { NodeAvailableWarning } from '../../../components/node_available_warning'; import { UpgradeWarning } from '../../../components/upgrade'; @@ -63,15 +68,27 @@ export const Page: FC = () => { + + + + + + + + + + - + + + diff --git a/x-pack/plugins/ml/public/application/explorer/_explorer.scss b/x-pack/plugins/ml/public/application/explorer/_explorer.scss index a46f35cbd4d20..7e5f354bbb402 100644 --- a/x-pack/plugins/ml/public/application/explorer/_explorer.scss +++ b/x-pack/plugins/ml/public/application/explorer/_explorer.scss @@ -42,7 +42,6 @@ } .mlAnomalyExplorer__filterBar { - margin-bottom: $euiSize; padding-right: $euiSize; padding-left: $euiSize; } diff --git a/x-pack/plugins/ml/public/application/explorer/explorer.js b/x-pack/plugins/ml/public/application/explorer/explorer.js index 5cebb6354c0db..1a5a9a9d82862 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer.js @@ -20,10 +20,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiFormRow, + EuiHorizontalRule, EuiIconTip, EuiPage, EuiPageBody, - EuiScreenReaderOnly, + EuiPageHeader, + EuiPageHeaderSection, EuiSelect, EuiSpacer, EuiTitle, @@ -38,12 +40,14 @@ import { } from './components'; import { ExplorerSwimlane } from './explorer_swimlane'; import { getTimeBucketsFromCache } from '../util/time_buckets'; +import { DatePickerWrapper } from '../components/navigation_menu/date_picker_wrapper'; import { InfluencersList } from '../components/influencers_list'; import { ALLOW_CELL_RANGE_SELECTION, dragSelect$, explorerService, } from './explorer_dashboard_service'; +import { AnomalyResultsViewSelector } from '../components/anomaly_results_view_selector'; import { LoadingIndicator } from '../components/loading_indicator/loading_indicator'; import { NavigationMenu } from '../components/navigation_menu'; import { CheckboxShowCharts } from '../components/controls/checkbox_showcharts'; @@ -89,17 +93,67 @@ function mapSwimlaneOptionsToEuiOptions(options) { text: option, })); } -const ExplorerPage = ({ children, jobSelectorProps, resizeRef }) => ( + +const ExplorerPage = ({ + children, + jobSelectorProps, + noInfluencersConfigured, + influencers, + filterActive, + filterPlaceHolder, + indexPattern, + queryString, + filterIconTriggeredQuery, + updateLanguage, + resizeRef, +}) => (
- - + + - -

- -

-
+ + + + + + + + +

+ +

+
+
+
+
+ + + {noInfluencersConfigured === false && influencers !== undefined && ( + +
+ +
+
+ )} + + + +
+
+
+ + {children}
@@ -314,7 +368,18 @@ export class Explorer extends React.Component { if (loading === true) { return ( - + +
- {noInfluencersConfigured === false && influencers !== undefined && ( -
- -
- )} - {noInfluencersConfigured && (
j.id); + return ( @@ -366,17 +369,7 @@ export class JobsListView extends Component { - - - - - - - - - - - + - + - - + + - + -
-
- + + + + + + + + + + + + + +
+
+ this.refreshJobSummaryList(true)} + /> + +
+ this.refreshJobSummaryList(true)} + selectedJobsCount={this.state.selectedJobs.length} + loading={loading} + /> + this.refreshJobSummaryList(true)} + allJobIds={jobIds} + /> + this.refreshJobSummaryList(true)} + /> + this.refreshJobSummaryList(true)} + /> + -
- this.refreshJobSummaryList(true)} - selectedJobsCount={this.state.selectedJobs.length} - loading={loading} - /> - this.refreshJobSummaryList(true)} - allJobIds={jobIds} - /> - this.refreshJobSummaryList(true)} - /> - this.refreshJobSummaryList(true)} - /> - -
+ ); diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx index d738c8aa3d615..1e45f28594572 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx @@ -21,7 +21,7 @@ interface JobsPageProps { export const JobsPage: FC = (props) => { return (
- +
); diff --git a/x-pack/plugins/ml/public/application/routing/breadcrumbs.ts b/x-pack/plugins/ml/public/application/routing/breadcrumbs.ts index 288914f702728..82e233745f9e4 100644 --- a/x-pack/plugins/ml/public/application/routing/breadcrumbs.ts +++ b/x-pack/plugins/ml/public/application/routing/breadcrumbs.ts @@ -28,6 +28,13 @@ export const ANOMALY_DETECTION_BREADCRUMB: ChromeBreadcrumb = Object.freeze({ href: '#/jobs', }); +export const DATA_FRAME_ANALYTICS_BREADCRUMB: ChromeBreadcrumb = Object.freeze({ + text: i18n.translate('xpack.ml.dataFrameAnalyticsLabel', { + defaultMessage: 'Data Frame Analytics', + }), + href: '#/data_frame_analytics', +}); + export const DATA_VISUALIZER_BREADCRUMB: ChromeBreadcrumb = Object.freeze({ text: i18n.translate('xpack.ml.datavisualizerBreadcrumbLabel', { defaultMessage: 'Data Visualizer', diff --git a/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx b/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx index 2dde5426ec9a0..1ffea2c06faf4 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx @@ -13,13 +13,14 @@ import { useResolver } from '../../use_resolver'; import { basicResolvers } from '../../resolvers'; import { Page } from '../../../data_frame_analytics/pages/analytics_exploration'; import { ANALYSIS_CONFIG_TYPE } from '../../../data_frame_analytics/common/analytics'; -import { ML_BREADCRUMB } from '../../breadcrumbs'; +import { ML_BREADCRUMB, DATA_FRAME_ANALYTICS_BREADCRUMB } from '../../breadcrumbs'; const breadcrumbs = [ ML_BREADCRUMB, + DATA_FRAME_ANALYTICS_BREADCRUMB, { text: i18n.translate('xpack.ml.dataFrameAnalyticsBreadcrumbs.dataFrameExplorationLabel', { - defaultMessage: 'Data Frame Analytics', + defaultMessage: 'Exploration', }), href: '', }, diff --git a/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_jobs_list.tsx b/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_jobs_list.tsx index f6d7d91884646..2623136d1e98f 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_jobs_list.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_jobs_list.tsx @@ -11,13 +11,14 @@ import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; import { basicResolvers } from '../../resolvers'; import { Page } from '../../../data_frame_analytics/pages/analytics_management'; -import { ML_BREADCRUMB } from '../../breadcrumbs'; +import { ML_BREADCRUMB, DATA_FRAME_ANALYTICS_BREADCRUMB } from '../../breadcrumbs'; const breadcrumbs = [ ML_BREADCRUMB, + DATA_FRAME_ANALYTICS_BREADCRUMB, { text: i18n.translate('xpack.ml.dataFrameAnalyticsBreadcrumbs.dataFrameListLabel', { - defaultMessage: 'Data Frame Analytics', + defaultMessage: 'Job Management', }), href: '', }, diff --git a/x-pack/plugins/ml/public/application/routing/routes/settings/settings.tsx b/x-pack/plugins/ml/public/application/routing/routes/settings/settings.tsx index 7cb943c091c4e..a80c173dbca34 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/settings/settings.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/settings/settings.tsx @@ -21,7 +21,7 @@ import { checkPermission, } from '../../../capabilities/check_capabilities'; import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes'; -import { Settings } from '../../../settings'; +import { AnomalyDetectionSettingsContext, Settings } from '../../../settings'; import { ML_BREADCRUMB, SETTINGS } from '../../breadcrumbs'; const breadcrumbs = [ML_BREADCRUMB, SETTINGS]; @@ -42,11 +42,17 @@ const PageWrapper: FC = ({ deps }) => { useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); const canGetFilters = checkPermission('canGetFilters'); + const canCreateFilter = checkPermission('canCreateFilter'); const canGetCalendars = checkPermission('canGetCalendars'); + const canCreateCalendar = checkPermission('canCreateCalendar'); return ( - + + + ); }; diff --git a/x-pack/plugins/ml/public/application/settings/_settings.scss b/x-pack/plugins/ml/public/application/settings/_settings.scss index beeb07381b90b..9a50a494000cf 100644 --- a/x-pack/plugins/ml/public/application/settings/_settings.scss +++ b/x-pack/plugins/ml/public/application/settings/_settings.scss @@ -1,5 +1,11 @@ .mlSettingsPage { + .mlSettingsPage__header { + width: map-get($euiBreakpoints, 'xl'); + margin-left: auto; + margin-right: auto; + } + .mlSettingsPage__content { width: map-get($euiBreakpoints, 'xl'); margin-top: $euiSize; diff --git a/x-pack/plugins/ml/public/application/settings/anomaly_detection_settings.tsx b/x-pack/plugins/ml/public/application/settings/anomaly_detection_settings.tsx new file mode 100644 index 0000000000000..16d7e1605263c --- /dev/null +++ b/x-pack/plugins/ml/public/application/settings/anomaly_detection_settings.tsx @@ -0,0 +1,226 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, Fragment, useContext, useEffect, useState } from 'react'; + +import { + EuiBadge, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiPageContent, + EuiPageContentHeader, + EuiSpacer, + EuiText, + EuiTextColor, + EuiTitle, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { AnomalyDetectionSettingsContext } from './anomaly_detection_settings_context'; +import { useNotifications } from '../contexts/kibana'; +import { ml } from '../services/ml_api_service'; + +export const AnomalyDetectionSettings: FC = () => { + const [calendarsCount, setCalendarsCount] = useState(0); + const [filterListsCount, setFilterListsCount] = useState(0); + + const { canGetFilters, canCreateFilter, canGetCalendars, canCreateCalendar } = useContext( + AnomalyDetectionSettingsContext + ); + + const { toasts } = useNotifications(); + + useEffect(() => { + loadSummaryStats(); + }, []); + + async function loadSummaryStats() { + // Obtain the counts of calendars and filter lists. + if (canGetCalendars === true) { + try { + const calendars = await ml.calendars(); + setCalendarsCount(calendars.length); + } catch (e) { + toasts.addDanger( + i18n.translate('xpack.ml.settings.anomalyDetection.loadingCalendarsCountErrorMessage', { + defaultMessage: 'An error occurred obtaining the count of calendars', + }) + ); + } + } + + if (canGetFilters === true) { + try { + const filterLists = await ml.filters.filtersStats(); + setFilterListsCount(filterLists.length); + } catch (e) { + toasts.addDanger( + i18n.translate('xpack.ml.settings.anomalyDetection.loadingFilterListCountErrorMessage', { + defaultMessage: 'An error occurred obtaining the count of filter lists', + }) + ); + } + } + } + + return ( + + + + +

+ +

+
+
+ + + + +

+ +

+
+ + + +

+ +

+
+
+ + + {canGetCalendars && ( + + + {calendarsCount}, + calendarsCount, + }} + /> + + + )} + + + + + + + + + + + +
+ + +

+ +

+
+ + + +

+ +

+
+
+ + + {canGetFilters && ( + + + {filterListsCount}, + filterListsCount, + }} + /> + + + )} + + + + + + + + + + + +
+
+
+
+ ); +}; diff --git a/x-pack/plugins/ml/public/application/settings/anomaly_detection_settings_context.tsx b/x-pack/plugins/ml/public/application/settings/anomaly_detection_settings_context.tsx new file mode 100644 index 0000000000000..0b6e03c6e39d9 --- /dev/null +++ b/x-pack/plugins/ml/public/application/settings/anomaly_detection_settings_context.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createContext } from 'react'; + +export interface AnomalyDetectionSettingsContextValue { + canGetFilters: boolean; + canCreateFilter: boolean; + canGetCalendars: boolean; + canCreateCalendar: boolean; +} + +export const AnomalyDetectionSettingsContext = createContext({ + canGetFilters: false, + canCreateFilter: false, + canGetCalendars: false, + canCreateCalendar: false, +}); diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap b/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap index df2e119f511e1..c17dc6c7db06a 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap @@ -150,7 +150,7 @@ exports[`CalendarForm Renders calendar form 1`] = ` grow={false} > , ( { onDeleteClick(event.event_id); @@ -106,7 +106,7 @@ export const EventsTable = ({ { instance.setState(testState); wrapper.update(); expect(wrapper.state('selectedEvents').length).toBe(2); - const deleteButton = wrapper.find('[data-test-subj="event_delete"]'); + const deleteButton = wrapper.find('[data-test-subj="mlEventDelete"]'); const button = deleteButton.find('EuiButtonEmpty').first(); button.simulate('click'); diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js index 0a6e8916fa657..3e64d7ea3d367 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_calendar.test.js @@ -117,7 +117,7 @@ describe('NewCalendar', () => { test('Import modal shown on Import Events button click', () => { const wrapper = mountWithIntl(); - const importButton = wrapper.find('[data-test-subj="ml_import_events"]'); + const importButton = wrapper.find('[data-test-subj="mlImportEvents"]'); const button = importButton.find('EuiButton'); button.simulate('click'); @@ -127,7 +127,7 @@ describe('NewCalendar', () => { test('New event modal shown on New event button click', () => { const wrapper = mountWithIntl(); - const importButton = wrapper.find('[data-test-subj="ml_new_event"]'); + const importButton = wrapper.find('[data-test-subj="mlNewEvent"]'); const button = importButton.find('EuiButton'); button.simulate('click'); @@ -154,7 +154,7 @@ describe('NewCalendar', () => { const wrapper = mountWithIntl(); - const buttons = wrapper.find('[data-test-subj="ml_save_calendar_button"]'); + const buttons = wrapper.find('[data-test-subj="mlSaveCalendarButton"]'); const saveButton = buttons.find('EuiButton'); expect(saveButton.prop('isDisabled')).toBe(true); diff --git a/x-pack/plugins/ml/public/application/settings/index.ts b/x-pack/plugins/ml/public/application/settings/index.ts index db74dcb1a1846..1ae1824886591 100644 --- a/x-pack/plugins/ml/public/application/settings/index.ts +++ b/x-pack/plugins/ml/public/application/settings/index.ts @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ +export { AnomalyDetectionSettingsContext } from './anomaly_detection_settings_context'; export { Settings } from './settings'; diff --git a/x-pack/plugins/ml/public/application/settings/settings.test.js b/x-pack/plugins/ml/public/application/settings/settings.test.js deleted file mode 100644 index 6b4e752845774..0000000000000 --- a/x-pack/plugins/ml/public/application/settings/settings.test.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import React from 'react'; - -import { Settings } from './settings'; - -jest.mock('../components/navigation_menu', () => ({ - NavigationMenu: () =>
, -})); - -describe('Settings', () => { - test('Renders settings page with all buttons enabled.', () => { - const wrapper = mountWithIntl(); - - const filterButton = wrapper - .find('[data-test-subj="ml_filter_lists_button"]') - .find('EuiButtonEmpty'); - expect(filterButton.prop('isDisabled')).toBe(false); - - const calendarButton = wrapper - .find('[data-test-subj="ml_calendar_mng_button"]') - .find('EuiButtonEmpty'); - expect(calendarButton.prop('isDisabled')).toBe(false); - }); - - test('Filter Lists button disabled if canGetFilters is false', () => { - const wrapper = mountWithIntl(); - - const filterButton = wrapper - .find('[data-test-subj="ml_filter_lists_button"]') - .find('EuiButtonEmpty'); - expect(filterButton.prop('isDisabled')).toBe(true); - - const calendarButton = wrapper - .find('[data-test-subj="ml_calendar_mng_button"]') - .find('EuiButtonEmpty'); - expect(calendarButton.prop('isDisabled')).toBe(false); - }); - - test('Calendar management button disabled if canGetCalendars is false', () => { - const wrapper = mountWithIntl(); - - const filterButton = wrapper - .find('[data-test-subj="ml_filter_lists_button"]') - .find('EuiButtonEmpty'); - expect(filterButton.prop('isDisabled')).toBe(false); - - const calendarButton = wrapper - .find('[data-test-subj="ml_calendar_mng_button"]') - .find('EuiButtonEmpty'); - expect(calendarButton.prop('isDisabled')).toBe(true); - }); -}); diff --git a/x-pack/plugins/ml/public/application/settings/settings.test.tsx b/x-pack/plugins/ml/public/application/settings/settings.test.tsx new file mode 100644 index 0000000000000..f16bf62632152 --- /dev/null +++ b/x-pack/plugins/ml/public/application/settings/settings.test.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; + +import { AnomalyDetectionSettingsContext } from './anomaly_detection_settings_context'; +import { Settings } from './settings'; + +jest.mock('../components/navigation_menu', () => ({ + NavigationMenu: () =>
, +})); + +jest.mock('../contexts/kibana', () => ({ + useNotifications: () => { + return { + toasts: { addDanger: jest.fn() }, + }; + }, +})); + +describe('Settings', () => { + function runCheckButtonsDisabledTest( + canGetFilters: boolean, + canCreateFilter: boolean, + canGetCalendars: boolean, + canCreateCalendar: boolean, + isFilterListsMngDisabled: boolean, + isFilterListCreateDisabled: boolean, + isCalendarsMngDisabled: boolean, + isCalendarCreateDisabled: boolean + ) { + const wrapper = mountWithIntl( + + + + ); + + const filterMngButton = wrapper + .find('[data-test-subj="mlFilterListsMngButton"]') + .find('EuiButtonEmpty'); + expect(filterMngButton.prop('isDisabled')).toBe(isFilterListsMngDisabled); + + const filterCreateButton = wrapper + .find('[data-test-subj="mlFilterListsCreateButton"]') + .find('EuiButtonEmpty'); + expect(filterCreateButton.prop('isDisabled')).toBe(isFilterListCreateDisabled); + + const calendarMngButton = wrapper + .find('[data-test-subj="mlCalendarsMngButton"]') + .find('EuiButtonEmpty'); + expect(calendarMngButton.prop('isDisabled')).toBe(isCalendarsMngDisabled); + + const calendarCreateButton = wrapper + .find('[data-test-subj="mlCalendarsCreateButton"]') + .find('EuiButtonEmpty'); + expect(calendarCreateButton.prop('isDisabled')).toBe(isCalendarCreateDisabled); + } + + test('should render settings page with all buttons enabled when full user capabilities', () => { + runCheckButtonsDisabledTest(true, true, true, true, false, false, false, false); + }); + + test('should disable Filter Lists buttons if filters capabilities are false', () => { + runCheckButtonsDisabledTest(false, false, true, true, true, true, false, false); + }); + + test('should disable Calendars buttons if calendars capabilities are false', () => { + runCheckButtonsDisabledTest(true, true, false, false, false, false, true, true); + }); +}); diff --git a/x-pack/plugins/ml/public/application/settings/settings.tsx b/x-pack/plugins/ml/public/application/settings/settings.tsx index e2e7a4030f04f..94195678dd503 100644 --- a/x-pack/plugins/ml/public/application/settings/settings.tsx +++ b/x-pack/plugins/ml/public/application/settings/settings.tsx @@ -6,76 +6,30 @@ import React, { FC, Fragment } from 'react'; -import { - EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, - EuiPage, - EuiPageContentHeader, - EuiPageContent, - EuiPageBody, - EuiTitle, -} from '@elastic/eui'; +import { EuiPage, EuiPageBody, EuiPageHeader, EuiPageHeaderSection, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { NavigationMenu } from '../components/navigation_menu'; +import { AnomalyDetectionSettings } from './anomaly_detection_settings'; -interface Props { - canGetFilters: boolean; - canGetCalendars: boolean; -} +import { NavigationMenu } from '../components/navigation_menu'; -export const Settings: FC = ({ canGetFilters, canGetCalendars }) => { +export const Settings: FC = () => { return ( - - - + + +

- +

-
- - - - - - - - - - - - - - -
+ + +
diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js index 87bd2bb4af62c..86f12d7ca68c8 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js @@ -491,7 +491,6 @@ export class ForecastingModalUI extends Component { + {fieldNamesWithEmptyValues.length > 0 && ( = ({ children, dateFormatTz, - loading, resizeRef, }) => { return ( <> - - {/* Show animated progress bar while loading */} - {loading === true && ( - - )} - {/* Show a progress bar with progress 0% when not loading. - If we'd just show no progress bar when not loading it would result in a flickering height effect. */} - {loading === false && ( - - )} - +
= ({ > - -

- -

-
+ + + + + + + + +

+ +

+
+
+
+
+ + + + + + + +
+ + + + {children}
diff --git a/x-pack/plugins/monitoring/public/_hacks.scss b/x-pack/plugins/monitoring/public/_hacks.scss deleted file mode 100644 index bd5079a560820..0000000000000 --- a/x-pack/plugins/monitoring/public/_hacks.scss +++ /dev/null @@ -1,24 +0,0 @@ -@import '@elastic/eui/src/components/header/variables'; - -#monitoring-app { - // SASSTODO: PUI tooltips can be replaced with EuiToolTip - .pui-tooltip-inner { - font-size: $euiFontSizeXS; - } - - .monitoring-tooltip__trigger, - .monitoring-tooltip__trigger:hover { - color: $euiTextColor; - } - - // SASSTODO: Remove font awesome - .monTabs--icon { - margin-left: $euiSizeXS; - color: $euiColorDarkShade; - font-size: $euiFontSize; - } - - .monCallout--meta { - margin-top: $euiSize; - } -} diff --git a/x-pack/plugins/monitoring/public/angular/index.ts b/x-pack/plugins/monitoring/public/angular/index.ts index b371503fdb7c9..3f2a51a898d1f 100644 --- a/x-pack/plugins/monitoring/public/angular/index.ts +++ b/x-pack/plugins/monitoring/public/angular/index.ts @@ -12,7 +12,7 @@ import { localAppModule, appModuleName } from './app_modules'; import { MonitoringPluginDependencies } from '../types'; -const APP_WRAPPER_CLASS = 'monitoringApplicationWrapper'; +const APP_WRAPPER_CLASS = 'monApplicationWrapper'; export class AngularApp { private injector?: angular.auto.IInjectorService; diff --git a/x-pack/plugins/monitoring/public/components/chart/_chart.scss b/x-pack/plugins/monitoring/public/components/chart/_chart.scss deleted file mode 100644 index 1b8ebb762533d..0000000000000 --- a/x-pack/plugins/monitoring/public/components/chart/_chart.scss +++ /dev/null @@ -1,97 +0,0 @@ -@mixin monitoringNoUserSelect { - user-select: none; -} - -.monRhythmChart__wrapper .monRhythmChart__zoom { - visibility: hidden; - padding-right: $euiSizeM; -} - -.monRhythmChart__wrapper:hover .monRhythmChart__zoom { - visibility: visible; -} - -.monRhythmChart { - position: relative; - display: flex; - flex-direction: column; - flex: 1 0 auto; -} - -.monRhythmChart__title { - color: $euiTextColor; - margin: 0 0 $euiSizeS; -} - -.monRhythmChart__content { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - display: flex; - flex: 1 0 auto; -} - -.monRhythmChart__visualization { - display: flex; - flex-direction: column; - flex: 1 0 auto; - position: relative; - - // SASSTODO: generic selector - & > div { - min-width: 1px; - width: 100%; - height: 100%; - } - - // SASSTODO: generic selector - div { - @include monitoringNoUserSelect; - } -} - -.monRhythmChart__legendItem { - font-size: $euiFontSizeXS; - cursor: pointer; - color: $euiTextColor; - display: flex; - flex-direction: row; - align-items: center; - - &-isDisabled { - opacity: 0.5; - } -} - -.monRhythmChart__legendHorizontal { - margin-top: $euiSizeXS; -} - -.monRhythmChart__legendLabel { - overflow: hidden; - white-space: nowrap; - display: flex; - flex-direction: row; - align-items: center; -} - -.monRhythmChart__legendValue { - overflow: hidden; - white-space: nowrap; - margin-left: $euiSizeXS; -} - -.monChart__tooltipLabel, -.monChart__tooltipValue { - text-align: left; - font-size: $euiFontSizeXS; - padding: $euiSizeXS; - word-wrap: break-word; - white-space: normal; -} - -.monChart__tooltipLabel { - font-weight: $euiFontWeightBold; -} diff --git a/x-pack/plugins/monitoring/public/components/chart/_index.scss b/x-pack/plugins/monitoring/public/components/chart/_index.scss deleted file mode 100644 index 2626b91b4c1e2..0000000000000 --- a/x-pack/plugins/monitoring/public/components/chart/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'chart'; diff --git a/x-pack/plugins/monitoring/public/components/chart/horizontal_legend.js b/x-pack/plugins/monitoring/public/components/chart/horizontal_legend.js index 738775ed8d4d9..7d7f232f63975 100644 --- a/x-pack/plugins/monitoring/public/components/chart/horizontal_legend.js +++ b/x-pack/plugins/monitoring/public/components/chart/horizontal_legend.js @@ -9,6 +9,7 @@ import { includes, isFunction } from 'lodash'; import { EuiFlexItem, EuiFlexGroup, EuiIcon, EuiKeyboardAccessible } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import './horizontal_legend.scss'; export class HorizontalLegend extends React.Component { constructor() { diff --git a/x-pack/plugins/monitoring/public/components/chart/horizontal_legend.scss b/x-pack/plugins/monitoring/public/components/chart/horizontal_legend.scss new file mode 100644 index 0000000000000..a68f86d28d201 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/chart/horizontal_legend.scss @@ -0,0 +1,30 @@ +.monRhythmChart__legendItem { + font-size: $euiFontSizeXS; + cursor: pointer; + color: $euiTextColor; + display: flex; + flex-direction: row; + align-items: center; + + &-isDisabled { + opacity: .5; + } +} + +.monRhythmChart__legendHorizontal { + margin-top: $euiSizeXS; +} + +.monRhythmChart__legendLabel { + overflow: hidden; + white-space: nowrap; + display: flex; + flex-direction: row; + align-items: center; +} + +.monRhythmChart__legendValue { + overflow: hidden; + white-space: nowrap; + margin-left: $euiSizeXS; +} diff --git a/x-pack/plugins/monitoring/public/components/chart/info_tooltip.js b/x-pack/plugins/monitoring/public/components/chart/info_tooltip.js index 37fe756187cd4..87efd0a065d49 100644 --- a/x-pack/plugins/monitoring/public/components/chart/info_tooltip.js +++ b/x-pack/plugins/monitoring/public/components/chart/info_tooltip.js @@ -6,6 +6,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; +import './info_tooltip.scss'; export function InfoTooltip({ series, bucketSize }) { const tableRows = series.map((item, index) => { diff --git a/x-pack/plugins/monitoring/public/components/chart/info_tooltip.scss b/x-pack/plugins/monitoring/public/components/chart/info_tooltip.scss new file mode 100644 index 0000000000000..7798951ce23fc --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/chart/info_tooltip.scss @@ -0,0 +1,12 @@ +.monChart__tooltipLabel, +.monChart__tooltipValue { + text-align: left; + font-size: $euiFontSizeXS; + padding: $euiSizeXS; + word-wrap: break-word; + white-space: normal; +} + +.monChart__tooltipLabel { + font-weight: $euiFontWeightBold; +} diff --git a/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries_container.js b/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries_container.js index c5f4082e2b684..c6bd0773343e0 100644 --- a/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries_container.js +++ b/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries_container.js @@ -10,6 +10,7 @@ import { getTitle } from './get_title'; import { getUnits } from './get_units'; import { MonitoringTimeseries } from './monitoring_timeseries'; import { InfoTooltip } from './info_tooltip'; +import './monitoring_timeseries_container.scss'; import { EuiIconTip, diff --git a/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries_container.scss b/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries_container.scss new file mode 100644 index 0000000000000..0da89b55e69e4 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries_container.scss @@ -0,0 +1,8 @@ +.monRhythmChart__wrapper .monRhythmChart__zoom { + visibility: hidden; + padding-right: $euiSizeM; +} + +.monRhythmChart__wrapper:hover .monRhythmChart__zoom { + visibility: visible; +} diff --git a/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.js b/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.js index 02753f4d1da5b..eb32ee108e7b3 100644 --- a/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.js +++ b/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.js @@ -11,6 +11,7 @@ import { TimeseriesContainer } from './timeseries_container'; import { HorizontalLegend } from './horizontal_legend'; import { getValuesForSeriesIndex, getValuesByX } from './get_values_for_legend'; import { DEBOUNCE_SLOW_MS } from '../../../common/constants'; +import './timeseries_visualization.scss'; export class TimeseriesVisualization extends React.Component { constructor(props) { diff --git a/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.scss b/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.scss new file mode 100644 index 0000000000000..8c954e2896dc4 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.scss @@ -0,0 +1,39 @@ +.monRhythmChart { + position: relative; + display: flex; + flex-direction: column; + flex: 1 0 auto; +} + +.monRhythmChart__content { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + flex: 1 0 auto; +} + +@mixin monitoringNoUserSelect { + user-select: none; +} + +.monRhythmChart__visualization { + display: flex; + flex-direction: column; + flex: 1 0 auto; + position: relative; + + // SASSTODO: generic selector + & > div { + min-width: 1px; + width: 100%; + height: 100%; + } + + // SASSTODO: generic selector + div { + @include monitoringNoUserSelect; + } +} diff --git a/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js index f1a867536b606..d47af51ee159f 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js @@ -25,6 +25,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public'; import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants'; +import './listing.scss'; const IsClusterSupported = ({ isSupported, children }) => { return isSupported ? children : '-'; @@ -191,7 +192,7 @@ const getColumns = ( if (!licenseType) { return (
-
N/A
+
N/A
); } @@ -209,7 +210,7 @@ const getColumns = ( return (
-
{capitalize(licenseType)}
+
{capitalize(licenseType)}
{showLicenseExpiration ? licenseExpiry() : null}
diff --git a/x-pack/plugins/monitoring/public/components/table/_table.scss b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.scss similarity index 82% rename from x-pack/plugins/monitoring/public/components/table/_table.scss rename to x-pack/plugins/monitoring/public/components/cluster/listing/listing.scss index 7c5ab4d51d999..035d9fe5729d6 100644 --- a/x-pack/plugins/monitoring/public/components/table/_table.scss +++ b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.scss @@ -9,12 +9,12 @@ color: $euiTextColor; } -.monTableCell__clusterCellLiscense { - font-size: 16px; +.monTableCell__clusterCellLicense { + font-size: $euiFontSize; } + .monTableCell__clusterCellExpiration { color: $euiColorDarkShade; - @include euiFontSizeS; } .monTableCell__name, @@ -28,8 +28,8 @@ } .monTableCell__transportAddress { - color: $euiColorDarkShade; @include euiFontSizeS; + color: $euiColorDarkShade; } .monTableCell__number { @@ -39,8 +39,3 @@ .monTableCell__splitNumber { @include euiFontSizeM; } - -.monElasticsearchIndicesTable__status { - display: flex; - align-items: center; -} diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/alerts_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/alerts_panel.js index ff311281b1247..87c548bc22efd 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/alerts_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/alerts_panel.js @@ -104,7 +104,8 @@ export function AlertsPanel({ alerts, changeUrl }) { changeUrl={changeUrl} /> -

+ +

{message}

-

+ +

{ const key = data.id; const initialClasses = ['monChild']; + if (data.type === 'index') { + initialClasses.push('monChild--index'); + } const shardStats = get(this.props.shardStats.indices, key); if (shardStats) { switch (shardStats.status) { diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/calculate_class.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/calculate_class.js index 23f70de3e262a..3acbfcb94d3a8 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/calculate_class.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/calculate_class.js @@ -10,6 +10,7 @@ export function calculateClass(item, initial) { classes.push(initial); } if (item.type === 'shard') { + classes.push('monShard'); classes.push((item.primary && 'primary') || 'replica'); classes.push(item.state.toLowerCase()); if (item.state === 'UNASSIGNED' && item.primary) { diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.js index c5ddfecb04488..af3b55bf92146 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.js @@ -9,6 +9,7 @@ import { EuiTitle, EuiBadge, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elast import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { ClusterView } from './components/cluster_view'; +import './shard_allocation.scss'; export const ShardAllocation = ({ scope, kbnUrl, type, shardStats }) => { const types = [ diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/_shard_allocation.scss b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.scss similarity index 85% rename from x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/_shard_allocation.scss rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.scss index 50e92d572908c..c46d7a048b93b 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/_shard_allocation.scss +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.scss @@ -7,7 +7,7 @@ .monCluster { .monUnassigned { vertical-align: middle; - width: 150px; + width: $euiSize * 10; } .monUnassigned__children, @@ -22,9 +22,9 @@ margin: $euiSizeS; border: 1px solid $euiColorMediumShade; border-radius: $euiSizeXS; - padding: $euiSizeXS/2 0; - // SASS-TODO: Rename this class following Eui conventions - &.index { + padding: $euiSizeXS / 2 0; + + &.monChild--index { border-left: $euiSizeXS solid $euiColorSuccess; &.monChild--danger { @@ -58,10 +58,10 @@ } td:first-child { - width: 200px; + width: $euiSize * 12.5; } - // SASS-TODO: Rename this class following Eui conventions - .shard { + + .monShard { align-self: center; padding: $euiSizeXS $euiSizeS; font-size: $euiFontSizeXS; diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap index 7a5f8e89e8620..b25f256f802ea 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap @@ -3,7 +3,7 @@ exports[`CollapsibleStatement component renders child components 1`] = ` diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap index db95a2bca5119..0bfdb6de50188 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap @@ -3,7 +3,7 @@ exports[`PluginStatement component adds warning highlight for cpu time 1`] = ` @@ -75,7 +75,7 @@ exports[`PluginStatement component adds warning highlight for cpu time 1`] = ` exports[`PluginStatement component adds warning highlight for event millis 1`] = ` @@ -147,7 +147,7 @@ exports[`PluginStatement component adds warning highlight for event millis 1`] = exports[`PluginStatement component does not render explicit id field if no id is specified 1`] = ` @@ -197,7 +197,7 @@ exports[`PluginStatement component does not render explicit id field if no id is exports[`PluginStatement component renders input metrics and explicit id fields 1`] = ` @@ -257,7 +257,7 @@ exports[`PluginStatement component renders input metrics and explicit id fields exports[`PluginStatement component renders processor statement metrics 1`] = ` diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_index.scss b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_index.scss deleted file mode 100644 index 6a50f7b76b501..0000000000000 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'pipeline_viewer'; diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_pipeline_viewer.scss b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_pipeline_viewer.scss deleted file mode 100644 index 64356c59944e4..0000000000000 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_pipeline_viewer.scss +++ /dev/null @@ -1,103 +0,0 @@ -.monPipelineViewer { - max-width: 1000px; -} - -.monPipelineViewer__statement { - padding-left: $euiSizeM; -} - -.monPipelineViewer__plugin { - margin-left: $euiSizeXS; -} - -.monPipelineViewer__spaceContainer { - background-color: $euiColorEmptyShade; - align-self: stretch; - display: flex; - // Separates the left border spaces properly - border-bottom: solid 2px $euiColorEmptyShade; -} - -.monPipelineViewer__spacer { - width: $euiSizeM; - align-self: stretch; - margin-left: $euiSizeM; - border-left: 1px $euiBorderColor dashed; - - // This allows the border to be flush - &:last-child { - width: 0px; - } - - &:first-child { - // Odd number is because of the single pixel border. - margin-left: $euiSizeL - 1px; - } -} - -.monPipelineViewer__metric { - text-align: right; - - &--cpuTime { - width: $euiSizeXXL; - } - - &--events, &--eventsEmitted { - width: $euiSizeXXL * 4; - } - - &--eventMillis { - width: $euiSizeXXL * 2; - } -} - -.monPipelineViewer__queueMessage { - margin-left: $euiSizeL; - color: $euiColorDarkShade; -} - -.monPipelineViewer__list { - .monPipelineViewer__listItem { - display: flex; - min-height: $euiSizeXL; - align-items: center; - padding-right: $euiSizeM; - - &:nth-child(2n+1) { - background: tintOrShade($euiColorLightestShade, 2%, 2%); - } - } -} - -.monPipelineViewer__conditional { - font-weight: bold; -} - -img.lspvDetailDrawerIcon { - display: inline; - margin: 0 $euiSizeXS 0 0; - width: auto; - vertical-align: middle; -} - -// SASSTODO: Why is this width here? -.lspvDetailDrawerSparklineContainer { - width: 7vw; -} - -@include euiBreakpoint('m') { - .monPipelineViewer { - .monPipelineViewer__spacer { - border: none; - } - - .monPipelineViewer__metricFlexItem { - margin-bottom: $euiSizeXS !important; - } - - .monPipelineViewer__metric { - text-align: left; - padding-left: $euiSizeXL; - } - } -} diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.js index 77fd484d2be6f..9fcd922d10b79 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.js @@ -8,6 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import './collapsible_statement.scss'; function getToggleIconType(isCollapsed) { return isCollapsed ? 'arrowRight' : 'arrowDown'; @@ -29,7 +30,7 @@ export function CollapsibleStatement(props) { responsive={false} gutterSize="none" alignItems="center" - className="monPipelineViewer__statement" + className="monPipelineViewer__collapsibleStatement" > ; diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.scss b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.scss new file mode 100644 index 0000000000000..061ac275ff74d --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.scss @@ -0,0 +1,4 @@ +// SASSTODO: Why is this width here? +.lspvDetailDrawerSparklineContainer { + width: 7vw; +} diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.js index cf9f0c8b02878..6bc8caab08688 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.js @@ -8,6 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiFlexItem, EuiBadge, EuiText } from '@elastic/eui'; import classNames from 'classnames'; +import './metric.scss'; export function Metric({ className, warning, value }) { const classes = classNames('monPipelineViewer__metric', className); diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.scss b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.scss new file mode 100644 index 0000000000000..71b16cdaf3ff4 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.scss @@ -0,0 +1,27 @@ +.monPipelineViewer__metric { + text-align: right; + + &--cpuTime { + width: $euiSizeXXL; + } + + &--events, + &--eventsEmitted { + width: $euiSizeXXL * 4; + } + + &--eventMillis { + width: $euiSizeXXL * 2; + } +} + +@include euiBreakpoint('m') { + .monPipelineViewer__metricFlexItem { + margin-bottom: $euiSizeXS !important; // sass-lint:disable-line no-important + } + + .monPipelineViewer__metric { + text-align: left; + padding-left: $euiSizeXL; + } +} diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.js index 2f5968ee4737e..2061860c98ed8 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.js @@ -10,6 +10,7 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiBadge } from '@elastic/eu import { formatMetric } from '../../../../lib/format_number'; import { Metric } from './metric'; import { i18n } from '@kbn/i18n'; +import './plugin_statement.scss'; function getInputStatementMetrics({ latestEventsPerSecond }) { return [ @@ -69,7 +70,7 @@ export function PluginStatement({ return ( diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.scss b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.scss new file mode 100644 index 0000000000000..564c1ab615a16 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.scss @@ -0,0 +1,7 @@ +.monPipelineViewer__plugin { + margin-left: $euiSizeXS; +} + +.monPipelineViewer__pluginStatement { + padding-left: $euiSizeM; +} diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.js index c3fd9986474cf..3fd9c6e333d7f 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.js @@ -8,6 +8,7 @@ import React from 'react'; import { StatementListHeading } from './statement_list_heading'; import { EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import './queue.scss'; export function Queue() { return ( diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.scss b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.scss new file mode 100644 index 0000000000000..60bac9f41b991 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.scss @@ -0,0 +1,4 @@ +.monPipelineViewer__queueMessage { + margin-left: $euiSizeL; + color: $euiColorDarkShade; +} diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.js index 6b763500bf6de..4553aeb07ed19 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.js @@ -11,6 +11,7 @@ import { PluginStatement as PluginStatementModel } from '../models/pipeline/plug import { CollapsibleStatement } from './collapsible_statement'; import { IfElement } from '../models/list/if_element'; import { PluginStatement } from './plugin_statement'; +import './statement.scss'; function renderStatementName(name, onVertexSelected) { return ( diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.scss b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.scss new file mode 100644 index 0000000000000..00d139463af50 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.scss @@ -0,0 +1,47 @@ +.monPipelineViewer__spaceContainer { + background-color: $euiColorEmptyShade; + align-self: stretch; + display: flex; + // Separates the left border spaces properly + border-bottom: solid 2px $euiColorEmptyShade; +} + +.monPipelineViewer__spacer { + width: $euiSizeM; + align-self: stretch; + margin-left: $euiSizeM; + border-left: 1px $euiBorderColor dashed; + + // This allows the border to be flush + &:last-child { + width: 0; + } + + &:first-child { + // Odd number is because of the single pixel border. + margin-left: $euiSizeL - 1px; + } +} + +.monPipelineViewer__list { + .monPipelineViewer__listItem { + display: flex; + min-height: $euiSizeXL; + align-items: center; + padding-right: $euiSizeM; + + &:nth-child(2n + 1) { + background: tintOrShade($euiColorLightestShade, 2%, 2%); + } + } +} + +.monPipelineViewer__conditional { + font-weight: bold; +} + +@include euiBreakpoint('m') { + .monPipelineViewer__spacer { + border: none; + } +} diff --git a/x-pack/plugins/monitoring/public/components/no_data/_index.scss b/x-pack/plugins/monitoring/public/components/no_data/_index.scss deleted file mode 100644 index bc9b5d065ebd9..0000000000000 --- a/x-pack/plugins/monitoring/public/components/no_data/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'no_data'; diff --git a/x-pack/plugins/monitoring/public/components/no_data/_no_data.scss b/x-pack/plugins/monitoring/public/components/no_data/_no_data.scss deleted file mode 100644 index efd370c7bc7cb..0000000000000 --- a/x-pack/plugins/monitoring/public/components/no_data/_no_data.scss +++ /dev/null @@ -1,5 +0,0 @@ -.noData__content { - max-width: 600px; - text-align: center; - position: relative; -} diff --git a/x-pack/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap b/x-pack/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap index 7b468d08e29a6..f4d8232b2e340 100644 --- a/x-pack/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap @@ -9,7 +9,7 @@ exports[`PageLoading should show a simple page loading component 1`] = ` class="euiPageBody" >

diff --git a/x-pack/plugins/monitoring/public/components/page_loading/page_loading.scss b/x-pack/plugins/monitoring/public/components/page_loading/page_loading.scss new file mode 100644 index 0000000000000..ebe0defecb02a --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/page_loading/page_loading.scss @@ -0,0 +1,5 @@ +.monNoData__content { + max-width: $euiSizeM * 50; + text-align: center; + position: relative; +} diff --git a/x-pack/plugins/monitoring/public/components/setup_mode/_index.scss b/x-pack/plugins/monitoring/public/components/setup_mode/_index.scss deleted file mode 100644 index b9c218fc4f39c..0000000000000 --- a/x-pack/plugins/monitoring/public/components/setup_mode/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'enter_button'; diff --git a/x-pack/plugins/monitoring/public/components/setup_mode/_enter_button.scss b/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.scss similarity index 100% rename from x-pack/plugins/monitoring/public/components/setup_mode/_enter_button.scss rename to x-pack/plugins/monitoring/public/components/setup_mode/enter_button.scss diff --git a/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.tsx b/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.tsx index 8adcb635a6559..1edfdddeaae99 100644 --- a/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.tsx +++ b/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import './enter_button.scss'; export interface SetupModeEnterButtonProps { enabled: boolean; diff --git a/x-pack/plugins/monitoring/public/components/sparkline/_index.scss b/x-pack/plugins/monitoring/public/components/sparkline/_index.scss deleted file mode 100644 index 8fd74e94d91a2..0000000000000 --- a/x-pack/plugins/monitoring/public/components/sparkline/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'sparkline'; diff --git a/x-pack/plugins/monitoring/public/components/sparkline/index.js b/x-pack/plugins/monitoring/public/components/sparkline/index.js index db210de5300f6..c424e5a0b897e 100644 --- a/x-pack/plugins/monitoring/public/components/sparkline/index.js +++ b/x-pack/plugins/monitoring/public/components/sparkline/index.js @@ -8,6 +8,7 @@ import { isEqual } from 'lodash'; import React from 'react'; import PropTypes from 'prop-types'; import { SparklineFlotChart } from './sparkline_flot_chart'; +import './sparkline.scss'; export class Sparkline extends React.Component { constructor(props) { diff --git a/x-pack/plugins/monitoring/public/components/sparkline/_sparkline.scss b/x-pack/plugins/monitoring/public/components/sparkline/sparkline.scss similarity index 100% rename from x-pack/plugins/monitoring/public/components/sparkline/_sparkline.scss rename to x-pack/plugins/monitoring/public/components/sparkline/sparkline.scss diff --git a/x-pack/plugins/monitoring/public/components/summary_status/__snapshots__/summary_status.test.js.snap b/x-pack/plugins/monitoring/public/components/summary_status/__snapshots__/summary_status.test.js.snap index feb0f7297fb47..5a0ee905f92e4 100644 --- a/x-pack/plugins/monitoring/public/components/summary_status/__snapshots__/summary_status.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/summary_status/__snapshots__/summary_status.test.js.snap @@ -6,13 +6,14 @@ exports[`Summary Status Component should allow label to be optional 1`] = ` intl="[object Object]" >
@@ -47,9 +48,10 @@ exports[`Summary Status Component should allow label to be optional 1`] = `
@@ -74,9 +76,10 @@ exports[`Summary Status Component should allow label to be optional 1`] = `
@@ -110,14 +113,15 @@ exports[`Summary Status Component should allow status to be optional 1`] = ` intl="[object Object]" >
@@ -144,9 +148,10 @@ exports[`Summary Status Component should allow status to be optional 1`] = `
@@ -180,13 +185,14 @@ exports[`Summary Status Component should render metrics in a summary bar 1`] = ` intl="[object Object]" >
@@ -221,9 +227,10 @@ exports[`Summary Status Component should render metrics in a summary bar 1`] = `
@@ -250,9 +257,10 @@ exports[`Summary Status Component should render metrics in a summary bar 1`] = `
diff --git a/x-pack/plugins/monitoring/public/components/summary_status/_index.scss b/x-pack/plugins/monitoring/public/components/summary_status/_index.scss deleted file mode 100644 index 6f0c842776263..0000000000000 --- a/x-pack/plugins/monitoring/public/components/summary_status/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'summary_status'; diff --git a/x-pack/plugins/monitoring/public/components/summary_status/_summary_status.scss b/x-pack/plugins/monitoring/public/components/summary_status/_summary_status.scss deleted file mode 100644 index 4bda98876d651..0000000000000 --- a/x-pack/plugins/monitoring/public/components/summary_status/_summary_status.scss +++ /dev/null @@ -1,13 +0,0 @@ -.monSummaryStatusNoWrap { - margin-left: $euiSizeM; - margin-right: $euiSizeM; - .euiTitle { - overflow-x: hidden; - white-space: nowrap; - @include euiFontSizeXS; - } - - .euiFlexItem { - margin: $euiSizeS; - } -} diff --git a/x-pack/plugins/monitoring/public/components/summary_status/summary_status.js b/x-pack/plugins/monitoring/public/components/summary_status/summary_status.js index 957ce50ebed95..943e100dc5409 100644 --- a/x-pack/plugins/monitoring/public/components/summary_status/summary_status.js +++ b/x-pack/plugins/monitoring/public/components/summary_status/summary_status.js @@ -11,10 +11,22 @@ import { EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; import { StatusIcon } from '../status_icon/index.js'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import './summary_status.scss'; const wrapChild = ({ label, value, ...props }, index) => ( - - + + ); @@ -46,7 +58,12 @@ const StatusIndicator = ({ status, isOnline, IconComponent }) => { } return ( - + @@ -55,8 +72,9 @@ const StatusIndicator = ({ status, isOnline, IconComponent }) => { {capitalize(status)} } - titleSize="xs" + titleSize="xxxs" textAlign="left" + className="monSummaryStatusNoWrap__stat" description={i18n.translate('xpack.monitoring.summaryStatus.statusDescription', { defaultMessage: 'Status', })} @@ -74,7 +92,7 @@ export function SummaryStatus({ }) { return (
- + {metrics.map(wrapChild)} diff --git a/x-pack/plugins/monitoring/public/components/summary_status/summary_status.scss b/x-pack/plugins/monitoring/public/components/summary_status/summary_status.scss new file mode 100644 index 0000000000000..24577c84e701d --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/summary_status/summary_status.scss @@ -0,0 +1,10 @@ +.monSummaryStatusNoWrap { + margin-left: $euiSizeM; + margin-right: $euiSizeM; + + .monSummaryStatusNoWrap__stat { + p { + @include euiTextTruncate; + } + } +} diff --git a/x-pack/plugins/monitoring/public/components/table/_index.scss b/x-pack/plugins/monitoring/public/components/table/_index.scss deleted file mode 100644 index 999e0d69e2480..0000000000000 --- a/x-pack/plugins/monitoring/public/components/table/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'table'; diff --git a/x-pack/plugins/monitoring/public/index.scss b/x-pack/plugins/monitoring/public/index.scss index 4dda80ee7454b..e25885debebdd 100644 --- a/x-pack/plugins/monitoring/public/index.scss +++ b/x-pack/plugins/monitoring/public/index.scss @@ -1,9 +1,3 @@ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - -// Temporary hacks -@import 'hacks'; - // Monitoring plugin styles // Prefix all styles with "mon" to avoid conflicts. @@ -13,17 +7,7 @@ // monChart__legend--small // monChart__legend-isLoading -@import 'components/chart/index'; -@import 'components/no_data/index'; -@import 'components/sparkline/index'; -@import 'components/summary_status/index'; -@import 'components/table/index'; -@import 'components/logstash/pipeline_viewer/views/index'; -@import 'components/elasticsearch/shard_allocation/index'; -@import 'components/setup_mode/index'; -@import 'components/elasticsearch/ccr/index'; - -.monitoringApplicationWrapper { +.monApplicationWrapper { display: flex; flex-direction: column; flex-grow: 1; diff --git a/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts b/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts index 246c605f4bfe6..3ce5329e42517 100644 --- a/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts @@ -28,22 +28,25 @@ import { getChromeLogLocation } from '../paths'; import { puppeteerLaunch } from '../puppeteer'; import { args } from './args'; -type binaryPath = string; type BrowserConfig = CaptureConfig['browser']['chromium']; type ViewportConfig = CaptureConfig['viewport']; export class HeadlessChromiumDriverFactory { - private binaryPath: binaryPath; + private binaryPath: string; private captureConfig: CaptureConfig; private browserConfig: BrowserConfig; private userDataDir: string; private getChromiumArgs: (viewport: ViewportConfig) => string[]; - constructor(binaryPath: binaryPath, logger: LevelLogger, captureConfig: CaptureConfig) { + constructor(binaryPath: string, captureConfig: CaptureConfig, logger: LevelLogger) { this.binaryPath = binaryPath; this.captureConfig = captureConfig; this.browserConfig = captureConfig.browser.chromium; + if (this.browserConfig.disableSandbox) { + logger.warning(`Enabling the Chromium sandbox provides an additional layer of protection.`); + } + this.userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chromium-')); this.getChromiumArgs = (viewport: ViewportConfig) => args({ diff --git a/x-pack/plugins/reporting/server/browsers/chromium/index.ts b/x-pack/plugins/reporting/server/browsers/chromium/index.ts index 5f89662c94da2..cebcd228b01c3 100644 --- a/x-pack/plugins/reporting/server/browsers/chromium/index.ts +++ b/x-pack/plugins/reporting/server/browsers/chromium/index.ts @@ -4,16 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import { BrowserDownload } from '../'; import { CaptureConfig } from '../../../server/types'; import { LevelLogger } from '../../lib'; import { HeadlessChromiumDriverFactory } from './driver_factory'; +import { paths } from './paths'; -export { paths } from './paths'; - -export async function createDriverFactory( - binaryPath: string, - logger: LevelLogger, - captureConfig: CaptureConfig -): Promise { - return new HeadlessChromiumDriverFactory(binaryPath, logger, captureConfig); -} +export const chromium: BrowserDownload = { + paths, + createDriverFactory: (binaryPath: string, captureConfig: CaptureConfig, logger: LevelLogger) => + new HeadlessChromiumDriverFactory(binaryPath, captureConfig, logger), +}; diff --git a/x-pack/plugins/reporting/server/browsers/create_browser_driver_factory.ts b/x-pack/plugins/reporting/server/browsers/create_browser_driver_factory.ts deleted file mode 100644 index f3486a48ba7b1..0000000000000 --- a/x-pack/plugins/reporting/server/browsers/create_browser_driver_factory.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { first } from 'rxjs/operators'; -import { ReportingConfig } from '../'; -import { LevelLogger } from '../lib'; -import { HeadlessChromiumDriverFactory } from './chromium/driver_factory'; -import { ensureBrowserDownloaded } from './download'; -import { chromium } from './index'; -import { installBrowser } from './install'; - -export async function createBrowserDriverFactory( - config: ReportingConfig, - logger: LevelLogger -): Promise { - const captureConfig = config.get('capture'); - const browserConfig = captureConfig.browser.chromium; - const browserAutoDownload = captureConfig.browser.autoDownload; - const browserType = captureConfig.browser.type; - const dataDir = await config.kbnConfig.get('path', 'data').pipe(first()).toPromise(); - - if (browserConfig.disableSandbox) { - logger.warning(`Enabling the Chromium sandbox provides an additional layer of protection.`); - } - if (browserAutoDownload) { - await ensureBrowserDownloaded(browserType, logger); - } - - try { - const { binaryPath } = await installBrowser(logger, chromium, dataDir); - return chromium.createDriverFactory(binaryPath, logger, captureConfig); - } catch (error) { - if (error.cause && ['EACCES', 'EEXIST'].includes(error.cause.code)) { - logger.error( - `Error code ${error.cause.code}: Insufficient permissions for extracting the browser archive. ` + - `Make sure the Kibana data directory (path.data) is owned by the same user that is running Kibana.` - ); - } - - throw error; // reject the promise with the original error - } -} diff --git a/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts b/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts index b334510d71947..add14448e2f1d 100644 --- a/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts +++ b/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts @@ -56,7 +56,7 @@ async function ensureDownloaded(browsers: BrowserDownload[], logger: LevelLogger const path = resolvePath(archivesPath, archiveFilename); if (existsSync(path) && (await md5(path)) === archiveChecksum) { - logger.info(`Browser archive exists in ${path}`); + logger.debug(`Browser archive exists in ${path}`); return; } diff --git a/x-pack/plugins/reporting/server/browsers/index.ts b/x-pack/plugins/reporting/server/browsers/index.ts index 7f6e40fb433b6..be5b869ba523b 100644 --- a/x-pack/plugins/reporting/server/browsers/index.ts +++ b/x-pack/plugins/reporting/server/browsers/index.ts @@ -4,20 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as chromiumDefinition from './chromium'; +import { first } from 'rxjs/operators'; +import { LevelLogger } from '../lib'; +import { CaptureConfig } from '../types'; +import { chromium } from './chromium'; +import { HeadlessChromiumDriverFactory } from './chromium/driver_factory'; +import { installBrowser } from './install'; +import { ReportingConfig } from '..'; export { ensureAllBrowsersDownloaded } from './download'; -export { createBrowserDriverFactory } from './create_browser_driver_factory'; - export { HeadlessChromiumDriver } from './chromium/driver'; export { HeadlessChromiumDriverFactory } from './chromium/driver_factory'; +export { chromium } from './chromium'; -export const chromium = { - paths: chromiumDefinition.paths, - createDriverFactory: chromiumDefinition.createDriverFactory, -}; +type CreateDriverFactory = ( + binaryPath: string, + captureConfig: CaptureConfig, + logger: LevelLogger +) => HeadlessChromiumDriverFactory; export interface BrowserDownload { + createDriverFactory: CreateDriverFactory; paths: { archivesPath: string; baseUrl: string; @@ -30,3 +37,13 @@ export interface BrowserDownload { }>; }; } + +export const initializeBrowserDriverFactory = async ( + config: ReportingConfig, + logger: LevelLogger +) => { + const { binaryPath$ } = installBrowser(chromium, config, logger); + const binaryPath = await binaryPath$.pipe(first()).toPromise(); + const captureConfig = config.get('capture'); + return chromium.createDriverFactory(binaryPath, captureConfig, logger); +}; diff --git a/x-pack/plugins/reporting/server/browsers/install.ts b/x-pack/plugins/reporting/server/browsers/install.ts index 01526af307022..49361b7b6014d 100644 --- a/x-pack/plugins/reporting/server/browsers/install.ts +++ b/x-pack/plugins/reporting/server/browsers/install.ts @@ -6,9 +6,13 @@ import fs from 'fs'; import path from 'path'; +import * as Rx from 'rxjs'; +import { first } from 'rxjs/operators'; import { promisify } from 'util'; +import { ReportingConfig } from '../'; import { LevelLogger } from '../lib'; import { BrowserDownload } from './'; +import { ensureBrowserDownloaded } from './download'; // @ts-ignore import { md5 } from './download/checksum'; // @ts-ignore @@ -19,37 +23,60 @@ const chmod = promisify(fs.chmod); interface Package { platforms: string[]; } -interface PathResponse { - binaryPath: string; -} /** * "install" a browser by type into installs path by extracting the downloaded * archive. If there is an error extracting the archive an `ExtractError` is thrown */ -export async function installBrowser( - logger: LevelLogger, +export function installBrowser( browser: BrowserDownload, - installsPath: string -): Promise { - const pkg = browser.paths.packages.find((p: Package) => p.platforms.includes(process.platform)); + config: ReportingConfig, + logger: LevelLogger +): { binaryPath$: Rx.Subject } { + const binaryPath$ = new Rx.Subject(); + const backgroundInstall = async () => { + const captureConfig = config.get('capture'); + const { autoDownload, type: browserType } = captureConfig.browser; + if (autoDownload) { + await ensureBrowserDownloaded(browserType, logger); + } + + const pkg = browser.paths.packages.find((p: Package) => p.platforms.includes(process.platform)); + if (!pkg) { + throw new Error(`Unsupported platform: ${JSON.stringify(browser, null, 2)}`); + } + + const dataDir = await config.kbnConfig.get('path', 'data').pipe(first()).toPromise(); + const binaryPath = path.join(dataDir, pkg.binaryRelativePath); + + try { + const binaryChecksum = await md5(binaryPath).catch(() => ''); - if (!pkg) { - throw new Error(`Unsupported platform: ${JSON.stringify(browser, null, 2)}`); - } + if (binaryChecksum !== pkg.binaryChecksum) { + const archive = path.join(browser.paths.archivesPath, pkg.archiveFilename); + logger.info(`Extracting [${archive}] to [${binaryPath}]`); + await extract(archive, dataDir); + await chmod(binaryPath, '755'); + } + } catch (error) { + if (error.cause && ['EACCES', 'EEXIST'].includes(error.cause.code)) { + logger.error( + `Error code ${error.cause.code}: Insufficient permissions for extracting the browser archive. ` + + `Make sure the Kibana data directory (path.data) is owned by the same user that is running Kibana.` + ); + } - const binaryPath = path.join(installsPath, pkg.binaryRelativePath); - const binaryChecksum = await md5(binaryPath).catch(() => ''); + throw error; // reject the promise with the original error + } + + logger.debug(`Browser executable: ${binaryPath}`); + + binaryPath$.next(binaryPath); // subscribers wait for download and extract to complete + }; - if (binaryChecksum !== pkg.binaryChecksum) { - const archive = path.join(browser.paths.archivesPath, pkg.archiveFilename); - logger.debug(`Extracting [${archive}] to [${binaryPath}]`); - await extract(archive, installsPath); - await chmod(binaryPath, '755'); - } + backgroundInstall(); - logger.debug(`Browser installed at ${binaryPath}`); return { - binaryPath, + binaryPath$, }; } diff --git a/x-pack/plugins/reporting/server/config/config.ts b/x-pack/plugins/reporting/server/config/config.ts index 4142ab6f0ae43..2a09ebea9619c 100644 --- a/x-pack/plugins/reporting/server/config/config.ts +++ b/x-pack/plugins/reporting/server/config/config.ts @@ -4,10 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Observable } from 'rxjs'; import { get } from 'lodash'; -import { map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { first, map } from 'rxjs/operators'; import { CoreSetup, PluginInitializerContext } from 'src/core/server'; +import { LevelLogger } from '../lib'; +import { createConfig$ } from './create_config'; import { ReportingConfigType } from './schema'; // make config.get() aware of the value type it returns @@ -55,11 +57,12 @@ export interface ReportingConfig extends Config { kbnConfig: Config; } -export const buildConfig = ( +export const buildConfig = async ( initContext: PluginInitializerContext, core: CoreSetup, - reportingConfig: ReportingConfigType -): ReportingConfig => { + logger: LevelLogger +): Promise => { + const config$ = initContext.config.create(); const { http } = core; const serverInfo = http.getServerInfo(); @@ -77,6 +80,8 @@ export const buildConfig = ( }, }; + const reportingConfig$ = createConfig$(core, config$, logger); + const reportingConfig = await reportingConfig$.pipe(first()).toPromise(); return { get: (...keys: string[]) => get(reportingConfig, keys.join('.'), null), // spreading arguments as an array allows the return type to be known by the compiler kbnConfig: { diff --git a/x-pack/plugins/reporting/server/config/create_config.test.ts b/x-pack/plugins/reporting/server/config/create_config.test.ts index 1c4c840cfa312..8ad8042a93105 100644 --- a/x-pack/plugins/reporting/server/config/create_config.test.ts +++ b/x-pack/plugins/reporting/server/config/create_config.test.ts @@ -45,7 +45,11 @@ describe('Reporting server createConfig$', () => { mockInitContext = makeMockInitContext({ kibanaServer: {}, }); - mockLogger = ({ warn: jest.fn(), debug: jest.fn() } as unknown) as LevelLogger; + mockLogger = ({ + warn: jest.fn(), + debug: jest.fn(), + clone: jest.fn().mockImplementation(() => mockLogger), + } as unknown) as LevelLogger; }); afterEach(() => { diff --git a/x-pack/plugins/reporting/server/config/create_config.ts b/x-pack/plugins/reporting/server/config/create_config.ts index 67df7dacc77ab..5c66bd599dd9a 100644 --- a/x-pack/plugins/reporting/server/config/create_config.ts +++ b/x-pack/plugins/reporting/server/config/create_config.ts @@ -23,8 +23,9 @@ import { ReportingConfigType } from './schema'; export function createConfig$( core: CoreSetup, config$: Observable, - logger: LevelLogger + parentLogger: LevelLogger ) { + const logger = parentLogger.clone(['config']); return config$.pipe( map((config) => { // encryption key diff --git a/x-pack/plugins/reporting/server/config/index.ts b/x-pack/plugins/reporting/server/config/index.ts index caa64a7414005..a89b952702e1b 100644 --- a/x-pack/plugins/reporting/server/config/index.ts +++ b/x-pack/plugins/reporting/server/config/index.ts @@ -7,7 +7,6 @@ import { PluginConfigDescriptor } from 'kibana/server'; import { ConfigSchema, ReportingConfigType } from './schema'; export { buildConfig } from './config'; -export { createConfig$ } from './create_config'; export { ConfigSchema, ReportingConfigType }; export const config: PluginConfigDescriptor = { diff --git a/x-pack/plugins/reporting/server/core.ts b/x-pack/plugins/reporting/server/core.ts index e7786b3b753fb..94b138ffcae0b 100644 --- a/x-pack/plugins/reporting/server/core.ts +++ b/x-pack/plugins/reporting/server/core.ts @@ -26,7 +26,6 @@ import { ESQueueInstance } from './lib/create_queue'; import { EnqueueJobFn } from './lib/enqueue_job'; export interface ReportingInternalSetup { - browserDriverFactory: HeadlessChromiumDriverFactory; elasticsearch: ElasticsearchServiceSetup; licensing: LicensingPluginSetup; basePath: BasePath['get']; @@ -44,6 +43,7 @@ interface ReportingInternalStart { export class ReportingCore { private pluginSetupDeps?: ReportingInternalSetup; private pluginStartDeps?: ReportingInternalStart; + private browserDriverFactory?: HeadlessChromiumDriverFactory; private readonly pluginSetup$ = new Rx.ReplaySubject(); private readonly pluginStart$ = new Rx.ReplaySubject(); private exportTypesRegistry = getExportTypesRegistry(); @@ -63,6 +63,10 @@ export class ReportingCore { return this.pluginStart$.pipe(first(), mapTo(true)).toPromise(); } + public setBrowserDriverFactory(browserDriverFactory: HeadlessChromiumDriverFactory) { + this.browserDriverFactory = browserDriverFactory; + } + /* * Internal module dependencies */ @@ -93,7 +97,10 @@ export class ReportingCore { } public getScreenshotsObservable(): ScreenshotsObservableFn { - const { browserDriverFactory } = this.getPluginSetupDeps(); + const { browserDriverFactory } = this; + if (!browserDriverFactory) { + throw new Error(`"browserDriverFactory" dependency hasn't initialized yet`); + } return screenshotsObservableFactory(this.config.get('capture'), browserDriverFactory); } diff --git a/x-pack/plugins/reporting/server/lib/validate/index.ts b/x-pack/plugins/reporting/server/lib/validate/index.ts index 404cbcda31a09..7c439d6023d5f 100644 --- a/x-pack/plugins/reporting/server/lib/validate/index.ts +++ b/x-pack/plugins/reporting/server/lib/validate/index.ts @@ -7,8 +7,8 @@ import { i18n } from '@kbn/i18n'; import { ElasticsearchServiceSetup } from 'kibana/server'; import { ReportingConfig } from '../../'; -import { LevelLogger } from '../../lib'; import { HeadlessChromiumDriverFactory } from '../../browsers/chromium/driver_factory'; +import { LevelLogger } from '../../lib'; import { validateBrowser } from './validate_browser'; import { validateMaxContentLength } from './validate_max_content_length'; @@ -16,8 +16,9 @@ export async function runValidations( config: ReportingConfig, elasticsearch: ElasticsearchServiceSetup, browserFactory: HeadlessChromiumDriverFactory, - logger: LevelLogger + parentLogger: LevelLogger ) { + const logger = parentLogger.clone(['validations']); try { await Promise.all([ validateBrowser(browserFactory, logger), diff --git a/x-pack/plugins/reporting/server/plugin.test.ts b/x-pack/plugins/reporting/server/plugin.test.ts new file mode 100644 index 0000000000000..b2bcd6b9c97ce --- /dev/null +++ b/x-pack/plugins/reporting/server/plugin.test.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +jest.mock('./browsers/install', () => ({ + installBrowser: jest.fn().mockImplementation(() => ({ + binaryPath$: { + pipe: jest.fn().mockImplementation(() => ({ + toPromise: () => Promise.resolve(), + })), + }, + })), +})); + +import { coreMock } from 'src/core/server/mocks'; +import { ReportingPlugin } from './plugin'; +import { createMockConfigSchema } from './test_helpers'; + +const sleep = (time: number) => new Promise((r) => setTimeout(r, time)); + +describe('Reporting Plugin', () => { + let configSchema: any; + let initContext: any; + let coreSetup: any; + let coreStart: any; + let pluginSetup: any; + let pluginStart: any; + + beforeEach(async () => { + configSchema = createMockConfigSchema(); + initContext = coreMock.createPluginInitializerContext(configSchema); + coreSetup = await coreMock.createSetup(configSchema); + coreStart = await coreMock.createStart(); + pluginSetup = ({ + licensing: {}, + usageCollection: { + makeUsageCollector: jest.fn(), + registerCollector: jest.fn(), + }, + security: { + authc: { + getCurrentUser: () => ({ + id: '123', + roles: ['superuser'], + username: 'Tom Riddle', + }), + }, + }, + } as unknown) as any; + pluginStart = ({ + data: { + fieldFormats: {}, + }, + } as unknown) as any; + }); + + it('has a sync setup process', () => { + const plugin = new ReportingPlugin(initContext); + + expect(plugin.setup(coreSetup, pluginSetup)).not.toHaveProperty('then'); + }); + + it('logs setup issues', async () => { + const plugin = new ReportingPlugin(initContext); + // @ts-ignore overloading error logger + plugin.logger.error = jest.fn(); + coreSetup.elasticsearch = null; + plugin.setup(coreSetup, pluginSetup); + + await sleep(5); + + // @ts-ignore overloading error logger + expect(plugin.logger.error.mock.calls[0][0]).toMatch( + /Error in Reporting setup, reporting may not function properly/ + ); + // @ts-ignore overloading error logger + expect(plugin.logger.error).toHaveBeenCalledTimes(2); + }); + + it('has a sync startup process', async () => { + const plugin = new ReportingPlugin(initContext); + plugin.setup(coreSetup, pluginSetup); + await sleep(5); + expect(plugin.start(coreStart, pluginStart)).not.toHaveProperty('then'); + }); + + it('logs start issues', async () => { + const plugin = new ReportingPlugin(initContext); + // @ts-ignore overloading error logger + plugin.logger.error = jest.fn(); + plugin.setup(coreSetup, pluginSetup); + await sleep(5); + plugin.start(null as any, pluginStart); + await sleep(10); + // @ts-ignore overloading error logger + expect(plugin.logger.error.mock.calls[0][0]).toMatch( + /Error in Reporting start, reporting may not function properly/ + ); + // @ts-ignore overloading error logger + expect(plugin.logger.error).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index d0d25f6d9e0ae..a3c89c7b8a8ce 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -4,13 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Observable } from 'rxjs'; -import { first } from 'rxjs/operators'; +import * as Rx from 'rxjs'; import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/server'; -import { ReportingCore } from './core'; -import { ReportingConfigType } from './config'; -import { createBrowserDriverFactory } from './browsers'; -import { buildConfig, createConfig$ } from './config'; +import { ReportingCore } from './'; +import { initializeBrowserDriverFactory } from './browsers'; +import { buildConfig, ReportingConfigType } from './config'; import { createQueueFactory, enqueueJobFactory, LevelLogger, runValidations } from './lib'; import { registerRoutes } from './routes'; import { setFieldFormats } from './services'; @@ -22,61 +20,83 @@ export class ReportingPlugin private readonly initializerContext: PluginInitializerContext; private logger: LevelLogger; private reportingCore?: ReportingCore; - private config$: Observable; + + // Setup some observables for modules that need to await setup/start + public readonly setup$ = new Rx.Subject(); + public readonly start$ = new Rx.Subject(); constructor(context: PluginInitializerContext) { this.logger = new LevelLogger(context.logger.get()); this.initializerContext = context; - this.config$ = context.config.create(); } - public async setup(core: CoreSetup, plugins: ReportingSetupDeps) { + public setup(core: CoreSetup, plugins: ReportingSetupDeps) { const { elasticsearch, http } = core; const { licensing, security } = plugins; const { initializerContext: initContext } = this; const router = http.createRouter(); const basePath = http.basePath.get; - const coreConfig = await createConfig$(core, this.config$, this.logger) - .pipe(first()) - .toPromise(); // apply computed defaults to config - const reportingConfig = buildConfig(initContext, core, coreConfig); // combine kbnServer configs - this.reportingCore = new ReportingCore(reportingConfig); - - const browserDriverFactory = await createBrowserDriverFactory(reportingConfig, this.logger); - - this.reportingCore.pluginSetup({ - browserDriverFactory, - elasticsearch, - licensing, - basePath, - router, - security, - }); + // async background setup + (async () => { + const config = await buildConfig(initContext, core, this.logger); + const reportingCore = new ReportingCore(config); + + reportingCore.pluginSetup({ + elasticsearch, + licensing, + basePath, + router, + security, + }); - runValidations(reportingConfig, elasticsearch, browserDriverFactory, this.logger); - registerReportingUsageCollector(this.reportingCore, plugins); - registerRoutes(this.reportingCore, this.logger); + registerReportingUsageCollector(reportingCore, plugins); + registerRoutes(reportingCore, this.logger); + this.reportingCore = reportingCore; + + this.logger.debug('Setup complete'); + this.setup$.next(true); + })().catch((e) => { + this.logger.error(`Error in Reporting setup, reporting may not function properly`); + this.logger.error(e); + }); return {}; } - public async start(core: CoreStart, plugins: ReportingStartDeps) { + public start(core: CoreStart, plugins: ReportingStartDeps) { + // use data plugin for csv formats + setFieldFormats(plugins.data.fieldFormats); + const { logger } = this; const reportingCore = this.getReportingCore(); + const config = reportingCore.getConfig(); + const { elasticsearch } = reportingCore.getPluginSetupDeps(); - const esqueue = await createQueueFactory(reportingCore, logger); - const enqueueJob = enqueueJobFactory(reportingCore, logger); + // async background start + (async () => { + const browserDriverFactory = await initializeBrowserDriverFactory(config, logger); + reportingCore.setBrowserDriverFactory(browserDriverFactory); - reportingCore.pluginStart({ - savedObjects: core.savedObjects, - uiSettings: core.uiSettings, - esqueue, - enqueueJob, - }); + const esqueue = await createQueueFactory(reportingCore, logger); + const enqueueJob = enqueueJobFactory(reportingCore, logger); - setFieldFormats(plugins.data.fieldFormats); - logger.info('reporting plugin started'); + reportingCore.pluginStart({ + savedObjects: core.savedObjects, + uiSettings: core.uiSettings, + esqueue, + enqueueJob, + }); + + // run self-check validations + runValidations(config, elasticsearch, browserDriverFactory, this.logger); + + this.logger.debug('Start complete'); + this.start$.next(true); + })().catch((e) => { + this.logger.error(`Error in Reporting start, reporting may not function properly`); + this.logger.error(e); + }); return {}; } diff --git a/x-pack/plugins/reporting/server/routes/jobs.ts b/x-pack/plugins/reporting/server/routes/jobs.ts index 8c35f79ec0fb4..29cf55bc5c72e 100644 --- a/x-pack/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/jobs.ts @@ -22,7 +22,7 @@ interface ListQuery { } const MAIN_ENTRY = `${API_BASE_URL}/jobs`; -export async function registerJobInfoRoutes(reporting: ReportingCore) { +export function registerJobInfoRoutes(reporting: ReportingCore) { const config = reporting.getConfig(); const setupDeps = reporting.getPluginSetupDeps(); const userHandler = authorizedUserPreRoutingFactory(reporting); diff --git a/x-pack/plugins/reporting/server/test_helpers/create_mock_browserdriverfactory.ts b/x-pack/plugins/reporting/server/test_helpers/create_mock_browserdriverfactory.ts index 5b0d740e031ab..97e22e2ca2863 100644 --- a/x-pack/plugins/reporting/server/test_helpers/create_mock_browserdriverfactory.ts +++ b/x-pack/plugins/reporting/server/test_helpers/create_mock_browserdriverfactory.ts @@ -6,8 +6,7 @@ import { Page } from 'puppeteer'; import * as Rx from 'rxjs'; -import { HeadlessChromiumDriver, HeadlessChromiumDriverFactory } from '../browsers'; -import { createDriverFactory } from '../browsers/chromium'; +import { chromium, HeadlessChromiumDriver, HeadlessChromiumDriverFactory } from '../browsers'; import * as contexts from '../export_types/common/lib/screenshots/constants'; import { LevelLogger } from '../lib'; import { CaptureConfig, ElementsPositionAndAttribute } from '../types'; @@ -113,8 +112,12 @@ export const createMockBrowserDriverFactory = async ( maxAttempts: 1, }; - const binaryPath = '/usr/local/share/common/secure/'; - const mockBrowserDriverFactory = await createDriverFactory(binaryPath, logger, captureConfig); + const binaryPath = '/usr/local/share/common/secure/super_awesome_binary'; + const mockBrowserDriverFactory = await chromium.createDriverFactory( + binaryPath, + captureConfig, + logger + ); const mockPage = {} as Page; const mockBrowserDriver = new HeadlessChromiumDriver(mockPage, { inspect: true, diff --git a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts index b04e697d0a118..669381a92c522 100644 --- a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts +++ b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts @@ -7,49 +7,65 @@ jest.mock('../routes'); jest.mock('../usage'); jest.mock('../browsers'); -jest.mock('../browsers'); jest.mock('../lib/create_queue'); jest.mock('../lib/enqueue_job'); jest.mock('../lib/validate'); import { of } from 'rxjs'; +import { first } from 'rxjs/operators'; import { coreMock } from 'src/core/server/mocks'; import { ReportingConfig, ReportingCore } from '../'; +import { + chromium, + HeadlessChromiumDriverFactory, + initializeBrowserDriverFactory, +} from '../browsers'; import { ReportingInternalSetup } from '../core'; import { ReportingPlugin } from '../plugin'; import { ReportingSetupDeps, ReportingStartDeps } from '../types'; +(initializeBrowserDriverFactory as jest.Mock< + Promise +>).mockImplementation(() => Promise.resolve({} as HeadlessChromiumDriverFactory)); + +(chromium as any).createDriverFactory.mockImplementation(() => ({})); + const createMockSetupDeps = (setupMock?: any): ReportingSetupDeps => { return { security: setupMock.security, licensing: { license$: of({ isAvailable: true, isActive: true, type: 'basic' }), } as any, - usageCollection: {} as any, + usageCollection: { + makeUsageCollector: jest.fn(), + registerCollector: jest.fn(), + } as any, }; }; +export const createMockConfigSchema = (overrides?: any) => ({ + index: '.reporting', + kibanaServer: { + hostname: 'localhost', + port: '80', + }, + capture: { + browser: { + chromium: { + disableSandbox: true, + }, + }, + }, + ...overrides, +}); + export const createMockStartDeps = (startMock?: any): ReportingStartDeps => ({ data: startMock.data, }); const createMockReportingPlugin = async (config: ReportingConfig): Promise => { - const mockConfig = { - index: '.reporting', - kibanaServer: { - hostname: 'localhost', - port: '80', - }, - capture: { - browser: { - chromium: { - disableSandbox: true, - }, - }, - }, - ...config, - }; - const plugin = new ReportingPlugin(coreMock.createPluginInitializerContext(mockConfig)); + const mockConfigSchema = createMockConfigSchema(config); + const plugin = new ReportingPlugin(coreMock.createPluginInitializerContext(mockConfigSchema)); const setupMock = coreMock.createSetup(); const coreStartMock = coreMock.createStart(); const startMock = { @@ -57,8 +73,10 @@ const createMockReportingPlugin = async (config: ReportingConfig): Promise & { excludeFromBaseAll?: boolean; excludeFromBaseRead?: boolean; + privileges?: FeatureConfig['privileges']; } ) => { - const { excludeFromBaseAll, excludeFromBaseRead, ...rest } = config; + const { excludeFromBaseAll, excludeFromBaseRead, privileges, ...rest } = config; return new Feature({ icon: 'discoverApp', navLinkId: 'discover', app: [], catalogue: [], - privileges: { - all: { - excludeFromBasePrivileges: excludeFromBaseAll, - savedObject: { - all: ['all-type'], - read: ['read-type'], - }, - ui: ['read-ui', 'all-ui', `read-${config.id}`, `all-${config.id}`], - }, - read: { - excludeFromBasePrivileges: excludeFromBaseRead, - savedObject: { - all: [], - read: ['read-type'], - }, - ui: ['read-ui', `read-${config.id}`], - }, - }, + privileges: + privileges === null + ? null + : { + all: { + excludeFromBasePrivileges: excludeFromBaseAll, + savedObject: { + all: ['all-type'], + read: ['read-type'], + }, + ui: ['read-ui', 'all-ui', `read-${config.id}`, `all-${config.id}`], + }, + read: { + excludeFromBasePrivileges: excludeFromBaseRead, + savedObject: { + all: [], + read: ['read-type'], + }, + ui: ['read-ui', `read-${config.id}`], + }, + }, ...rest, }); }; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx index be475398608e0..6bc829f766e58 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx @@ -762,4 +762,88 @@ describe('FeatureTable', () => { }, }); }); + + it('renders a description for features with only reserved privileges (omitting the primary feature controls)', () => { + const role = createRole([ + { + spaces: ['foo'], + base: [], + feature: {}, + }, + ]); + const reservedFeature = createFeature({ + id: 'reserved_feature', + name: 'Reserved Feature', + privileges: null, + reserved: { + description: 'this is my reserved feature description', + privileges: [ + { + id: 'priv_1', + privilege: { + api: [], + savedObject: { all: [], read: [] }, + ui: [], + }, + }, + ], + }, + }); + + const { wrapper } = setup({ + role, + features: [reservedFeature], + privilegeIndex: 0, + calculateDisplayedPrivileges: false, + canCustomizeSubFeaturePrivileges: false, + }); + + expect(findTestSubject(wrapper, 'reservedFeatureDescription').text()).toMatchInlineSnapshot( + `"this is my reserved feature description"` + ); + + expect(findTestSubject(wrapper, 'primaryFeaturePrivilegeControl')).toHaveLength(0); + }); + + it('renders renders the primary feature controls when both primary and reserved privileges are specified', () => { + const role = createRole([ + { + spaces: ['foo'], + base: [], + feature: {}, + }, + ]); + const reservedFeature = createFeature({ + id: 'reserved_feature', + name: 'Reserved Feature with primary feature privileges', + reserved: { + description: 'this is my reserved feature description', + privileges: [ + { + id: 'priv_1', + privilege: { + api: [], + savedObject: { all: [], read: [] }, + ui: [], + }, + }, + ], + }, + }); + + const { displayedPrivileges, wrapper } = setup({ + role, + features: [reservedFeature], + privilegeIndex: 0, + calculateDisplayedPrivileges: true, + canCustomizeSubFeaturePrivileges: false, + }); + + expect(findTestSubject(wrapper, 'reservedFeatureDescription')).toHaveLength(0); + expect(displayedPrivileges).toEqual({ + reserved_feature: { + primaryFeaturePrivilege: 'none', + }, + }); + }); }); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx index 0776f2af2ddd7..38e4390a2856a 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx @@ -193,12 +193,16 @@ export class FeatureTable extends Component { render: (roleEntry: Role, record: TableRow) => { const { feature } = record; - if (feature.reserved) { - return {feature.reserved.description}; - } - const primaryFeaturePrivileges = feature.getPrimaryFeaturePrivileges(); + if (feature.reserved && primaryFeaturePrivileges.length === 0) { + return ( + + {feature.reserved.description} + + ); + } + if (primaryFeaturePrivileges.length === 0) { return null; } diff --git a/x-pack/plugins/security/server/authentication/index.ts b/x-pack/plugins/security/server/authentication/index.ts index 4d4c475e48aa2..c2321b0d3c874 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -112,6 +112,7 @@ export async function setupAuthentication({ encryptionKey: config.encryptionKey, isSecure: config.secureCookies, name: config.cookieName, + sameSite: config.sameSiteCookies, validate: (session: ProviderSession | ProviderSession[]) => { const array: ProviderSession[] = Array.isArray(session) ? session : [session]; for (const sess of array) { diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index 51f1e41528bbb..11cd73d35d747 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -150,6 +150,9 @@ export const ConfigSchema = schema.object({ lifespan: schema.nullable(schema.duration()), }), secureCookies: schema.boolean({ defaultValue: false }), + sameSiteCookies: schema.maybe( + schema.oneOf([schema.literal('Strict'), schema.literal('Lax'), schema.literal('None')]) + ), public: schema.object({ protocol: schema.maybe(schema.oneOf([schema.literal('http'), schema.literal('https')])), hostname: schema.maybe(schema.string({ hostname: true })), diff --git a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx index 4fbbcfc8d948a..8f7e0e06fb7a1 100644 --- a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx +++ b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx @@ -35,8 +35,7 @@ export const ManagementPageView = memo>((options) => href: getManagementUrl({ name: 'policyList' }), }, ]; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [tabName]); + }, [options.viewType, tabName]); return ; }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx index 6c447df618791..9b0ca73cf021f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx @@ -137,15 +137,14 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { description: details.agent.version, }, ]; - // eslint-disable-next-line react-hooks/exhaustive-deps }, [ - details.agent.version, details.endpoint.policy.id, - details.host.hostname, details.host.ip, - policyResponseUri.search, - policyStatusClickHandler, + details.host.hostname, + details.agent.version, policyStatus, + policyResponseUri, + policyStatusClickHandler, ]); return ( diff --git a/x-pack/plugins/security_solution/public/management/store/reducer.ts b/x-pack/plugins/security_solution/public/management/store/reducer.ts index e00b3ec9e0f5b..2ed3dfe86d2f8 100644 --- a/x-pack/plugins/security_solution/public/management/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/store/reducer.ts @@ -40,6 +40,5 @@ export const mockManagementState: Immutable = { export const managementReducer = immutableCombineReducers({ [MANAGEMENT_STORE_POLICY_LIST_NAMESPACE]: policyListReducer, [MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: policyDetailsReducer, - // @ts-ignore [MANAGEMENT_STORE_ENDPOINTS_NAMESPACE]: hostListReducer, }); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/README.md b/x-pack/plugins/security_solution/scripts/endpoint/README.md index e97bd9be61d3b..f78cc5e1717e6 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/README.md +++ b/x-pack/plugins/security_solution/scripts/endpoint/README.md @@ -13,7 +13,7 @@ Example command sequence to get ES and kibana running with sample data after ins `yarn es snapshot` -> starts ES -`npx yarn start --xpack.securitySolution.endpoint.enabled=true --no-base-path` -> starts kibana +`npx yarn start --xpack.securitySolution.enabled=true --no-base-path` -> starts kibana `cd ~/path/to/kibana/x-pack/plugins/endpoint` @@ -27,17 +27,15 @@ Options: --seed, -s random seed to use for document generator [string] --node, -n elasticsearch node url - [string] [default: "http://localhost:9200"] - --alertIndex, --ai index to store alerts in - [string] [default: "events-endpoint-1"] + [string] [default: "http://elastic:changeme@localhost:9200"] + --kibana, -k kibana url + [string] [default: "http://elastic:changeme@localhost:5601"] --eventIndex, --ei index to store events in [string] [default: "events-endpoint-1"] --metadataIndex, --mi index to store host metadata in - [string] [default: "metrics-endpoint-default-1"] + [string] [default: "metrics-endpoint.metadata-default-1"] --policyIndex, --pi index to store host policy in [string] [default: "metrics-endpoint.policy-default-1"] - --auth elasticsearch username and password, separated - by a colon [string] --ancestors, --anc number of ancestors of origin to create [number] [default: 3] --generations, --gen number of child generations to create @@ -61,8 +59,4 @@ Options: [number] [default: 1] --delete, -d delete indices and remake them [boolean] [default: false] - --setupOnly, --so Run only the index and pipeline creation then - exit. This is intended to be used to set up the - Endpoint App for use with the real Elastic - Endpoint. [boolean] [default: false] ``` diff --git a/x-pack/plugins/security_solution/scripts/endpoint/alert_mapping.json b/x-pack/plugins/security_solution/scripts/endpoint/alert_mapping.json deleted file mode 100644 index 2e0041d0af986..0000000000000 --- a/x-pack/plugins/security_solution/scripts/endpoint/alert_mapping.json +++ /dev/null @@ -1,2376 +0,0 @@ -{ - "mappings": { - "_meta": { - "version": "1.5.0-dev" - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "mutable_state": { - "properties": { - "triage_status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "compile_time": { - "type": "date" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { - "properties": { - "features": { - "properties": { - "data": { - "properties": { - "buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "mapped_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "mapped_size": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "endpoint": { - "properties": { - "artifact": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "policy": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "sequence": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "entry_modified": { - "type": "double" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "macro": { - "properties": { - "code_page": { - "type": "long" - }, - "collection": { - "properties": { - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - }, - "type": "object" - }, - "errors": { - "properties": { - "count": { - "type": "long" - }, - "error_type": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "file_extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "project_file": { - "properties": { - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - }, - "type": "object" - }, - "stream": { - "properties": { - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "raw_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "raw_code_size": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - } - } - }, - "malware_classification": { - "properties": { - "features": { - "properties": { - "data": { - "properties": { - "buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "temp_file_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "cpu_percent": { - "type": "double" - }, - "cwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "env_variables": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "handles": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { - "properties": { - "features": { - "properties": { - "data": { - "properties": { - "buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "memory_percent": { - "type": "double" - }, - "memory_region": { - "properties": { - "allocation_base": { - "ignore_above": 1024, - "type": "keyword" - }, - "allocation_protection": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram": { - "properties": { - "histogram_array": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_flavor": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_resolution": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "length": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "permission": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_base": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_tag": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "unbacked_on_disk": { - "type": "boolean" - } - }, - "type": "nested" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "num_threads": { - "type": "long" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "entrypoint": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "start_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_address_module": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "phys_memory_bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "services": { - "ignore_above": 1024, - "type": "keyword" - }, - "session_id": { - "type": "long" - }, - "short_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "call_stack": { - "properties": { - "instruction_pointer": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_section": { - "properties": { - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "rva": { - "ignore_above": 1024, - "type": "keyword" - }, - "symbol_info": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "entrypoint": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "start_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_address_module": { - "ignore_above": 1024, - "type": "keyword" - }, - "token": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "integrity_level": { - "type": "long" - }, - "integrity_level_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "is_appcontainer": { - "type": "boolean" - }, - "privileges": { - "properties": { - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uptime": { - "type": "long" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "token": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "integrity_level": { - "type": "long" - }, - "integrity_level_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "is_appcontainer": { - "type": "boolean" - }, - "privileges": { - "properties": { - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tty_device": { - "properties": { - "major_number": { - "type": "integer" - }, - "minor_number": { - "type": "integer" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uptime": { - "type": "long" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - }, - "virt_memory_bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "target": { - "properties": { - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "compile_time": { - "type": "date" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { - "properties": { - "features": { - "properties": { - "data": { - "properties": { - "buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "mapped_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "mapped_size": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "cpu_percent": { - "type": "double" - }, - "cwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "env_variables": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "handles": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { - "properties": { - "features": { - "properties": { - "data": { - "properties": { - "buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "memory_percent": { - "type": "double" - }, - "memory_region": { - "properties": { - "allocation_base": { - "ignore_above": 1024, - "type": "keyword" - }, - "allocation_protection": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram": { - "properties": { - "histogram_array": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_flavor": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_resolution": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "length": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "permission": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_base": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_tag": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "unbacked_on_disk": { - "type": "boolean" - } - }, - "type": "nested" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "num_threads": { - "type": "long" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "entrypoint": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "start_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_address_module": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "phys_memory_bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "services": { - "ignore_above": 1024, - "type": "keyword" - }, - "session_id": { - "type": "long" - }, - "short_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "call_stack": { - "properties": { - "instruction_pointer": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_section": { - "properties": { - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "rva": { - "ignore_above": 1024, - "type": "keyword" - }, - "symbol_info": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "entrypoint": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "start_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_address_module": { - "ignore_above": 1024, - "type": "keyword" - }, - "token": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "integrity_level": { - "type": "long" - }, - "integrity_level_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "is_appcontainer": { - "type": "boolean" - }, - "privileges": { - "properties": { - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uptime": { - "type": "long" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "token": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "integrity_level": { - "type": "long" - }, - "integrity_level_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "is_appcontainer": { - "type": "boolean" - }, - "privileges": { - "properties": { - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tty_device": { - "properties": { - "major_number": { - "type": "integer" - }, - "minor_number": { - "type": "integer" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uptime": { - "type": "long" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - }, - "virt_memory_bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "mapping": { - "total_fields": { - "limit": 10000 - } - }, - "refresh_interval": "5s" - } - } -} \ No newline at end of file diff --git a/x-pack/plugins/security_solution/scripts/endpoint/event_mapping.json b/x-pack/plugins/security_solution/scripts/endpoint/event_mapping.json deleted file mode 100644 index f410edc7abe5e..0000000000000 --- a/x-pack/plugins/security_solution/scripts/endpoint/event_mapping.json +++ /dev/null @@ -1,2369 +0,0 @@ -{ - "mappings": { - "_meta": { - "version": "1.5.0-dev" - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "compile_time": { - "type": "date" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { - "properties": { - "features": { - "properties": { - "data": { - "properties": { - "buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "mapped_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "mapped_size": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "endpoint": { - "properties": { - "artifact": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "policy": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "sequence": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "entry_modified": { - "type": "double" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "macro": { - "properties": { - "code_page": { - "type": "long" - }, - "collection": { - "properties": { - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - }, - "type": "object" - }, - "errors": { - "properties": { - "count": { - "type": "long" - }, - "error_type": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "file_extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "project_file": { - "properties": { - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - }, - "type": "object" - }, - "stream": { - "properties": { - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "raw_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "raw_code_size": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - } - } - }, - "malware_classification": { - "properties": { - "features": { - "properties": { - "data": { - "properties": { - "buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "temp_file_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "cpu_percent": { - "type": "double" - }, - "cwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "env_variables": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "handles": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { - "properties": { - "features": { - "properties": { - "data": { - "properties": { - "buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "memory_percent": { - "type": "double" - }, - "memory_region": { - "properties": { - "allocation_base": { - "ignore_above": 1024, - "type": "keyword" - }, - "allocation_protection": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram": { - "properties": { - "histogram_array": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_flavor": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_resolution": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "length": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "permission": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_base": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_tag": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "unbacked_on_disk": { - "type": "boolean" - } - }, - "type": "nested" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "num_threads": { - "type": "long" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "entrypoint": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "start_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_address_module": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "phys_memory_bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "services": { - "ignore_above": 1024, - "type": "keyword" - }, - "session_id": { - "type": "long" - }, - "short_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "call_stack": { - "properties": { - "instruction_pointer": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_section": { - "properties": { - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "rva": { - "ignore_above": 1024, - "type": "keyword" - }, - "symbol_info": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "entrypoint": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "start_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_address_module": { - "ignore_above": 1024, - "type": "keyword" - }, - "token": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "integrity_level": { - "type": "long" - }, - "integrity_level_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "is_appcontainer": { - "type": "boolean" - }, - "privileges": { - "properties": { - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uptime": { - "type": "long" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "token": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "integrity_level": { - "type": "long" - }, - "integrity_level_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "is_appcontainer": { - "type": "boolean" - }, - "privileges": { - "properties": { - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tty_device": { - "properties": { - "major_number": { - "type": "integer" - }, - "minor_number": { - "type": "integer" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uptime": { - "type": "long" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - }, - "virt_memory_bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "target": { - "properties": { - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "compile_time": { - "type": "date" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { - "properties": { - "features": { - "properties": { - "data": { - "properties": { - "buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "mapped_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "mapped_size": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "cpu_percent": { - "type": "double" - }, - "cwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "env_variables": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "handles": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { - "properties": { - "features": { - "properties": { - "data": { - "properties": { - "buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "memory_percent": { - "type": "double" - }, - "memory_region": { - "properties": { - "allocation_base": { - "ignore_above": 1024, - "type": "keyword" - }, - "allocation_protection": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram": { - "properties": { - "histogram_array": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_flavor": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_resolution": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "length": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "permission": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_base": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_tag": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "unbacked_on_disk": { - "type": "boolean" - } - }, - "type": "nested" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "num_threads": { - "type": "long" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "entrypoint": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "start_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_address_module": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "phys_memory_bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "services": { - "ignore_above": 1024, - "type": "keyword" - }, - "session_id": { - "type": "long" - }, - "short_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "call_stack": { - "properties": { - "instruction_pointer": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_section": { - "properties": { - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "rva": { - "ignore_above": 1024, - "type": "keyword" - }, - "symbol_info": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "entrypoint": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "start_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_address_module": { - "ignore_above": 1024, - "type": "keyword" - }, - "token": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "integrity_level": { - "type": "long" - }, - "integrity_level_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "is_appcontainer": { - "type": "boolean" - }, - "privileges": { - "properties": { - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uptime": { - "type": "long" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "token": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "integrity_level": { - "type": "long" - }, - "integrity_level_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "is_appcontainer": { - "type": "boolean" - }, - "privileges": { - "properties": { - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tty_device": { - "properties": { - "major_number": { - "type": "integer" - }, - "minor_number": { - "type": "integer" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uptime": { - "type": "long" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - }, - "virt_memory_bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "mapping": { - "total_fields": { - "limit": 10000 - } - }, - "refresh_interval": "5s", - "default_pipeline": "endpoint-event-pipeline" - } - } -} \ No newline at end of file diff --git a/x-pack/plugins/security_solution/scripts/endpoint/metadata_mapping.json b/x-pack/plugins/security_solution/scripts/endpoint/metadata_mapping.json deleted file mode 100644 index c4eb305305749..0000000000000 --- a/x-pack/plugins/security_solution/scripts/endpoint/metadata_mapping.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "mappings": { - "_meta": { - "version": "1.5.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elastic": { - "properties": { - "agent": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - } - } - }, - "endpoint": { - "properties": { - "policy": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - } - } - }, - "event": { - "properties": { - "created": { - "type": "date" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "variant": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "settings": { - "index": { - "mapping": { - "total_fields": { - "limit": 10000 - } - }, - "refresh_interval": "5s" - } - } -} diff --git a/x-pack/plugins/security_solution/scripts/endpoint/policy_mapping.json b/x-pack/plugins/security_solution/scripts/endpoint/policy_mapping.json deleted file mode 100644 index b879ba180eba8..0000000000000 --- a/x-pack/plugins/security_solution/scripts/endpoint/policy_mapping.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "mappings": { - "_meta": { - "version": "1.6.0-dev" - }, - "date_detection": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "endpoint": { - "properties": { - "policy": { - "properties": { - "applied": { - "properties": { - "actions": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "artifacts": { - "properties": { - "global": { - "properties": { - "identifiers": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "user": { - "properties": { - "identifiers": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "configurations": { - "properties": { - "events": { - "properties": { - "concerned_actions": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "logging": { - "properties": { - "concerned_actions": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "malware": { - "properties": { - "concerned_actions": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "streaming": { - "properties": { - "concerned_actions": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "policy": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "response": { - "type": "object" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - } - }, - "type": "object" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - } - } - }, - "settings": { - "index": { - "mapping": { - "total_fields": { - "limit": 10000 - } - }, - "refresh_interval": "5s" - } - } -} diff --git a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator.ts b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator.ts index 63c1507718137..fb25d22a1b5fe 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator.ts @@ -3,22 +3,21 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import * as yargs from 'yargs'; +/* eslint-disable no-console */ +import * as path from 'path'; +import yargs from 'yargs'; +import * as url from 'url'; +import fetch from 'node-fetch'; import seedrandom from 'seedrandom'; import { Client, ClientOptions } from '@elastic/elasticsearch'; import { ResponseError } from '@elastic/elasticsearch/lib/errors'; import { EndpointDocGenerator, Event } from '../../common/endpoint/generate_data'; -import { default as eventMapping } from './event_mapping.json'; -import { default as alertMapping } from './alert_mapping.json'; -import { default as policyMapping } from './policy_mapping.json'; -import { default as metadataMapping } from './metadata_mapping.json'; main(); async function deleteIndices(indices: string[], client: Client) { const handleErr = (err: unknown) => { if (err instanceof ResponseError && err.statusCode !== 404) { - // eslint-disable-next-line no-console console.log(JSON.stringify(err, null, 2)); // eslint-disable-next-line no-process-exit process.exit(1); @@ -33,12 +32,45 @@ async function deleteIndices(indices: string[], client: Client) { } catch (err) { handleErr(err); } + } +} - try { - await client.indices.delete({ index }); - } catch (err) { - handleErr(err); +async function doIngestSetup(kibanaURL: string) { + try { + const kbURL = new url.URL(kibanaURL); + // this includes the base path that is randomly generated by Kibana + const pathname = path.posix.join(path.posix.sep, kbURL.pathname, 'api/ingest_manager/setup'); + const connectURL = new url.URL(pathname, kbURL); + console.log('Calling ingest manager setup at ', connectURL.toString()); + const response = await fetch( + // wrap base url in URL class because the kibana basepath will get removed otherwise + connectURL.toString(), + { + method: 'POST', + headers: { + 'kbn-xsrf': 'blah', + }, + } + ); + if (response.status !== 200) { + console.log('POST response ', response); + console.log( + 'Request failed please check that you have the correct base path and credentials for the kibana URL' + ); + // eslint-disable-next-line no-process-exit + process.exit(1); } + const setupResponse = await response.json(); + console.log('Ingest setup response ', setupResponse); + if (!setupResponse?.isInitialized) { + console.log('Initializing the ingest manager failed, existing'); + // eslint-disable-next-line no-process-exit + process.exit(1); + } + } catch (error) { + console.log(JSON.stringify(error, null, 2)); + // eslint-disable-next-line no-process-exit + process.exit(1); } } @@ -52,13 +84,13 @@ async function main() { node: { alias: 'n', describe: 'elasticsearch node url', - default: 'http://localhost:9200', + default: 'http://elastic:changeme@localhost:9200', type: 'string', }, - alertIndex: { - alias: 'ai', - describe: 'index to store alerts in', - default: 'events-endpoint-1', + kibana: { + alias: 'k', + describe: 'kibana url', + default: 'http://elastic:changeme@localhost:5601', type: 'string', }, eventIndex: { @@ -79,10 +111,6 @@ async function main() { default: 'metrics-endpoint.policy-default-1', type: 'string', }, - auth: { - describe: 'elasticsearch username and password, separated by a colon', - type: 'string', - }, ancestors: { alias: 'anc', describe: 'number of ancestors of origin to create', @@ -150,74 +178,21 @@ async function main() { type: 'boolean', default: false, }, - setupOnly: { - alias: 'so', - describe: - 'Run only the index and pipeline creation then exit. This is intended to be used to set up the Endpoint App for use with the real Elastic Endpoint.', - type: 'boolean', - default: false, - }, }).argv; - const pipelineName = 'endpoint-event-pipeline'; - eventMapping.settings.index.default_pipeline = pipelineName; + await doIngestSetup(argv.kibana); + const clientOptions: ClientOptions = { node: argv.node, }; - if (argv.auth) { - const [username, password]: string[] = argv.auth.split(':', 2); - clientOptions.auth = { username, password }; - } + const client = new Client(clientOptions); if (argv.delete) { - await deleteIndices( - [argv.eventIndex, argv.metadataIndex, argv.alertIndex, argv.policyIndex], - client - ); - } - - const pipeline = { - description: 'redirects alerts to their own index', - processors: [ - { - set: { - field: '_index', - value: argv.alertIndex, - if: "ctx.event.kind == 'alert'", - }, - }, - { - set: { - field: 'mutable_state.triage_status', - value: 'open', - }, - }, - ], - }; - try { - await client.ingest.putPipeline({ - id: pipelineName, - body: pipeline, - }); - } catch (err) { - // eslint-disable-next-line no-console - console.log(err); - // eslint-disable-next-line no-process-exit - process.exit(1); - } - - await createIndex(client, argv.alertIndex, alertMapping); - await createIndex(client, argv.eventIndex, eventMapping); - await createIndex(client, argv.policyIndex, policyMapping); - await createIndex(client, argv.metadataIndex, metadataMapping); - if (argv.setupOnly) { - // eslint-disable-next-line no-process-exit - process.exit(0); + await deleteIndices([argv.eventIndex, argv.metadataIndex, argv.policyIndex], client); } let seed = argv.seed; if (!seed) { seed = Math.random().toString(); - // eslint-disable-next-line no-console console.log(`No seed supplied, using random seed: ${seed}`); } const random = seedrandom(seed); @@ -233,12 +208,14 @@ async function main() { await client.index({ index: argv.metadataIndex, body: generator.generateHostMetadata(timestamp - timeBetweenDocs * (argv.numDocs - j - 1)), + op_type: 'create', }); await client.index({ index: argv.policyIndex, body: generator.generatePolicyResponse( timestamp - timeBetweenDocs * (argv.numDocs - j - 1) ), + op_type: 'create', }); } @@ -264,33 +241,12 @@ async function main() { const body = resolverDocs.reduce( // eslint-disable-next-line @typescript-eslint/no-explicit-any (array: Array>, doc) => ( - array.push({ index: { _index: argv.eventIndex } }, doc), array + array.push({ create: { _index: argv.eventIndex } }, doc), array ), [] ); - await client.bulk({ body }); + await client.bulk({ body, refresh: 'true' }); } } - // eslint-disable-next-line no-console console.log(`Creating and indexing documents took: ${new Date().getTime() - startTime}ms`); } - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -async function createIndex(client: Client, index: string, mapping: any) { - try { - await client.indices.create({ - index, - body: mapping, - }); - } catch (err) { - if ( - err instanceof ResponseError && - err.body.error.type !== 'resource_already_exists_exception' - ) { - // eslint-disable-next-line no-console - console.log(err.body); - // eslint-disable-next-line no-process-exit - process.exit(1); - } - } -} diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx index 5e5aace4139f0..255a245081d5a 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx @@ -59,11 +59,12 @@ interface Props { transformId: string; transformConfig: any; overrides: StepDetailsExposedState; + timeFieldName?: string | undefined; onChange(s: StepDetailsExposedState): void; } export const StepCreateForm: FC = React.memo( - ({ createIndexPattern, transformConfig, transformId, onChange, overrides }) => { + ({ createIndexPattern, transformConfig, transformId, onChange, overrides, timeFieldName }) => { const defaults = { ...getDefaultStepCreateState(), ...overrides }; const [redirectToTransformManagement, setRedirectToTransformManagement] = useState(false); @@ -187,8 +188,8 @@ export const StepCreateForm: FC = React.memo( Object.assign(newIndexPattern, { id: '', title: indexPatternName, + timeFieldName, }); - const id = await newIndexPattern.create(); await indexPatterns.clearCache(); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx index afda7bf75fa34..271fde27f519a 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx @@ -23,10 +23,17 @@ import { ToastNotificationText } from '../../../../components'; import { useDocumentationLinks } from '../../../../hooks/use_documentation_links'; import { SearchItems } from '../../../../hooks/use_search_items'; import { useApi } from '../../../../hooks/use_api'; - -import { isTransformIdValid, TransformPivotConfig } from '../../../../common'; +import { StepDetailsTimeField } from './step_details_time_field'; +import { + getPivotQuery, + getPreviewRequestBody, + isTransformIdValid, + TransformPivotConfig, +} from '../../../../common'; import { EsIndexName, IndexPatternTitle } from './common'; import { delayValidator } from '../../../../common/validators'; +import { StepDefineExposedState } from '../step_define/common'; +import { dictionaryToArray } from '../../../../../../common/types/common'; export interface StepDetailsExposedState { continuousModeDateField: string; @@ -38,6 +45,7 @@ export interface StepDetailsExposedState { transformId: TransformId; transformDescription: string; valid: boolean; + indexPatternDateField?: string | undefined; } export function getDefaultStepDetailsState(): StepDetailsExposedState { @@ -51,6 +59,7 @@ export function getDefaultStepDetailsState(): StepDetailsExposedState { destinationIndex: '', touched: false, valid: false, + indexPatternDateField: undefined, }; } @@ -74,10 +83,11 @@ interface Props { overrides?: StepDetailsExposedState; onChange(s: StepDetailsExposedState): void; searchItems: SearchItems; + stepDefineState: StepDefineExposedState; } export const StepDetailsForm: FC = React.memo( - ({ overrides = {}, onChange, searchItems }) => { + ({ overrides = {}, onChange, searchItems, stepDefineState }) => { const deps = useAppDependencies(); const toastNotifications = useToastNotifications(); const { esIndicesCreateIndex } = useDocumentationLinks(); @@ -93,8 +103,28 @@ export const StepDetailsForm: FC = React.memo( ); const [transformIds, setTransformIds] = useState([]); const [indexNames, setIndexNames] = useState([]); + + // Index pattern state const [indexPatternTitles, setIndexPatternTitles] = useState([]); const [createIndexPattern, setCreateIndexPattern] = useState(defaults.createIndexPattern); + const [previewDateColumns, setPreviewDateColumns] = useState([]); + const [indexPatternDateField, setIndexPatternDateField] = useState(); + + const onTimeFieldChanged = React.useCallback( + (e: React.ChangeEvent) => { + const value = e.target.value; + // If the value is an empty string, it's not a valid selection + if (value === '') { + return; + } + // Find the time field based on the selected value + // this is to account for undefined when user chooses not to use a date field + const timeField = previewDateColumns.find((col) => col === value); + + setIndexPatternDateField(timeField); + }, + [setIndexPatternDateField, previewDateColumns] + ); // Continuous mode state const [isContinuousModeEnabled, setContinuousModeEnabled] = useState( @@ -107,6 +137,37 @@ export const StepDetailsForm: FC = React.memo( useEffect(() => { // use an IIFE to avoid returning a Promise to useEffect. (async function () { + try { + const { searchQuery, groupByList, aggList } = stepDefineState; + const pivotAggsArr = dictionaryToArray(aggList); + const pivotGroupByArr = dictionaryToArray(groupByList); + const pivotQuery = getPivotQuery(searchQuery); + const previewRequest = getPreviewRequestBody( + searchItems.indexPattern.title, + pivotQuery, + pivotGroupByArr, + pivotAggsArr + ); + + const transformPreview = await api.getTransformsPreview(previewRequest); + const properties = transformPreview.generated_dest_index.mappings.properties; + const datetimeColumns: string[] = Object.keys(properties).filter( + (col) => properties[col].type === 'date' + ); + + setPreviewDateColumns(datetimeColumns); + setIndexPatternDateField(datetimeColumns[0]); + } catch (e) { + toastNotifications.addDanger({ + title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformPreview', { + defaultMessage: 'An error occurred getting transform preview', + }), + text: toMountPoint( + + ), + }); + } + try { setTransformIds( (await api.getTransforms()).transforms.map( @@ -198,6 +259,7 @@ export const StepDetailsForm: FC = React.memo( destinationIndex, touched: true, valid, + indexPatternDateField, }); // custom comparison /* eslint-disable react-hooks/exhaustive-deps */ @@ -210,6 +272,7 @@ export const StepDetailsForm: FC = React.memo( transformDescription, destinationIndex, valid, + indexPatternDateField, /* eslint-enable react-hooks/exhaustive-deps */ ]); @@ -318,6 +381,7 @@ export const StepDetailsForm: FC = React.memo( data-test-subj="transformDestinationIndexInput" /> + = React.memo( data-test-subj="transformCreateIndexPatternSwitch" /> + {createIndexPattern && !indexPatternTitleExists && previewDateColumns.length > 0 && ( + + )} = React.memo( )} > ({ text }))} + options={dateFieldNames.map((text: string) => ({ text }))} value={continuousModeDateField} onChange={(e) => setContinuousModeDateField(e.target.value)} data-test-subj="transformContinuousDateFieldSelect" diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx index 7d4d25c1d05cf..bd6dc8f709e99 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx @@ -8,7 +8,7 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFieldText, EuiFormRow } from '@elastic/eui'; +import { EuiFieldText, EuiFormRow, EuiSelect } from '@elastic/eui'; import { StepDetailsExposedState } from './step_details_form'; @@ -21,6 +21,7 @@ export const StepDetailsSummary: FC = React.memo( transformDescription, destinationIndex, touched, + indexPatternDateField, }) => { if (touched === false) { return null; @@ -56,6 +57,21 @@ export const StepDetailsSummary: FC = React.memo( > + + + + {isContinuousModeEnabled && ( ) => void; +} + +export const StepDetailsTimeField: FC = ({ + previewDateColumns, + indexPatternDateField, + onTimeFieldChanged, +}) => { + const noTimeFieldLabel = i18n.translate( + 'xpack.transform.stepDetailsForm.noTimeFieldOptionLabel', + { + defaultMessage: "I don't want to use the Time Filter", + } + ); + + const noTimeFieldOption = { + text: noTimeFieldLabel, + value: undefined, + }; + + const disabledDividerOption = { + disabled: true, + text: '───', + value: '', + }; + + return ( + + } + helpText={ + + } + > + ({ text })), + disabledDividerOption, + noTimeFieldOption, + ]} + value={indexPatternDateField} + onChange={onTimeFieldChanged} + data-test-subj="transformIndexPatternDateFieldSelect" + /> + + ); +}; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx index 5c34eb0d3fdf4..806dcbfa75604 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx @@ -91,6 +91,8 @@ export const CreateTransformWizardContext = createContext<{ indexPattern: IndexP }); export const Wizard: FC = React.memo(({ cloneConfig, searchItems }) => { + const { indexPattern } = searchItems; + // The current WIZARD_STEP const [currentStep, setCurrentStep] = useState(WIZARD_STEPS.DEFINE); @@ -110,6 +112,7 @@ export const Wizard: FC = React.memo(({ cloneConfig, searchItems }) onChange={setStepDetailsState} overrides={stepDetailsState} searchItems={searchItems} + stepDefineState={stepDefineState} /> ) : ( @@ -146,8 +149,6 @@ export const Wizard: FC = React.memo(({ cloneConfig, searchItems }) } }, []); - const { indexPattern } = searchItems; - const transformConfig = getCreateRequestBody( indexPattern.title, stepDefineState, @@ -162,6 +163,7 @@ export const Wizard: FC = React.memo(({ cloneConfig, searchItems }) transformConfig={transformConfig} onChange={setStepCreateState} overrides={stepCreateState} + timeFieldName={stepDetailsState.indexPatternDateField} /> ) : ( diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 47a8d75c4ae06..ffff09bdb6470 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -398,6 +398,9 @@ "core.ui.securityNavList.label": "セキュリティ", "core.ui.welcomeErrorMessage": "Elastic Kibana が正常に読み込まれませんでした。詳細はサーバーアウトプットを確認してください。", "core.ui.welcomeMessage": "Elastic Kibana の読み込み中", + "core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage": "{advancedSettingsLink}で{storeInSessionStorageParam}オプションを有効にするか、オンスクリーンビジュアルを簡素化してください。", + "core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage.advancedSettingsLinkText": "高度な設定", + "core.ui.errorUrlOverflow.bigUrlWarningNotificationTitle": "URLが大きく、Kibanaの動作が停止する可能性があります", "dashboard.actions.toggleExpandPanelMenuItem.expandedDisplayName": "最小化", "dashboard.actions.toggleExpandPanelMenuItem.notExpandedDisplayName": "全画面", "dashboard.addExistingVisualizationLinkText": "既存のユーザーを追加", @@ -2120,6 +2123,28 @@ "share.advancedSettings.csv.quoteValuesTitle": "CSV の値を引用", "share.advancedSettings.csv.separatorText": "エクスポートされた値をこの文字列で区切ります", "share.advancedSettings.csv.separatorTitle": "CSV セパレーター", + "share.contextMenu.embedCodeLabel": "埋め込みコード", + "share.contextMenu.embedCodePanelTitle": "埋め込みコード", + "share.contextMenu.permalinkPanelTitle": "パーマリンク", + "share.contextMenu.permalinksLabel": "パーマリンク", + "share.contextMenuTitle": "この {objectType} を共有", + "share.urlGenerators.error.createUrlFnProvided": "このジェネレーターは非推奨とマークされています。createUrl fn を付けないでください。", + "share.urlGenerators.error.migrateCalledNotDeprecated": "非推奨以外のジェネレーターで migrate を呼び出すことはできません。", + "share.urlGenerators.error.migrationFnGivenNotDeprecated": "移行機能を提供する場合、このジェネレーターに非推奨マークを付ける必要があります", + "share.urlGenerators.error.noCreateUrlFnProvided": "このジェネレーターには非推奨のマークがありません。createUrl fn を付けてください。", + "share.urlGenerators.error.noMigrationFnProvided": "アクセスリンクジェネレーターに非推奨マークが付いている場合、移行機能を提供する必要があります。", + "share.urlGenerators.errors.noGeneratorWithId": "{id} という ID のジェネレーターはありません", + "share.urlPanel.canNotShareAsSavedObjectHelpText": "{objectType} が保存されるまで保存されたオブジェクトを共有することはできません。", + "share.urlPanel.copyIframeCodeButtonLabel": "iFrame コードをコピー", + "share.urlPanel.copyLinkButtonLabel": "リンクをコピー", + "share.urlPanel.generateLinkAsLabel": "名前を付けてリンクを生成", + "share.urlPanel.savedObjectDescription": "この URL を共有することで、他のユーザーがこの {objectType} の最も最近保存されたバージョンを読み込めるようになります。", + "share.urlPanel.savedObjectLabel": "保存されたオブジェクト", + "share.urlPanel.shortUrlHelpText": "互換性が最も高くなるよう、短いスナップショット URL を共有することをお勧めします。Internet Explorer は URL の長さに制限があり、一部の wiki やマークアップパーサーは長い完全なスナップショット URL に対応していませんが、短い URL は正常に動作するはずです。", + "share.urlPanel.shortUrlLabel": "短い URL", + "share.urlPanel.snapshotDescription": "スナップショット URL には、{objectType} の現在の状態がエンコードされています。保存された {objectType} への編集内容はこの URL には反映されません。.", + "share.urlPanel.snapshotLabel": "スナップショット", + "share.urlPanel.unableCreateShortUrlErrorMessage": "短い URL を作成できません。エラー: {errorMessage}", "kbn.advancedSettings.darkModeText": "Kibana UI のダークモードを有効にします。この設定を適用するにはページの更新が必要です。", "kbn.advancedSettings.darkModeTitle": "ダークモード", "kbn.advancedSettings.dateFormat.dayOfWeekText": "週の初めの曜日を設定します", @@ -2171,9 +2196,37 @@ "kbn.advancedSettings.visualization.tileMap.wmsDefaultsTitle": "デフォルトの WMS プロパティ", "visualizations.advancedSettings.visualizeEnableLabsText": "ユーザーが実験的なビジュアライゼーションを作成、表示、編集できるようになります。無効の場合、\n ユーザーは本番準備が整ったビジュアライゼーションのみを利用できます。", "visualizations.advancedSettings.visualizeEnableLabsTitle": "実験的なビジュアライゼーションを有効にする", - "core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage": "{advancedSettingsLink}で{storeInSessionStorageParam}オプションを有効にするか、オンスクリーンビジュアルを簡素化してください。", - "core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage.advancedSettingsLinkText": "高度な設定", - "core.ui.errorUrlOverflow.bigUrlWarningNotificationTitle": "URLが大きく、Kibanaの動作が停止する可能性があります", + "visualizations.disabledLabVisualizationMessage": "ラボビジュアライゼーションを表示するには、高度な設定でラボモードをオンにしてください。", + "visualizations.disabledLabVisualizationTitle": "{title} はラボビジュアライゼーションです。", + "visualizations.displayName": "ビジュアライゼーション", + "visualizations.function.range.from.help": "範囲の開始", + "visualizations.function.range.help": "範囲オブジェクトを生成します", + "visualizations.function.range.to.help": "範囲の終了", + "visualizations.function.visDimension.accessor.help": "使用するデータセット内の列 (列インデックスまたは列名)", + "visualizations.function.visDimension.error.accessor": "入力された列名は無効です。", + "visualizations.function.visDimension.format.help": "フォーマット", + "visualizations.function.visDimension.formatParams.help": "フォーマットパラメーター", + "visualizations.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します", + "visualizations.functions.visualization.help": "シンプルなビジュアライゼーションです", + "visualizations.newVisWizard.betaDescription": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません", + "visualizations.newVisWizard.betaTitle": "ベータ", + "visualizations.newVisWizard.chooseSourceTitle": "ソースの選択", + "visualizations.newVisWizard.experimentalDescription": "このビジュアライゼーションは実験的なものです。デザインと導入は安定したビジュアライゼーションよりも完成度が低く、変更される可能性があります。", + "visualizations.newVisWizard.experimentalTitle": "実験的", + "visualizations.newVisWizard.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。", + "visualizations.newVisWizard.filterVisTypeAriaLabel": "ビジュアライゼーションのタイプでフィルタリング", + "visualizations.newVisWizard.helpText": "タイプを選択してビジュアライゼーションの作成を始めましょう。", + "visualizations.newVisWizard.helpTextAriaLabel": "タイプを選択してビジュアライゼーションの作成を始めましょう。ESC を押してこのモーダルを閉じます。Tab キーを押して次に進みます。", + "visualizations.newVisWizard.newVisTypeTitle": "新規 {visTypeName}", + "visualizations.newVisWizard.resultsFound": "{resultCount} 個の{resultCount, plural, one {タイプ} other {タイプ} } が見つかりました", + "visualizations.newVisWizard.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。", + "visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン", + "visualizations.newVisWizard.searchSelection.savedObjectType.search": "保存検索", + "visualizations.newVisWizard.selectVisType": "ビジュアライゼーションのタイプを選択してください", + "visualizations.newVisWizard.title": "新規ビジュアライゼーション", + "visualizations.newVisWizard.visTypeAliasDescription": "Visualize 外で Kibana アプリケーションを開きます。", + "visualizations.newVisWizard.visTypeAliasTitle": "Kibana アプリケーション", + "visualizations.savedObjectName": "ビジュアライゼーション", "kibana_legacy.notify.fatalError.errorStatusMessage": "エラー {errStatus} {errStatusText}: {errMessage}", "kibana_legacy.notify.fatalError.unavailableServerErrorMessage": "HTTP リクエストで接続に失敗しました。Kibana サーバーが実行されていて、ご使用のブラウザの接続が正常に動作していることを確認するか、システム管理者にお問い合わせください。", "kibana_legacy.notify.toaster.errorMessage": "エラー: {errorMessage}\n {errorStack}", @@ -2425,28 +2478,6 @@ "server.status.redTitle": "赤", "server.status.uninitializedTitle": "アンインストールしました", "server.status.yellowTitle": "黄色", - "share.contextMenu.embedCodeLabel": "埋め込みコード", - "share.contextMenu.embedCodePanelTitle": "埋め込みコード", - "share.contextMenu.permalinkPanelTitle": "パーマリンク", - "share.contextMenu.permalinksLabel": "パーマリンク", - "share.contextMenuTitle": "この {objectType} を共有", - "share.urlGenerators.error.createUrlFnProvided": "このジェネレーターは非推奨とマークされています。createUrl fn を付けないでください。", - "share.urlGenerators.error.migrateCalledNotDeprecated": "非推奨以外のジェネレーターで migrate を呼び出すことはできません。", - "share.urlGenerators.error.migrationFnGivenNotDeprecated": "移行機能を提供する場合、このジェネレーターに非推奨マークを付ける必要があります", - "share.urlGenerators.error.noCreateUrlFnProvided": "このジェネレーターには非推奨のマークがありません。createUrl fn を付けてください。", - "share.urlGenerators.error.noMigrationFnProvided": "アクセスリンクジェネレーターに非推奨マークが付いている場合、移行機能を提供する必要があります。", - "share.urlGenerators.errors.noGeneratorWithId": "{id} という ID のジェネレーターはありません", - "share.urlPanel.canNotShareAsSavedObjectHelpText": "{objectType} が保存されるまで保存されたオブジェクトを共有することはできません。", - "share.urlPanel.copyIframeCodeButtonLabel": "iFrame コードをコピー", - "share.urlPanel.copyLinkButtonLabel": "リンクをコピー", - "share.urlPanel.generateLinkAsLabel": "名前を付けてリンクを生成", - "share.urlPanel.savedObjectDescription": "この URL を共有することで、他のユーザーがこの {objectType} の最も最近保存されたバージョンを読み込めるようになります。", - "share.urlPanel.savedObjectLabel": "保存されたオブジェクト", - "share.urlPanel.shortUrlHelpText": "互換性が最も高くなるよう、短いスナップショット URL を共有することをお勧めします。Internet Explorer は URL の長さに制限があり、一部の wiki やマークアップパーサーは長い完全なスナップショット URL に対応していませんが、短い URL は正常に動作するはずです。", - "share.urlPanel.shortUrlLabel": "短い URL", - "share.urlPanel.snapshotDescription": "スナップショット URL には、{objectType} の現在の状態がエンコードされています。保存された {objectType} への編集内容はこの URL には反映されません。.", - "share.urlPanel.snapshotLabel": "スナップショット", - "share.urlPanel.unableCreateShortUrlErrorMessage": "短い URL を作成できません。エラー: {errorMessage}", "statusPage.loadStatus.serverIsDownErrorMessage": "サーバーステータスのリクエストに失敗しました。サーバーがダウンしている可能性があります。", "statusPage.loadStatus.serverStatusCodeErrorMessage": "サーバーステータスのリクエストに失敗しました。ステータスコード: {responseStatus}", "statusPage.metricsTiles.columns.heapTotalHeader": "ヒープ合計", @@ -3843,37 +3874,6 @@ "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}、トグルオプション", "visTypeVislib.vislib.tooltip.fieldLabel": "フィールド", "visTypeVislib.vislib.tooltip.valueLabel": "値", - "visualizations.disabledLabVisualizationMessage": "ラボビジュアライゼーションを表示するには、高度な設定でラボモードをオンにしてください。", - "visualizations.disabledLabVisualizationTitle": "{title} はラボビジュアライゼーションです。", - "visualizations.displayName": "ビジュアライゼーション", - "visualizations.function.range.from.help": "範囲の開始", - "visualizations.function.range.help": "範囲オブジェクトを生成します", - "visualizations.function.range.to.help": "範囲の終了", - "visualizations.function.visDimension.accessor.help": "使用するデータセット内の列 (列インデックスまたは列名)", - "visualizations.function.visDimension.error.accessor": "入力された列名は無効です。", - "visualizations.function.visDimension.format.help": "フォーマット", - "visualizations.function.visDimension.formatParams.help": "フォーマットパラメーター", - "visualizations.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します", - "visualizations.functions.visualization.help": "シンプルなビジュアライゼーションです", - "visualizations.newVisWizard.betaDescription": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません", - "visualizations.newVisWizard.betaTitle": "ベータ", - "visualizations.newVisWizard.chooseSourceTitle": "ソースの選択", - "visualizations.newVisWizard.experimentalDescription": "このビジュアライゼーションは実験的なものです。デザインと導入は安定したビジュアライゼーションよりも完成度が低く、変更される可能性があります。", - "visualizations.newVisWizard.experimentalTitle": "実験的", - "visualizations.newVisWizard.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。", - "visualizations.newVisWizard.filterVisTypeAriaLabel": "ビジュアライゼーションのタイプでフィルタリング", - "visualizations.newVisWizard.helpText": "タイプを選択してビジュアライゼーションの作成を始めましょう。", - "visualizations.newVisWizard.helpTextAriaLabel": "タイプを選択してビジュアライゼーションの作成を始めましょう。ESC を押してこのモーダルを閉じます。Tab キーを押して次に進みます。", - "visualizations.newVisWizard.newVisTypeTitle": "新規 {visTypeName}", - "visualizations.newVisWizard.resultsFound": "{resultCount} 個の{resultCount, plural, one {タイプ} other {タイプ} } が見つかりました", - "visualizations.newVisWizard.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。", - "visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン", - "visualizations.newVisWizard.searchSelection.savedObjectType.search": "保存検索", - "visualizations.newVisWizard.selectVisType": "ビジュアライゼーションのタイプを選択してください", - "visualizations.newVisWizard.title": "新規ビジュアライゼーション", - "visualizations.newVisWizard.visTypeAliasDescription": "Visualize 外で Kibana アプリケーションを開きます。", - "visualizations.newVisWizard.visTypeAliasTitle": "Kibana アプリケーション", - "visualizations.savedObjectName": "ビジュアライゼーション", "visualize.badge.readOnly.text": "読み取り専用", "visualize.badge.readOnly.tooltip": "ビジュアライゼーションを保存できません", "visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "indexPatternまたはsavedSearchIdが必要です", @@ -3973,6 +3973,8 @@ "xpack.uiActionsEnhanced.customizePanelTimeRange.modal.updatePanelTimeRangeButtonTitle": "更新", "xpack.uiActionsEnhanced.customizeTimeRange.modal.headerTitle": "パネルの時間範囲のカスタマイズ", "xpack.uiActionsEnhanced.customizeTimeRangeMenuItem.displayName": "時間範囲のカスタマイズ", + "xpack.uiActionsEnhanced.components.DiscoverDrilldownConfig.chooseIndexPattern": "対象インデックスパターンを選択", + "xpack.uiActionsEnhanced.drilldown.goToDiscover": "Discoverに移動(例)", "xpack.alerts.alertNavigationRegistry.get.missingNavigationError": "「{consumer}」内のアラートタイプ「{alertType}」のナビゲーションは登録されていません。", "xpack.alerts.alertNavigationRegistry.register.duplicateDefaultError": "「{consumer}」内のデフォルトナビゲーションは既に登録されています。", "xpack.alerts.alertNavigationRegistry.register.duplicateNavigationError": "「{consumer}」内のアラートタイプ「{alertType}」のナビゲーションは既に登録されています。", @@ -10458,13 +10460,10 @@ "xpack.ml.models.jobValidation.validateJobObject.jobIsNotObjectErrorMessage": "無効な {invalidParamName}:オブジェクトでなければなりません。", "xpack.ml.models.jobValidation.validateJobObject.timeFieldIsNotStringErrorMessage": "無効な {invalidParamName}:文字列でなければなりません。", "xpack.ml.navMenu.anomalyDetectionTabLinkText": "異常検知", - "xpack.ml.navMenu.anomalyExplorerTabLinkText": "異常エクスプローラー", "xpack.ml.navMenu.dataFrameAnalyticsTabLinkText": "分析", "xpack.ml.navMenu.dataVisualizerTabLinkText": "データビジュアライザー", - "xpack.ml.navMenu.jobManagementTabLinkText": "ジョブ管理", "xpack.ml.navMenu.overviewTabLinkText": "概要", "xpack.ml.navMenu.settingsTabLinkText": "設定", - "xpack.ml.navMenu.singleMetricViewerTabLinkText": "シングルメトリックビューアー", "xpack.ml.newJi18n(ob.recognize.jobsCreationFailed.resetButtonAriaLabel": "リセット", "xpack.ml.newJob.page.createJob": "ジョブを作成", "xpack.ml.newJob.recognize.advancedLabel": "高度な設定", @@ -10893,7 +10892,6 @@ "xpack.ml.settings.breadcrumbs.filterLists.createLabel": "作成", "xpack.ml.settings.breadcrumbs.filterLists.editLabel": "編集", "xpack.ml.settings.breadcrumbs.filterListsLabel": "フィルターリスト", - "xpack.ml.settings.calendarManagementButtonLabel": "カレンダー管理", "xpack.ml.settings.calendars.listHeader.calendarsDescription": "カレンダーにはシステム停止日や祝日などの予定を含むことができ、異常検知から除外します。カレンダーは複数のジョブに割り当てることができます。{br}{learnMoreLink}", "xpack.ml.settings.calendars.listHeader.calendarsDescription.learnMoreLinkText": "詳細", "xpack.ml.settings.calendars.listHeader.calendarsListTotalCount": "合計 {totalCount}", @@ -10943,8 +10941,6 @@ "xpack.ml.settings.filterLists.table.noFiltersCreatedTitle": "フィルターが 1 つも作成されていません", "xpack.ml.settings.filterLists.table.notInUseAriaLabel": "使用されていません", "xpack.ml.settings.filterLists.toolbar.deleteItemButtonLabel": "アイテムを削除", - "xpack.ml.settings.filterListsButtonLabel": "フィルターリスト", - "xpack.ml.settings.jobManagementTitle": "ジョブ管理", "xpack.ml.settingsBreadcrumbLabel": "設定", "xpack.ml.singleMetricViewerPageLabel": "シングルメトリックビューアー", "xpack.ml.stepDefineForm.invalidQuery": "無効なクエリ", @@ -16385,8 +16381,6 @@ "xpack.triggersActionsUI.timeUnits.secondLabel": "{timeValue, plural, one {秒} other {秒}}", "xpack.triggersActionsUI.typeRegistry.get.missingActionTypeErrorMessage": "オブジェクトタイプ「{id}」は登録されていません。", "xpack.triggersActionsUI.typeRegistry.register.duplicateObjectTypeErrorMessage": "オブジェクトタイプ「{id}」は既に登録されています。", - "xpack.uiActionsEnhanced.components.DiscoverDrilldownConfig.chooseIndexPattern": "対象インデックスパターンを選択", - "xpack.uiActionsEnhanced.drilldown.goToDiscover": "Discoverに移動(例)", "xpack.upgradeAssistant.appTitle": "{version} アップグレードアシスタント", "xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.calloutDetail": "{snapshotRestoreDocsButton} でデータをバックアップします。", "xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.snapshotRestoreDocsButtonLabel": "API のスナップショットと復元", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 2985ea78bea03..1c219be7f2143 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -398,6 +398,9 @@ "core.ui.securityNavList.label": "安全", "core.ui.welcomeErrorMessage": "Elastic Kibana 未正确加载。检查服务器输出以了解详情。", "core.ui.welcomeMessage": "正在加载 Elastic Kibana", + "core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage": "在{advancedSettingsLink}中启用“{storeInSessionStorageParam}”选项或简化屏幕视觉效果。", + "core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage.advancedSettingsLinkText": "高级设置", + "core.ui.errorUrlOverflow.bigUrlWarningNotificationTitle": "URL 过长,Kibana 可能无法工作", "dashboard.actions.toggleExpandPanelMenuItem.expandedDisplayName": "最小化", "dashboard.actions.toggleExpandPanelMenuItem.notExpandedDisplayName": "全屏", "dashboard.addExistingVisualizationLinkText": "将现有", @@ -2123,6 +2126,28 @@ "share.advancedSettings.csv.quoteValuesTitle": "使用引号引起 CSV 值", "share.advancedSettings.csv.separatorText": "使用此字符串分隔导出的值", "share.advancedSettings.csv.separatorTitle": "CSV 分隔符", + "share.contextMenu.embedCodeLabel": "嵌入代码", + "share.contextMenu.embedCodePanelTitle": "嵌入代码", + "share.contextMenu.permalinkPanelTitle": "固定链接", + "share.contextMenu.permalinksLabel": "固定链接", + "share.contextMenuTitle": "共享此 {objectType}", + "share.urlGenerators.error.createUrlFnProvided": "此生成器标记为已过时。切勿提供 createUrl 函数。", + "share.urlGenerators.error.migrateCalledNotDeprecated": "无法在非过时的生成器上调用迁移。", + "share.urlGenerators.error.migrationFnGivenNotDeprecated": "如果提供了迁移函数,则必须将此生成器标记为已过时", + "share.urlGenerators.error.noCreateUrlFnProvided": "此生成器未标记为已过时。请提供 createUrl 函数。", + "share.urlGenerators.error.noMigrationFnProvided": "如果访问链接生成器标记为已过时,则必须提供迁移函数。", + "share.urlGenerators.errors.noGeneratorWithId": "未找到 ID 为 {id} 的生成器", + "share.urlPanel.canNotShareAsSavedObjectHelpText": "只有保存 {objectType} 后,才能共享为已保存对象。", + "share.urlPanel.copyIframeCodeButtonLabel": "复制 iFrame 代码", + "share.urlPanel.copyLinkButtonLabel": "复制链接", + "share.urlPanel.generateLinkAsLabel": "将链接生成为", + "share.urlPanel.savedObjectDescription": "您可以将此 URL 共享给相关人员,以便他们可以加载此 {objectType} 最新的已保存版本。", + "share.urlPanel.savedObjectLabel": "已保存对象", + "share.urlPanel.shortUrlHelpText": "建议共享缩短的快照 URL,以实现最大的兼容性。Internet Explorer 有 URL 长度限制,某些 wiki 和标记分析器无法很好地处理全长版本的快照 URL,但应能很好地处理短 URL。", + "share.urlPanel.shortUrlLabel": "短 URL", + "share.urlPanel.snapshotDescription": "快照 URL 将 {objectType} 的当前状态编入 URL 自身之中。通过此 URL 无法看到对已保存 {objectType} 的编辑。", + "share.urlPanel.snapshotLabel": "快照", + "share.urlPanel.unableCreateShortUrlErrorMessage": "无法创建短 URL。错误:{errorMessage}", "kbn.advancedSettings.darkModeText": "为 Kibana UI 启用深色模式需要刷新页面,才能应用设置。", "kbn.advancedSettings.darkModeTitle": "深色模式", "kbn.advancedSettings.dateFormat.dayOfWeekText": "一周从哪一日开始?", @@ -2174,9 +2199,37 @@ "kbn.advancedSettings.visualization.tileMap.wmsDefaultsTitle": "默认 WMS 属性", "visualizations.advancedSettings.visualizeEnableLabsText": "允许用户创建、查看和编辑实验性可视化。如果禁用,\n 仅被视为生产就绪的可视化可供用户使用。", "visualizations.advancedSettings.visualizeEnableLabsTitle": "启用实验性可视化", - "core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage": "在{advancedSettingsLink}中启用“{storeInSessionStorageParam}”选项或简化屏幕视觉效果。", - "core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage.advancedSettingsLinkText": "高级设置", - "core.ui.errorUrlOverflow.bigUrlWarningNotificationTitle": "URL 过长,Kibana 可能无法工作", + "visualizations.disabledLabVisualizationMessage": "请在高级设置中打开实验室模式,以查看实验室可视化。", + "visualizations.disabledLabVisualizationTitle": "{title} 为实验室可视化。", + "visualizations.displayName": "可视化", + "visualizations.function.range.from.help": "范围起始", + "visualizations.function.range.help": "生成范围对象", + "visualizations.function.range.to.help": "范围结束", + "visualizations.function.visDimension.accessor.help": "要使用的数据集列(列索引或列名称)", + "visualizations.function.visDimension.error.accessor": "提供的列名称无效", + "visualizations.function.visDimension.format.help": "格式", + "visualizations.function.visDimension.formatParams.help": "格式参数", + "visualizations.function.visDimension.help": "生成 visConfig 维度对象", + "visualizations.functions.visualization.help": "简单可视化", + "visualizations.newVisWizard.betaDescription": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束", + "visualizations.newVisWizard.betaTitle": "公测版", + "visualizations.newVisWizard.chooseSourceTitle": "选择源", + "visualizations.newVisWizard.experimentalDescription": "这是实验性可视化。与稳定的可视化相比,其设计和实现均不够成熟,可能会随时发生更改。", + "visualizations.newVisWizard.experimentalTitle": "实验性", + "visualizations.newVisWizard.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。", + "visualizations.newVisWizard.filterVisTypeAriaLabel": "筛留可视化类型", + "visualizations.newVisWizard.helpText": "通过为该可视化选择类型,来开始创建您的可视化。", + "visualizations.newVisWizard.helpTextAriaLabel": "通过为该可视化选择类型,来开始创建您的可视化。按 Esc 键关闭此模式。按 Tab 键继续。", + "visualizations.newVisWizard.newVisTypeTitle": "新建{visTypeName}", + "visualizations.newVisWizard.resultsFound": "找到了 {resultCount} 个{resultCount, plural, one {类型} other {类型} }", + "visualizations.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。", + "visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "索引模式", + "visualizations.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索", + "visualizations.newVisWizard.selectVisType": "选择可视化类型", + "visualizations.newVisWizard.title": "新建可视化", + "visualizations.newVisWizard.visTypeAliasDescription": "打开 Visualize 外部的 Kibana 应用程序。", + "visualizations.newVisWizard.visTypeAliasTitle": "Kibana 应用程序", + "visualizations.savedObjectName": "可视化", "kibana_legacy.notify.fatalError.errorStatusMessage": "错误 {errStatus} {errStatusText}:{errMessage}", "kibana_legacy.notify.fatalError.unavailableServerErrorMessage": "HTTP 请求无法连接。请检查 Kibana 服务器是否正在运行以及您的浏览器是否具有有效的连接,或请联系您的系统管理员。", "kibana_legacy.notify.toaster.errorMessage": "错误:{errorMessage}\n {errorStack}", @@ -2428,28 +2481,6 @@ "server.status.redTitle": "红", "server.status.uninitializedTitle": "未初始化", "server.status.yellowTitle": "黄", - "share.contextMenu.embedCodeLabel": "嵌入代码", - "share.contextMenu.embedCodePanelTitle": "嵌入代码", - "share.contextMenu.permalinkPanelTitle": "固定链接", - "share.contextMenu.permalinksLabel": "固定链接", - "share.contextMenuTitle": "共享此 {objectType}", - "share.urlGenerators.error.createUrlFnProvided": "此生成器标记为已过时。切勿提供 createUrl 函数。", - "share.urlGenerators.error.migrateCalledNotDeprecated": "无法在非过时的生成器上调用迁移。", - "share.urlGenerators.error.migrationFnGivenNotDeprecated": "如果提供了迁移函数,则必须将此生成器标记为已过时", - "share.urlGenerators.error.noCreateUrlFnProvided": "此生成器未标记为已过时。请提供 createUrl 函数。", - "share.urlGenerators.error.noMigrationFnProvided": "如果访问链接生成器标记为已过时,则必须提供迁移函数。", - "share.urlGenerators.errors.noGeneratorWithId": "未找到 ID 为 {id} 的生成器", - "share.urlPanel.canNotShareAsSavedObjectHelpText": "只有保存 {objectType} 后,才能共享为已保存对象。", - "share.urlPanel.copyIframeCodeButtonLabel": "复制 iFrame 代码", - "share.urlPanel.copyLinkButtonLabel": "复制链接", - "share.urlPanel.generateLinkAsLabel": "将链接生成为", - "share.urlPanel.savedObjectDescription": "您可以将此 URL 共享给相关人员,以便他们可以加载此 {objectType} 最新的已保存版本。", - "share.urlPanel.savedObjectLabel": "已保存对象", - "share.urlPanel.shortUrlHelpText": "建议共享缩短的快照 URL,以实现最大的兼容性。Internet Explorer 有 URL 长度限制,某些 wiki 和标记分析器无法很好地处理全长版本的快照 URL,但应能很好地处理短 URL。", - "share.urlPanel.shortUrlLabel": "短 URL", - "share.urlPanel.snapshotDescription": "快照 URL 将 {objectType} 的当前状态编入 URL 自身之中。通过此 URL 无法看到对已保存 {objectType} 的编辑。", - "share.urlPanel.snapshotLabel": "快照", - "share.urlPanel.unableCreateShortUrlErrorMessage": "无法创建短 URL。错误:{errorMessage}", "statusPage.loadStatus.serverIsDownErrorMessage": "无法请求服务器状态。也许您的服务器已关闭?", "statusPage.loadStatus.serverStatusCodeErrorMessage": "无法使用状态代码 {responseStatus} 请求服务器状态", "statusPage.metricsTiles.columns.heapTotalHeader": "堆总计", @@ -3846,37 +3877,6 @@ "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}, 切换选项", "visTypeVislib.vislib.tooltip.fieldLabel": "字段", "visTypeVislib.vislib.tooltip.valueLabel": "值", - "visualizations.disabledLabVisualizationMessage": "请在高级设置中打开实验室模式,以查看实验室可视化。", - "visualizations.disabledLabVisualizationTitle": "{title} 为实验室可视化。", - "visualizations.displayName": "可视化", - "visualizations.function.range.from.help": "范围起始", - "visualizations.function.range.help": "生成范围对象", - "visualizations.function.range.to.help": "范围结束", - "visualizations.function.visDimension.accessor.help": "要使用的数据集列(列索引或列名称)", - "visualizations.function.visDimension.error.accessor": "提供的列名称无效", - "visualizations.function.visDimension.format.help": "格式", - "visualizations.function.visDimension.formatParams.help": "格式参数", - "visualizations.function.visDimension.help": "生成 visConfig 维度对象", - "visualizations.functions.visualization.help": "简单可视化", - "visualizations.newVisWizard.betaDescription": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束", - "visualizations.newVisWizard.betaTitle": "公测版", - "visualizations.newVisWizard.chooseSourceTitle": "选择源", - "visualizations.newVisWizard.experimentalDescription": "这是实验性可视化。与稳定的可视化相比,其设计和实现均不够成熟,可能会随时发生更改。", - "visualizations.newVisWizard.experimentalTitle": "实验性", - "visualizations.newVisWizard.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。", - "visualizations.newVisWizard.filterVisTypeAriaLabel": "筛留可视化类型", - "visualizations.newVisWizard.helpText": "通过为该可视化选择类型,来开始创建您的可视化。", - "visualizations.newVisWizard.helpTextAriaLabel": "通过为该可视化选择类型,来开始创建您的可视化。按 Esc 键关闭此模式。按 Tab 键继续。", - "visualizations.newVisWizard.newVisTypeTitle": "新建{visTypeName}", - "visualizations.newVisWizard.resultsFound": "找到了 {resultCount} 个{resultCount, plural, one {类型} other {类型} }", - "visualizations.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。", - "visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "索引模式", - "visualizations.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索", - "visualizations.newVisWizard.selectVisType": "选择可视化类型", - "visualizations.newVisWizard.title": "新建可视化", - "visualizations.newVisWizard.visTypeAliasDescription": "打开 Visualize 外部的 Kibana 应用程序。", - "visualizations.newVisWizard.visTypeAliasTitle": "Kibana 应用程序", - "visualizations.savedObjectName": "可视化", "visualize.badge.readOnly.text": "只读", "visualize.badge.readOnly.tooltip": "无法保存可视化", "visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "必须提供 indexPattern 或 savedSearchId", @@ -3976,6 +3976,8 @@ "xpack.uiActionsEnhanced.customizePanelTimeRange.modal.updatePanelTimeRangeButtonTitle": "更新", "xpack.uiActionsEnhanced.customizeTimeRange.modal.headerTitle": "定制面板时间范围", "xpack.uiActionsEnhanced.customizeTimeRangeMenuItem.displayName": "定制时间范围", + "xpack.uiActionsEnhanced.components.DiscoverDrilldownConfig.chooseIndexPattern": "选择目标索引模式", + "xpack.uiActionsEnhanced.drilldown.goToDiscover": "前往 Discover(示例)", "xpack.alerts.alertNavigationRegistry.get.missingNavigationError": "在“{consumer}”内针对告警类型“{alertType}”的导航未注册。", "xpack.alerts.alertNavigationRegistry.register.duplicateDefaultError": "“{consumer}”内的默认导航已注册。", "xpack.alerts.alertNavigationRegistry.register.duplicateNavigationError": "在“{consumer}”内针对告警类型“{alertType}”的导航已注册。", @@ -10462,13 +10464,10 @@ "xpack.ml.models.jobValidation.validateJobObject.jobIsNotObjectErrorMessage": "无效的 {invalidParamName}:需要是对象。", "xpack.ml.models.jobValidation.validateJobObject.timeFieldIsNotStringErrorMessage": "无效的 {invalidParamName}:需要是字符串。", "xpack.ml.navMenu.anomalyDetectionTabLinkText": "异常检测", - "xpack.ml.navMenu.anomalyExplorerTabLinkText": "Anomaly Explorer", "xpack.ml.navMenu.dataFrameAnalyticsTabLinkText": "分析", "xpack.ml.navMenu.dataVisualizerTabLinkText": "数据可视化工具", - "xpack.ml.navMenu.jobManagementTabLinkText": "作业管理", "xpack.ml.navMenu.overviewTabLinkText": "概览", "xpack.ml.navMenu.settingsTabLinkText": "设置", - "xpack.ml.navMenu.singleMetricViewerTabLinkText": "Single Metric Viewer", "xpack.ml.newJi18n(ob.recognize.jobsCreationFailed.resetButtonAriaLabel": "重置", "xpack.ml.newJob.page.createJob": "创建作业", "xpack.ml.newJob.recognize.advancedLabel": "高级", @@ -10897,7 +10896,6 @@ "xpack.ml.settings.breadcrumbs.filterLists.createLabel": "创建", "xpack.ml.settings.breadcrumbs.filterLists.editLabel": "编辑", "xpack.ml.settings.breadcrumbs.filterListsLabel": "筛选列表", - "xpack.ml.settings.calendarManagementButtonLabel": "日历管理", "xpack.ml.settings.calendars.listHeader.calendarsDescription": "日志包含不想生成异常的已计划事件列表,例如已计划系统中断或公共假期。同一日历可分配给多个作业。{br}{learnMoreLink}", "xpack.ml.settings.calendars.listHeader.calendarsDescription.learnMoreLinkText": "了解详情", "xpack.ml.settings.calendars.listHeader.calendarsListTotalCount": "合计 {totalCount}", @@ -10947,8 +10945,6 @@ "xpack.ml.settings.filterLists.table.noFiltersCreatedTitle": "未创建任何筛选", "xpack.ml.settings.filterLists.table.notInUseAriaLabel": "未在使用", "xpack.ml.settings.filterLists.toolbar.deleteItemButtonLabel": "删除项", - "xpack.ml.settings.filterListsButtonLabel": "筛选列表", - "xpack.ml.settings.jobManagementTitle": "作业管理", "xpack.ml.settingsBreadcrumbLabel": "设置", "xpack.ml.singleMetricViewerPageLabel": "Single Metric Viewer", "xpack.ml.stepDefineForm.invalidQuery": "无效查询", @@ -16391,8 +16387,6 @@ "xpack.triggersActionsUI.timeUnits.secondLabel": "{timeValue, plural, one {秒} other {秒}}", "xpack.triggersActionsUI.typeRegistry.get.missingActionTypeErrorMessage": "未注册对象类型“{id}”。", "xpack.triggersActionsUI.typeRegistry.register.duplicateObjectTypeErrorMessage": "已注册对象类型“{id}”。", - "xpack.uiActionsEnhanced.components.DiscoverDrilldownConfig.chooseIndexPattern": "选择目标索引模式", - "xpack.uiActionsEnhanced.drilldown.goToDiscover": "前往 Discover(示例)", "xpack.upgradeAssistant.appTitle": "{version} 升级助手", "xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.calloutDetail": "使用 {snapshotRestoreDocsButton} 备份您的数据。", "xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.snapshotRestoreDocsButtonLabel": "快照和还原 API", diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index 9769d81d8f147..87d43366ba1c8 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -8,7 +8,7 @@ require('@kbn/plugin-helpers').babelRegister(); require('@kbn/test').runTestsCli([ require.resolve('../test/functional/config.js'), - require.resolve('../test/functional_endpoint/config.ts'), + require.resolve('../test/security_solution_endpoint/config.ts'), require.resolve('../test/functional_with_es_ssl/config.ts'), require.resolve('../test/functional/config_security_basic.ts'), require.resolve('../test/functional/config_security_trial.ts'), diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 239da3ea8c693..181d41d77b4cb 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -25,6 +25,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const testSubjects = getService('testSubjects'); const filterBar = getService('filterBar'); + const listingTable = getService('listingTable'); async function assertExpectedMetric(metricCount: string = '19,986') { await PageObjects.lens.assertExactText( @@ -60,14 +61,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { async function clickOnBarHistogram() { const el = await elasticChart.getCanvas(); - await browser.getActions().move({ x: 5, y: 5, origin: el._webElement }).click().perform(); } - // FLAKY: https://github.com/elastic/kibana/issues/67838 - describe.skip('lens smokescreen tests', () => { + describe('lens smokescreen tests', () => { it('should allow editing saved visualizations', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); + await listingTable.searchForItemWithName('Artistpreviouslyknownaslens'); await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens'); await PageObjects.lens.goToTimeRange(); await assertExpectedMetric(); @@ -77,6 +77,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); await dashboardAddPanel.clickOpenAddPanel(); + await dashboardAddPanel.filterEmbeddableNames('Artistpreviouslyknownaslens'); await find.clickByButtonText('Artistpreviouslyknownaslens'); await dashboardAddPanel.closeAddPanel(); await PageObjects.lens.goToTimeRange(); @@ -87,6 +88,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); await dashboardAddPanel.clickOpenAddPanel(); + await dashboardAddPanel.filterEmbeddableNames('lnsXYvis'); await find.clickByButtonText('lnsXYvis'); await dashboardAddPanel.closeAddPanel(); await PageObjects.lens.goToTimeRange(); @@ -101,6 +103,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should allow seamless transition to and from table view', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); + await listingTable.searchForItemWithName('Artistpreviouslyknownaslens'); await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens'); await PageObjects.lens.goToTimeRange(); await assertExpectedMetric(); @@ -152,6 +155,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // Ensure the visualization shows up in the visualize list, and takes // us back to the visualization as we configured it. await PageObjects.visualize.gotoVisualizationLandingPage(); + await listingTable.searchForItemWithName('Afancilenstest'); await PageObjects.lens.clickVisualizeListItemTitle('Afancilenstest'); await PageObjects.lens.goToTimeRange(); diff --git a/x-pack/test/functional/apps/ml/pages.ts b/x-pack/test/functional/apps/ml/pages.ts index 817ee75705c9c..e2c80c8dab558 100644 --- a/x-pack/test/functional/apps/ml/pages.ts +++ b/x-pack/test/functional/apps/ml/pages.ts @@ -35,20 +35,12 @@ export default function ({ getService }: FtrProviderContext) { await ml.jobManagement.assertCreateNewJobButtonExists(); }); - it('loads the anomaly explorer page', async () => { - await ml.navigation.navigateToAnomalyExplorer(); - await ml.anomalyExplorer.assertAnomalyExplorerEmptyListMessageExists(); - }); - - it('loads the single metric viewer page', async () => { - await ml.navigation.navigateToSingleMetricViewer(); - await ml.singleMetricViewer.assertSingleMetricViewerEmptyListMessageExsist(); - }); - it('loads the settings page', async () => { await ml.navigation.navigateToSettings(); - await ml.settings.assertSettingsCalendarLinkExists(); - await ml.settings.assertSettingsFilterlistLinkExists(); + await ml.settings.assertSettingsManageCalendarsLinkExists(); + await ml.settings.assertSettingsCreateCalendarLinkExists(); + await ml.settings.assertSettingsManageFilterListsLinkExists(); + await ml.settings.assertSettingsCreateFilterListLinkExists(); }); it('loads the data frame analytics page', async () => { diff --git a/x-pack/test/functional/services/ml/navigation.ts b/x-pack/test/functional/services/ml/navigation.ts index 8454a0b071b8f..9b67a369f055f 100644 --- a/x-pack/test/functional/services/ml/navigation.ts +++ b/x-pack/test/functional/services/ml/navigation.ts @@ -59,42 +59,22 @@ export function MachineLearningNavigationProvider({ async navigateToAnomalyDetection() { await this.navigateToArea('~mlMainTab & ~anomalyDetection', 'mlPageJobManagement'); - await this.assertTabsExist('mlSubTab', [ - 'jobManagement', - 'anomalyExplorer', - 'singleMetricViewer', - 'settings', - ]); }, async navigateToDataFrameAnalytics() { await this.navigateToArea('~mlMainTab & ~dataFrameAnalytics', 'mlPageDataFrameAnalytics'); - await this.assertTabsExist('mlSubTab', []); }, async navigateToDataVisualizer() { await this.navigateToArea('~mlMainTab & ~dataVisualizer', 'mlPageDataVisualizerSelector'); - await this.assertTabsExist('mlSubTab', []); }, async navigateToJobManagement() { await this.navigateToAnomalyDetection(); - await this.navigateToArea('~mlSubTab & ~jobManagement', 'mlPageJobManagement'); - }, - - async navigateToAnomalyExplorer() { - await this.navigateToAnomalyDetection(); - await this.navigateToArea('~mlSubTab & ~anomalyExplorer', 'mlPageAnomalyExplorer'); - }, - - async navigateToSingleMetricViewer() { - await this.navigateToAnomalyDetection(); - await this.navigateToArea('~mlSubTab & ~singleMetricViewer', 'mlPageSingleMetricViewer'); }, async navigateToSettings() { - await this.navigateToAnomalyDetection(); - await this.navigateToArea('~mlSubTab & ~settings', 'mlPageSettings'); + await this.navigateToArea('~mlMainTab & ~settings', 'mlPageSettings'); }, }; } diff --git a/x-pack/test/functional/services/ml/settings.ts b/x-pack/test/functional/services/ml/settings.ts index 15a3131e7c969..f7428d06899bf 100644 --- a/x-pack/test/functional/services/ml/settings.ts +++ b/x-pack/test/functional/services/ml/settings.ts @@ -10,12 +10,20 @@ export function MachineLearningSettingsProvider({ getService }: FtrProviderConte const testSubjects = getService('testSubjects'); return { - async assertSettingsCalendarLinkExists() { - await testSubjects.existOrFail('ml_calendar_mng_button'); + async assertSettingsManageCalendarsLinkExists() { + await testSubjects.existOrFail('mlCalendarsMngButton'); }, - async assertSettingsFilterlistLinkExists() { - await testSubjects.existOrFail('ml_filter_lists_button'); + async assertSettingsCreateCalendarLinkExists() { + await testSubjects.existOrFail('mlCalendarsCreateButton'); + }, + + async assertSettingsManageFilterListsLinkExists() { + await testSubjects.existOrFail('mlFilterListsMngButton'); + }, + + async assertSettingsCreateFilterListLinkExists() { + await testSubjects.existOrFail('mlFilterListsCreateButton'); }, }; } diff --git a/x-pack/test/functional_endpoint/page_objects/endpoint_alerts_page.ts b/x-pack/test/functional_endpoint/page_objects/endpoint_alerts_page.ts deleted file mode 100644 index a5ad45536de89..0000000000000 --- a/x-pack/test/functional_endpoint/page_objects/endpoint_alerts_page.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { FtrProviderContext } from '../ftr_provider_context'; - -export function EndpointAlertsPageProvider({ getService }: FtrProviderContext) { - const testSubjects = getService('testSubjects'); - - return { - async enterSearchBarQuery(query: string) { - return await testSubjects.setValue('alertsSearchBar', query, { clearWithKeyboard: true }); - }, - async submitSearchBarFilter() { - return await testSubjects.click('querySubmitButton'); - }, - async setSearchBarDate(timestamp: string) { - await testSubjects.click('superDatePickerShowDatesButton'); - await testSubjects.click('superDatePickerstartDatePopoverButton'); - await testSubjects.click('superDatePickerAbsoluteTab'); - await testSubjects.setValue('superDatePickerAbsoluteDateInput', timestamp); - await this.submitSearchBarFilter(); - }, - }; -} diff --git a/x-pack/test/functional_endpoint/page_objects/page_utils.ts b/x-pack/test/functional_endpoint/page_objects/page_utils.ts deleted file mode 100644 index daf66464f7e1e..0000000000000 --- a/x-pack/test/functional_endpoint/page_objects/page_utils.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { FtrProviderContext } from '../ftr_provider_context'; - -export function EndpointPageUtils({ getService }: FtrProviderContext) { - const find = getService('find'); - - return { - /** - * Finds a given EuiCheckbox by test subject and clicks on it - * - * @param euiCheckBoxTestId - */ - async clickOnEuiCheckbox(euiCheckBoxTestId: string) { - // This utility is needed because EuiCheckbox forwards the test subject on to - // the actual `` which is not actually visible/accessible on the page. - // In order to actually cause the state of the checkbox to change, the `