diff --git a/docs/developer/getting-started/monorepo-packages.asciidoc b/docs/developer/getting-started/monorepo-packages.asciidoc index 5d7ba22841aa1..217645b903818 100644 --- a/docs/developer/getting-started/monorepo-packages.asciidoc +++ b/docs/developer/getting-started/monorepo-packages.asciidoc @@ -70,6 +70,7 @@ yarn kbn watch-bazel - @kbn/apm-utils - @kbn/babel-code-parser - @kbn/babel-preset +- @kbn/cli-dev-mode - @kbn/config - @kbn/config-schema - @kbn/crypto @@ -87,6 +88,7 @@ yarn kbn watch-bazel - @kbn/mapbox-gl - @kbn/monaco - @kbn/optimizer +- @kbn/plugin-helpers - @kbn/rule-data-utils - @kbn/securitysolution-es-utils - @kbn/securitysolution-hook-utils diff --git a/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.set.md b/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.set.md index 143cd397c40ae..bf08ca1682f3b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.set.md +++ b/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.set.md @@ -24,5 +24,7 @@ set(status$: Observable): void; ## Remarks +The first emission from this Observable should occur within 30s, else this plugin's status will fallback to `unavailable` until the first emission. + See the [StatusServiceSetup.derivedStatus$](./kibana-plugin-core-server.statusservicesetup.derivedstatus_.md) API for leveraging the default status calculation that is provided by Core. diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md index 54b5a33ccf682..2ca4847d6dc39 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md @@ -13,11 +13,11 @@ esFilters: { FILTERS: typeof FILTERS; FilterStateStore: typeof FilterStateStore; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("../common").Filter; - buildPhrasesFilter: (field: import("../common").IFieldType, params: any[], indexPattern: import("../common").IIndexPattern) => import("../common").PhrasesFilter; - buildExistsFilter: (field: import("../common").IFieldType, indexPattern: import("../common").IIndexPattern) => import("../common").ExistsFilter; - buildPhraseFilter: (field: import("../common").IFieldType, value: any, indexPattern: import("../common").IIndexPattern) => import("../common").PhraseFilter; + buildPhrasesFilter: (field: import("../common").IFieldType, params: any[], indexPattern: import("../common").MinimalIndexPattern) => import("../common").PhrasesFilter; + buildExistsFilter: (field: import("../common").IFieldType, indexPattern: import("../common").MinimalIndexPattern) => import("../common").ExistsFilter; + buildPhraseFilter: (field: import("../common").IFieldType, value: any, indexPattern: import("../common").MinimalIndexPattern) => import("../common").PhraseFilter; buildQueryFilter: (query: any, index: string, alias: string) => import("../common").QueryStringFilter; - buildRangeFilter: (field: import("../common").IFieldType, params: import("../common").RangeFilterParams, indexPattern: import("../common").IIndexPattern, formattedValue?: string | undefined) => import("../common").RangeFilter; + buildRangeFilter: (field: import("../common").IFieldType, params: import("../common").RangeFilterParams, indexPattern: import("../common").MinimalIndexPattern, formattedValue?: string | undefined) => import("../common").RangeFilter; isPhraseFilter: (filter: any) => filter is import("../common").PhraseFilter; isExistsFilter: (filter: any) => filter is import("../common").ExistsFilter; isPhrasesFilter: (filter: any) => filter is import("../common").PhrasesFilter; diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md index 2cde2b7455585..881a1fa803ca6 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md @@ -10,6 +10,6 @@ esKuery: { nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; + toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").MinimalIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; } ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md index 2430e6a93bd2b..70805aaaaee8c 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md @@ -10,7 +10,7 @@ esQuery: { buildEsQuery: typeof buildEsQuery; getEsQueryConfig: typeof getEsQueryConfig; - buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").IIndexPattern | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { + buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").MinimalIndexPattern | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { must: never[]; filter: import("../common").Filter[]; should: never[]; diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.fields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.fields.md deleted file mode 100644 index 792bee44f96a8..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.fields.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) > [fields](./kibana-plugin-plugins-data-public.iindexpattern.fields.md) - -## IIndexPattern.fields property - -Signature: - -```typescript -fields: IFieldType[]; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.id.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.id.md deleted file mode 100644 index 917a80975df6c..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) > [id](./kibana-plugin-plugins-data-public.iindexpattern.id.md) - -## IIndexPattern.id property - -Signature: - -```typescript -id?: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md index bf7f88ab37039..88d8520a373c6 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md @@ -12,7 +12,7 @@ Signature: ```typescript -export interface IIndexPattern +export interface IIndexPattern extends MinimalIndexPattern ``` ## Properties @@ -20,9 +20,7 @@ export interface IIndexPattern | Property | Type | Description | | --- | --- | --- | | [fieldFormatMap](./kibana-plugin-plugins-data-public.iindexpattern.fieldformatmap.md) | Record<string, SerializedFieldFormat<unknown> | undefined> | | -| [fields](./kibana-plugin-plugins-data-public.iindexpattern.fields.md) | IFieldType[] | | | [getFormatterForField](./kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md) | (field: IndexPatternField | IndexPatternField['spec'] | IFieldType) => FieldFormat | Look up a formatter for a given field | -| [id](./kibana-plugin-plugins-data-public.iindexpattern.id.md) | string | | | [timeFieldName](./kibana-plugin-plugins-data-public.iindexpattern.timefieldname.md) | string | | | [title](./kibana-plugin-plugins-data-public.iindexpattern.title.md) | string | | | [type](./kibana-plugin-plugins-data-public.iindexpattern.type.md) | string | Type is used for identifying rollup indices, otherwise left undefined | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md index d7e80d94db4e6..d951cb2426943 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md @@ -11,11 +11,11 @@ esFilters: { buildQueryFilter: (query: any, index: string, alias: string) => import("../common").QueryStringFilter; buildCustomFilter: typeof buildCustomFilter; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("../common").Filter; - buildExistsFilter: (field: import("../common").IFieldType, indexPattern: import("../common").IIndexPattern) => import("../common").ExistsFilter; + buildExistsFilter: (field: import("../common").IFieldType, indexPattern: import("../common").MinimalIndexPattern) => import("../common").ExistsFilter; buildFilter: typeof buildFilter; - buildPhraseFilter: (field: import("../common").IFieldType, value: any, indexPattern: import("../common").IIndexPattern) => import("../common").PhraseFilter; - buildPhrasesFilter: (field: import("../common").IFieldType, params: any[], indexPattern: import("../common").IIndexPattern) => import("../common").PhrasesFilter; - buildRangeFilter: (field: import("../common").IFieldType, params: import("../common").RangeFilterParams, indexPattern: import("../common").IIndexPattern, formattedValue?: string | undefined) => import("../common").RangeFilter; + buildPhraseFilter: (field: import("../common").IFieldType, value: any, indexPattern: import("../common").MinimalIndexPattern) => import("../common").PhraseFilter; + buildPhrasesFilter: (field: import("../common").IFieldType, params: any[], indexPattern: import("../common").MinimalIndexPattern) => import("../common").PhrasesFilter; + buildRangeFilter: (field: import("../common").IFieldType, params: import("../common").RangeFilterParams, indexPattern: import("../common").MinimalIndexPattern, formattedValue?: string | undefined) => import("../common").RangeFilter; isFilterDisabled: (filter: import("../common").Filter) => boolean; } ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md index 4b96d8af756f3..6274eb5f4f4a5 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md @@ -10,6 +10,6 @@ esKuery: { nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; + toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").MinimalIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; } ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esquery.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esquery.md index ac9be23bc6b6f..0d1baecb014f5 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esquery.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esquery.md @@ -8,7 +8,7 @@ ```typescript esQuery: { - buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").IIndexPattern | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { + buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").MinimalIndexPattern | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { must: never[]; filter: import("../common").Filter[]; should: never[]; diff --git a/docs/management/action-types.asciidoc b/docs/management/action-types.asciidoc index 65b600d4b7281..3d3d7aeb2d777 100644 --- a/docs/management/action-types.asciidoc +++ b/docs/management/action-types.asciidoc @@ -43,6 +43,10 @@ a| <> | Send a message to a Slack channel or user. +a| <> + +| Create an incident in Swimlane. + a| <> | Send a request to a web service. diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 853180ec816e9..66a23ee189ae1 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -482,6 +482,9 @@ of buckets to try to represent. [[visualization-visualize-chartslibrary]]`visualization:visualize:legacyChartsLibrary`:: Enables the legacy charts library for aggregation-based area, line, and bar charts in *Visualize*. +[[visualization-visualize-pieChartslibrary]]`visualization:visualize:legacyPieChartsLibrary`:: +Enables the legacy charts library for aggregation-based pie charts in *Visualize*. + [[visualization-colormapping]]`visualization:colorMapping`:: **This setting is deprecated and will not be supported as of 8.0.** Maps values to specific colors in charts using the *Compatibility* palette. diff --git a/docs/management/connectors/action-types/swimlane.asciidoc b/docs/management/connectors/action-types/swimlane.asciidoc new file mode 100644 index 0000000000000..88447bb496a86 --- /dev/null +++ b/docs/management/connectors/action-types/swimlane.asciidoc @@ -0,0 +1,105 @@ +[role="xpack"] +[[swimlane-action-type]] +=== Swimlane connector and action +++++ +Swimlane +++++ + +The Swimlane connector uses the https://swimlane.com/knowledge-center/docs/developer-guide/rest-api/[Swimlane REST API] to create Swimlane records. + +[float] +[[swimlane-connector-configuration]] +==== Connector configuration + +Swimlane connectors have the following configuration properties. + +Name:: The name of the connector. The name is used to identify a connector in the **Stack Management** UI connector listing, and in the connector list when configuring an action. +URL:: Swimlane instance URL. +Application ID:: Swimlane application ID. +API token:: Swimlane API authentication token for HTTP Basic authentication. + +[float] +[[Preconfigured-swimlane-configuration]] +==== Preconfigured connector type + +[source,text] +-- + my-swimlane: + name: preconfigured-swimlane-connector-type + actionTypeId: .swimlane + config: + apiUrl: https://elastic.swimlaneurl.us + appId: app-id + mappings: + alertIdConfig: + fieldType: text + id: agp4s + key: alert-id + name: Alert ID + caseIdConfig: + fieldType: text + id: ae1mi + key: case-id + name: Case ID + caseNameConfig: + fieldType: text + id: anxnr + key: case-name + name: Case Name + commentsConfig: + fieldType: comments + id: au18d + key: comments + name: Comments + descriptionConfig: + fieldType: text + id: ae1gd + key: description + name: Description + ruleNameConfig: + fieldType: text + id: avfsl + key: rule-name + name: Rule Name + severityConfig: + fieldType: text + id: a71ik + key: severity + name: severity + secrets: + apiToken: tokenkeystorevalue +-- + +Config defines information for the connector type. + +`apiUrl`:: An address that corresponds to *URL*. +`appId`:: A key that corresponds to *Application ID*. + +Secrets defines sensitive information for the connector type. + +`apiToken`:: A string that corresponds to *API Token*. Should be stored in the <>. + +[float] +[[define-swimlane-ui]] +==== Define connector in Stack Management + +Define Swimlane connector properties. + +[role="screenshot"] +image::management/connectors/images/swimlane-connector.png[Swimlane connector] + +Test Swimlane action parameters. + +[role="screenshot"] +image::management/connectors/images/swimlane-params-test.png[Swimlane params test] + +[float] +[[swimlane-action-configuration]] +==== Action configuration + +Swimlane actions have the following configuration properties. + +Comments:: Additional information for the client, such as how to troubleshoot the issue. +Severity:: The severity of the incident. + +NOTE: Alert ID and Rule Name are filled automatically. Specifically, Alert ID is set to `{{alert.id}}` and Rule Name to `{{rule.name}}`. \ No newline at end of file diff --git a/docs/management/connectors/images/swimlane-connector.png b/docs/management/connectors/images/swimlane-connector.png new file mode 100644 index 0000000000000..520c35d00381b Binary files /dev/null and b/docs/management/connectors/images/swimlane-connector.png differ diff --git a/docs/management/connectors/images/swimlane-params-test.png b/docs/management/connectors/images/swimlane-params-test.png new file mode 100644 index 0000000000000..c0e02c2c7b18f Binary files /dev/null and b/docs/management/connectors/images/swimlane-params-test.png differ diff --git a/docs/management/connectors/index.asciidoc b/docs/management/connectors/index.asciidoc index ea4fa46d3e808..033b1c3ac150e 100644 --- a/docs/management/connectors/index.asciidoc +++ b/docs/management/connectors/index.asciidoc @@ -6,6 +6,7 @@ include::action-types/teams.asciidoc[] include::action-types/pagerduty.asciidoc[] include::action-types/server-log.asciidoc[] include::action-types/servicenow.asciidoc[] +include::action-types/swimlane.asciidoc[] include::action-types/slack.asciidoc[] include::action-types/webhook.asciidoc[] include::pre-configured-connectors.asciidoc[] diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index 71f141d1ed5d6..d1d283ca60fbb 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -69,7 +69,7 @@ You can configure the following settings in the `kibana.yml` file. -- xpack.actions.customHostSettings: - url: smtp://mail.example.com:465 - tls: + ssl: verificationMode: 'full' certificateAuthoritiesFiles: [ 'one.crt' ] certificateAuthoritiesData: | @@ -79,7 +79,7 @@ xpack.actions.customHostSettings: smtp: requireTLS: true - url: https://webhook.example.com - tls: + ssl: // legacy rejectUnauthorized: false verificationMode: 'none' @@ -97,8 +97,8 @@ xpack.actions.customHostSettings: server, and the `https` URLs are used for actions which use `https` to connect to services. + + - Entries with `https` URLs can use the `tls` options, and entries with `smtp` - URLs can use both the `tls` and `smtp` options. + + Entries with `https` URLs can use the `ssl` options, and entries with `smtp` + URLs can use both the `ssl` and `smtp` options. + + No other URL values should be part of this URL, including paths, query strings, and authentication information. When an http or smtp request @@ -117,24 +117,24 @@ xpack.actions.customHostSettings: The options `smtp.ignoreTLS` and `smtp.requireTLS` can not both be set to true. | `xpack.actions.customHostSettings[n]` -`.tls.rejectUnauthorized` {ess-icon} - | Deprecated. Use <> instead. A boolean value indicating whether to bypass server certificate validation. +`.ssl.rejectUnauthorized` {ess-icon} + | Deprecated. Use <> instead. A boolean value indicating whether to bypass server certificate validation. Overrides the general `xpack.actions.rejectUnauthorized` configuration for requests made for this hostname/port. |[[action-config-custom-host-verification-mode]] `xpack.actions.customHostSettings[n]` -`.tls.verificationMode` +`.ssl.verificationMode` | Controls the verification of the server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection to the host server. Valid values are `full`, `certificate`, and `none`. - Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. Overrides the general `xpack.actions.tls.verificationMode` configuration + Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. Overrides the general `xpack.actions.ssl.verificationMode` configuration for requests made for this hostname/port. | `xpack.actions.customHostSettings[n]` -`.tls.certificateAuthoritiesFiles` +`.ssl.certificateAuthoritiesFiles` | A file name or list of file names of PEM-encoded certificate files to use to validate the server. | `xpack.actions.customHostSettings[n]` -`.tls.certificateAuthoritiesData` {ess-icon} +`.ssl.certificateAuthoritiesData` {ess-icon} | The contents of a PEM-encoded certificate file, or multiple files appended into a single string. This configuration can be used for environments where the files cannot be made available. @@ -165,28 +165,28 @@ xpack.actions.customHostSettings: a|`xpack.actions.` `proxyRejectUnauthorizedCertificates` {ess-icon} - | Deprecated. Use <> instead. Set to `false` to bypass certificate validation for the proxy, if using a proxy for actions. Default: `true`. + | Deprecated. Use <> instead. Set to `false` to bypass certificate validation for the proxy, if using a proxy for actions. Default: `true`. |[[action-config-proxy-verification-mode]] `xpack.actions[n]` -`.tls.proxyVerificationMode` {ess-icon} +`.ssl.proxyVerificationMode` {ess-icon} | Controls the verification for the proxy server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection to the proxy server. Valid values are `full`, `certificate`, and `none`. Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. | `xpack.actions.rejectUnauthorized` {ess-icon} - | Deprecated. Use <> instead. Set to `false` to bypass certificate validation for actions. Default: `true`. + + | Deprecated. Use <> instead. Set to `false` to bypass certificate validation for actions. Default: `true`. + + As an alternative to setting `xpack.actions.rejectUnauthorized`, you can use the setting - `xpack.actions.customHostSettings` to set TLS options for specific servers. + `xpack.actions.customHostSettings` to set SSL options for specific servers. |[[action-config-verification-mode]] `xpack.actions[n]` -`.tls.verificationMode` {ess-icon} +`.ssl.verificationMode` {ess-icon} | Controls the verification for the server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection for actions. Valid values are `full`, `certificate`, and `none`. Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. + + - As an alternative to setting `xpack.actions.tls.verificationMode`, you can use the setting - `xpack.actions.customHostSettings` to set TLS options for specific servers. + As an alternative to setting `xpack.actions.ssl.verificationMode`, you can use the setting + `xpack.actions.customHostSettings` to set SSL options for specific servers. diff --git a/docs/settings/task-manager-settings.asciidoc b/docs/settings/task-manager-settings.asciidoc index 87f5b700870eb..7f4dbb3a96e6b 100644 --- a/docs/settings/task-manager-settings.asciidoc +++ b/docs/settings/task-manager-settings.asciidoc @@ -29,7 +29,13 @@ Task Manager runs background tasks by polling for work on an interval. You can | The maximum number of tasks that this Kibana instance will run simultaneously. Defaults to 10. Starting in 8.0, it will not be possible to set the value greater than 100. - | `xpack.task_manager.monitored_stats_warn_delayed_task_start_in_seconds` + | `xpack.task_manager.` + `monitored_stats_health_verbose_log.enabled` + | This flag will enable automatic warn and error logging if task manager self detects a performance issue, such as the time between when a task is scheduled to execute and when it actually executes. Defaults to false. + + | `xpack.task_manager.` + `monitored_stats_health_verbose_log.` + `warn_delayed_task_start_in_seconds` | The amount of seconds we allow a task to delay before printing a warning server log. Defaults to 60. |=== diff --git a/package.json b/package.json index 26465133569cd..ecedb64c343ec 100644 --- a/package.json +++ b/package.json @@ -103,16 +103,16 @@ "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.13", "@elastic/ems-client": "7.14.0", - "@elastic/eui": "33.0.0", + "@elastic/eui": "34.3.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "^9.0.1-kibana3", "@elastic/maki": "6.3.0", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", - "@elastic/react-search-ui": "^1.5.1", + "@elastic/react-search-ui": "^1.6.0", "@elastic/request-crypto": "1.1.4", "@elastic/safer-lodash-set": "link:bazel-bin/packages/elastic-safer-lodash-set", - "@elastic/search-ui-app-search-connector": "^1.5.0", + "@elastic/search-ui-app-search-connector": "^1.6.0", "@elastic/ui-ace": "0.2.3", "@hapi/accept": "^5.0.2", "@hapi/boom": "^9.1.1", @@ -457,7 +457,7 @@ "@jest/reporters": "^26.6.2", "@kbn/babel-code-parser": "link:bazel-bin/packages/kbn-babel-code-parser", "@kbn/babel-preset": "link:bazel-bin/packages/kbn-babel-preset", - "@kbn/cli-dev-mode": "link:packages/kbn-cli-dev-mode", + "@kbn/cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode", "@kbn/dev-utils": "link:bazel-bin/packages/kbn-dev-utils", "@kbn/docs-utils": "link:bazel-bin/packages/kbn-docs-utils", "@kbn/es": "link:bazel-bin/packages/kbn-es", @@ -467,7 +467,7 @@ "@kbn/expect": "link:bazel-bin/packages/kbn-expect", "@kbn/optimizer": "link:bazel-bin/packages/kbn-optimizer", "@kbn/plugin-generator": "link:bazel-bin/packages/kbn-plugin-generator", - "@kbn/plugin-helpers": "link:packages/kbn-plugin-helpers", + "@kbn/plugin-helpers": "link:bazel-bin/packages/kbn-plugin-helpers", "@kbn/pm": "link:packages/kbn-pm", "@kbn/storybook": "link:bazel-bin/packages/kbn-storybook", "@kbn/telemetry-tools": "link:bazel-bin/packages/kbn-telemetry-tools", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index d9e2f0e1f9985..1094a2def3e70 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -12,6 +12,7 @@ filegroup( "//packages/kbn-apm-utils:build", "//packages/kbn-babel-code-parser:build", "//packages/kbn-babel-preset:build", + "//packages/kbn-cli-dev-mode:build", "//packages/kbn-common-utils:build", "//packages/kbn-config:build", "//packages/kbn-config-schema:build", @@ -31,6 +32,7 @@ filegroup( "//packages/kbn-monaco:build", "//packages/kbn-optimizer:build", "//packages/kbn-plugin-generator:build", + "//packages/kbn-plugin-helpers:build", "//packages/kbn-rule-data-utils:build", "//packages/kbn-securitysolution-list-constants:build", "//packages/kbn-securitysolution-io-ts-types:build", diff --git a/packages/elastic-eslint-config-kibana/.eslintrc.js b/packages/elastic-eslint-config-kibana/.eslintrc.js index a8c2e9546510e..3220a01184004 100644 --- a/packages/elastic-eslint-config-kibana/.eslintrc.js +++ b/packages/elastic-eslint-config-kibana/.eslintrc.js @@ -75,6 +75,11 @@ module.exports = { to: '@kbn/test', disallowedMessage: `import from the root of @kbn/test instead` }, + { + from: 'react-intl', + to: '@kbn/i18n/react', + disallowedMessage: `import from @kbn/i18n/react instead` + } ], ], }, diff --git a/packages/kbn-cli-dev-mode/BUILD.bazel b/packages/kbn-cli-dev-mode/BUILD.bazel new file mode 100644 index 0000000000000..ab1b6601f429b --- /dev/null +++ b/packages/kbn-cli-dev-mode/BUILD.bazel @@ -0,0 +1,103 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-cli-dev-mode" +PKG_REQUIRE_NAME = "@kbn/cli-dev-mode" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = ["**/*.test.*"], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md" +] + +SRC_DEPS = [ + "//packages/kbn-config", + "//packages/kbn-config-schema", + "//packages/kbn-dev-utils", + "//packages/kbn-logging", + "//packages/kbn-optimizer", + "//packages/kbn-server-http-tools", + "//packages/kbn-std", + "//packages/kbn-utils", + "@npm//@hapi/h2o2", + "@npm//@hapi/hapi", + "@npm//argsplit", + "@npm//chokidar", + "@npm//elastic-apm-node", + "@npm//execa", + "@npm//getopts", + "@npm//lodash", + "@npm//moment", + "@npm//rxjs", + "@npm//supertest", +] + +TYPES_DEPS = [ + "@npm//@types/hapi__h2o2", + "@npm//@types/hapi__hapi", + "@npm//@types/getopts", + "@npm//@types/jest", + "@npm//@types/lodash", + "@npm//@types/node", + "@npm//@types/supertest", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = DEPS + [":tsc"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-cli-dev-mode/package.json b/packages/kbn-cli-dev-mode/package.json index cf6fcfd88a26d..ac86ee2ef369b 100644 --- a/packages/kbn-cli-dev-mode/package.json +++ b/packages/kbn-cli-dev-mode/package.json @@ -5,11 +5,6 @@ "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", "private": true, - "scripts": { - "build": "../../node_modules/.bin/tsc", - "kbn:bootstrap": "yarn build", - "kbn:watch": "yarn build --watch" - }, "kibana": { "devOnly": true } diff --git a/packages/kbn-cli-dev-mode/tsconfig.json b/packages/kbn-cli-dev-mode/tsconfig.json index 4436d27dbff88..0c71ad8e245d4 100644 --- a/packages/kbn-cli-dev-mode/tsconfig.json +++ b/packages/kbn-cli-dev-mode/tsconfig.json @@ -1,10 +1,11 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "incremental": false, + "incremental": true, "outDir": "./target", "declaration": true, "declarationMap": true, + "rootDir": "./src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-cli-dev-mode/src", "types": [ diff --git a/packages/kbn-i18n/src/react/index.tsx b/packages/kbn-i18n/src/react/index.tsx index 08fa7173978d9..bc0a164d412af 100644 --- a/packages/kbn-i18n/src/react/index.tsx +++ b/packages/kbn-i18n/src/react/index.tsx @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +// eslint-disable-next-line @kbn/eslint/module_migration import { InjectedIntl as _InjectedIntl, InjectedIntlProps as _InjectedIntlProps } from 'react-intl'; export type { InjectedIntl, InjectedIntlProps } from 'react-intl'; diff --git a/packages/kbn-i18n/src/react/provider.tsx b/packages/kbn-i18n/src/react/provider.tsx index 2d88125291aa0..fc0f6769c7160 100644 --- a/packages/kbn-i18n/src/react/provider.tsx +++ b/packages/kbn-i18n/src/react/provider.tsx @@ -8,6 +8,8 @@ import * as PropTypes from 'prop-types'; import * as React from 'react'; + +// eslint-disable-next-line @kbn/eslint/module_migration import { IntlProvider } from 'react-intl'; import * as i18n from '../core'; diff --git a/packages/kbn-interpreter/BUILD.bazel b/packages/kbn-interpreter/BUILD.bazel index 4492faabfdf81..c29faf65638ca 100644 --- a/packages/kbn-interpreter/BUILD.bazel +++ b/packages/kbn-interpreter/BUILD.bazel @@ -1,5 +1,5 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") -load("@npm//pegjs:index.bzl", "pegjs") +load("@npm//peggy:index.bzl", "peggy") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") PKG_BASE_NAME = "kbn-interpreter" @@ -37,10 +37,10 @@ TYPES_DEPS = [ DEPS = SRC_DEPS + TYPES_DEPS -pegjs( +peggy( name = "grammar", data = [ - ":grammar/grammar.pegjs" + ":grammar/grammar.peggy" ], output_dir = True, args = [ @@ -48,7 +48,7 @@ pegjs( "expression,argument", "-o", "$(@D)/index.js", - "./%s/grammar/grammar.pegjs" % package_name() + "./%s/grammar/grammar.peggy" % package_name() ], ) diff --git a/packages/kbn-interpreter/grammar/grammar.pegjs b/packages/kbn-interpreter/grammar/grammar.peggy similarity index 100% rename from packages/kbn-interpreter/grammar/grammar.pegjs rename to packages/kbn-interpreter/grammar/grammar.peggy diff --git a/packages/kbn-plugin-helpers/BUILD.bazel b/packages/kbn-plugin-helpers/BUILD.bazel new file mode 100644 index 0000000000000..1a1f3453f768a --- /dev/null +++ b/packages/kbn-plugin-helpers/BUILD.bazel @@ -0,0 +1,97 @@ + +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-plugin-helpers" +PKG_REQUIRE_NAME = "@kbn/plugin-helpers" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md" +] + +SRC_DEPS = [ + "//packages/kbn-dev-utils", + "//packages/kbn-optimizer", + "//packages/kbn-utils", + "@npm//del", + "@npm//execa", + "@npm//extract-zip", + "@npm//globby", + "@npm//gulp-zip", + "@npm//inquirer", + "@npm//load-json-file", + "@npm//vinyl-fs", +] + +TYPES_DEPS = [ + "@npm//@types/extract-zip", + "@npm//@types/gulp-zip", + "@npm//@types/inquirer", + "@npm//@types/jest", + "@npm//@types/node", + "@npm//@types/vinyl-fs", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = DEPS + [":tsc"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index 36a37075191a3..1f4df52a03304 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -11,9 +11,5 @@ "types": "target/index.d.ts", "bin": { "plugin-helpers": "bin/plugin-helpers.js" - }, - "scripts": { - "kbn:bootstrap": "rm -rf target && ../../node_modules/.bin/tsc", - "kbn:watch": "../../node_modules/.bin/tsc --watch" } } \ No newline at end of file diff --git a/packages/kbn-plugin-helpers/tsconfig.json b/packages/kbn-plugin-helpers/tsconfig.json index 87d11843f398a..4348f1e1a7516 100644 --- a/packages/kbn-plugin-helpers/tsconfig.json +++ b/packages/kbn-plugin-helpers/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "incremental": false, + "incremental": true, "outDir": "target", "target": "ES2018", "declaration": true, diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index e455f487d1384..5be9dff630ed5 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -63827,6 +63827,7 @@ function getProjectPaths({ projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/plugin_functional/plugins/*')); projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/interpreter_functional/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/server_integration/__fixtures__/plugins/*')); projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'examples/*')); if (!ossOnly) { diff --git a/packages/kbn-pm/src/config.ts b/packages/kbn-pm/src/config.ts index a11b2ad9c72c3..666a2fed7a33c 100644 --- a/packages/kbn-pm/src/config.ts +++ b/packages/kbn-pm/src/config.ts @@ -31,6 +31,7 @@ export function getProjectPaths({ rootPath, ossOnly, skipKibanaPlugins }: Option // correct and the expect behavior. projectPaths.push(resolve(rootPath, 'test/plugin_functional/plugins/*')); projectPaths.push(resolve(rootPath, 'test/interpreter_functional/plugins/*')); + projectPaths.push(resolve(rootPath, 'test/server_integration/__fixtures__/plugins/*')); projectPaths.push(resolve(rootPath, 'examples/*')); if (!ossOnly) { diff --git a/packages/kbn-tinymath/grammar/grammar.peggy b/packages/kbn-tinymath/grammar/grammar.peggy index 1c6f8c3334c23..414bc2fa11cb7 100644 --- a/packages/kbn-tinymath/grammar/grammar.peggy +++ b/packages/kbn-tinymath/grammar/grammar.peggy @@ -43,7 +43,7 @@ Literal "literal" // Quoted variables are interpreted as strings // but unquoted variables are more restrictive Variable - = _ [\'] chars:(ValidChar / Space / [\"])* [\'] _ { + = _ '"' chars:("\\\"" { return "\""; } / [^"])* '"' _ { return { type: 'variable', value: chars.join(''), @@ -51,7 +51,7 @@ Variable text: text() }; } - / _ [\"] chars:(ValidChar / Space / [\'])* [\"] _ { + / _ "'" chars:("\\\'" { return "\'"; } / [^'])* "'" _ { return { type: 'variable', value: chars.join(''), diff --git a/packages/kbn-tinymath/test/library.test.js b/packages/kbn-tinymath/test/library.test.js index bbc8503684fd4..9d87919c4f1ac 100644 --- a/packages/kbn-tinymath/test/library.test.js +++ b/packages/kbn-tinymath/test/library.test.js @@ -92,6 +92,7 @@ describe('Parser', () => { expect(parse('@foo0')).toEqual(variableEqual('@foo0')); expect(parse('.foo0')).toEqual(variableEqual('.foo0')); expect(parse('-foo0')).toEqual(variableEqual('-foo0')); + expect(() => parse(`foo😀\t')`)).toThrow('Failed to parse'); }); }); @@ -103,6 +104,7 @@ describe('Parser', () => { expect(parse('"foo bar fizz buzz"')).toEqual(variableEqual('foo bar fizz buzz')); expect(parse('"foo bar baby"')).toEqual(variableEqual('foo bar baby')); expect(parse(`"f'oo"`)).toEqual(variableEqual(`f'oo`)); + expect(parse(`"foo😀\t"`)).toEqual(variableEqual(`foo😀\t`)); }); it('strings with single quotes', () => { @@ -119,6 +121,7 @@ describe('Parser', () => { expect(parse("'foo bar '")).toEqual(variableEqual("foo bar ")); expect(parse("'0foo'")).toEqual(variableEqual("0foo")); expect(parse(`'f"oo'`)).toEqual(variableEqual(`f"oo`)); + expect(parse(`'foo😀\t'`)).toEqual(variableEqual(`foo😀\t`)); /* eslint-enable prettier/prettier */ }); diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap index 3668829a6888c..0b10209bc13e5 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -370,54 +370,62 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` isOpen={true} onClose={[Function]} > - - - - } - /> - - -
-
+ + +
+
+ +
-
-
-
- - - -
+ data-euiicon-type="home" + /> + + + Home + + + + + +
-
-
- - + +
+
+ + + + +

+ Recently viewed +

+
+
+ + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" data-test-subj="collapsibleNavGroup-recentlyViewed" + id="generated-id" initialIsOpen={true} - isCollapsible={true} - key="recentlyViewed" + isLoading={false} + isLoadingMessage={false} onToggle={[Function]} - title="Recently viewed" + paddingSize="none" > - - - -

- Recently viewed -

-
-
- - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" +
-
- -
-
+ +
+ +
+ + + +
+
+ - -
+
+
-
- - - -
+ recent 2 + + + + + +
- -
+
+
-
-
- -
-
- + + + +
+
+ +
-
- + + + + + +

+ Analytics +

+
+
+ + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" data-test-subj="collapsibleNavGroup-kibana" - iconType="logoKibana" + id="generated-id" initialIsOpen={true} - isCollapsible={true} - key="kibana" + isLoading={false} + isLoadingMessage={false} onToggle={[Function]} - title="Analytics" + paddingSize="none" > - - - - - - -

- Analytics -

-
-
- - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" +
-
- -
-
+ +
+ +
+ + + +
+
+ - -
+
+
-
- - - -
+ dashboard + + + + + +
- -
+
+
-
-
- + + + + + + + + + +

+ Observability +

+
+
+ + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" data-test-subj="collapsibleNavGroup-observability" - iconType="logoObservability" + id="generated-id" initialIsOpen={true} - isCollapsible={true} - key="observability" + isLoading={false} + isLoadingMessage={false} onToggle={[Function]} - title="Observability" + paddingSize="none" > - - - - - - -

- Observability -

-
-
- - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" +
-
- -
-
+ +
+ +
+ + + +
+
+ - -
+
+
-
- - - -
+ logs + + + + + +
- -
+
+
-
-
- + + + + + + + + + +

+ Security +

+
+
+ + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" data-test-subj="collapsibleNavGroup-securitySolution" - iconType="logoSecurity" + id="generated-id" initialIsOpen={true} - isCollapsible={true} - key="securitySolution" + isLoading={false} + isLoadingMessage={false} onToggle={[Function]} - title="Security" + paddingSize="none" > - - - - - - -

- Security -

-
-
- - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" +
-
- -
-
+ +
+ +
+ + + +
+
+ - -
+
+
-
- - - -
+ siem + + + + + +
- -
+
+
-
-
- + + + + + + + + + +

+ Management +

+
+
+ + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" data-test-subj="collapsibleNavGroup-management" - iconType="managementApp" + id="generated-id" initialIsOpen={true} - isCollapsible={true} - key="management" + isLoading={false} + isLoadingMessage={false} onToggle={[Function]} - title="Management" + paddingSize="none" > - - - - - - -

- Management -

-
-
- - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" +
-
- -
-
+ +
+ +
+ + + +
+
+ - -
+
+
-
- - - -
+ monitoring + + + + + +
- -
+
+
-
-
- + + + +
-
- - - -
+ canvas + + + + + +
- - - +
+
+ + +
-
- -
    - - - - Dock navigation - - , - } - } - color="subdued" - data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" - onClick={[Function]} - size="xs" - > -
  • - -
  • -
    -
-
-
+ , + } + } + color="subdued" + data-test-subj="collapsible-nav-lock" + iconType="lockOpen" + label="Dock navigation" + onClick={[Function]} + size="xs" + > +
  • + +
  • + + +
    - - -
    - - - - - - - -
    - +
    + + + +
    + + `; @@ -2770,42 +2706,57 @@ exports[`CollapsibleNav renders the default nav 3`] = ` isOpen={false} onClose={[Function]} > - - -
    -
    +
    + + + + +

    + Recently viewed +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" data-test-subj="collapsibleNavGroup-recentlyViewed" + id="generated-id" initialIsOpen={true} - isCollapsible={true} - key="recentlyViewed" + isLoading={false} + isLoadingMessage={false} onToggle={[Function]} - title="Recently viewed" + paddingSize="none" > - - - -

    - Recently viewed -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" +
    -
    - -
    -
    + +
    + +
    + + + +
    +
    + - -
    +
    +
    -
    - -
    - -
    -

    - No recently viewed items -

    -
    -
    -
    -
    -
    +

    + No recently viewed items +

    +
    + +
    +
    -
    -
    + + -
    -
    - -
    -
    - + + + +
    +
    + +
    -
    - - + +
    -
    - -
      - - - - Undock navigation - - , - } - } - color="subdued" - data-test-subj="collapsible-nav-lock" - iconType="lock" - label="Undock navigation" - onClick={[Function]} - size="xs" - > -
    • - -
    • -
      -
    -
    -
    + , + } + } + color="subdued" + data-test-subj="collapsible-nav-lock" + iconType="lock" + label="Undock navigation" + onClick={[Function]} + size="xs" + > +
  • + +
  • + + +
    - - -
    - - - - - - - -
    - +
    + + + +
    + + `; diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 6ad1e2d3a1cc6..5aee9ca1b7c08 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -4947,42 +4947,57 @@ exports[`Header renders 1`] = ` isOpen={false} onClose={[Function]} > - - -
    -
    +
    + +
    +
    + +
    -
    -
    -
    - - - -
    + data-euiicon-type="home" + /> + + + Home + + + + + +
    -
    -
    - - + +
    +
    + + + + +

    + Recently viewed +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" data-test-subj="collapsibleNavGroup-recentlyViewed" + id="mockId" initialIsOpen={true} - isCollapsible={true} - key="recentlyViewed" + isLoading={false} + isLoadingMessage={false} onToggle={[Function]} - title="Recently viewed" + paddingSize="none" > - - - -

    - Recently viewed -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" +
    -
    - -
    -
    + +
    + +
    + + + +
    +
    + - -
    +
    +
    -
    - - - -
    + dashboard + + + + + +
    - -
    +
    +
    -
    -
    - -
    -
    - + + + +
    +
    + +
    -
    - +
    + +
      + +
    • + +
    • +
      +
    +
    +
    +
    + + +
    + + + Undock navigation + + , + } + } + color="subdued" + data-test-subj="collapsible-nav-lock" + iconType="lock" + label="Undock navigation" onClick={[Function]} - size="s" + size="xs" >
  • @@ -5445,163 +5540,11 @@ exports[`Header renders 1`] = `
    - - -
    -
    - -
      - - - - Undock navigation - - , - } - } - color="subdued" - data-test-subj="collapsible-nav-lock" - iconType="lock" - label="Undock navigation" - onClick={[Function]} - size="xs" - > -
    • - -
    • -
      -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - -
    + + + + + diff --git a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx index 7f338a859e7b4..460770744d53a 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx @@ -16,10 +16,6 @@ import { httpServiceMock } from '../../../http/http_service.mock'; import { ChromeRecentlyAccessedHistoryItem } from '../../recently_accessed'; import { CollapsibleNav } from './collapsible_nav'; -jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ - htmlIdGenerator: () => () => 'mockId', -})); - const { kibana, observability, security, management } = DEFAULT_APP_CATEGORIES; function mockLink({ title = 'discover', category }: Partial) { diff --git a/src/core/public/chrome/ui/header/header.test.tsx b/src/core/public/chrome/ui/header/header.test.tsx index fdbdde8556eeb..a3a0197b4017e 100644 --- a/src/core/public/chrome/ui/header/header.test.tsx +++ b/src/core/public/chrome/ui/header/header.test.tsx @@ -99,7 +99,7 @@ describe('Header', () => { act(() => isLocked$.next(true)); component.update(); - expect(component.find('nav[aria-label="Primary"]').exists()).toBeTruthy(); + expect(component.find('[data-test-subj="collapsibleNav"]').exists()).toBeTruthy(); expect(component).toMatchSnapshot(); act(() => diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index 67cdd24aae848..246ca83ef5ade 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -87,6 +87,7 @@ export function Header({ const isVisible = useObservable(observables.isVisible$, false); const isLocked = useObservable(observables.isLocked$, false); const [isNavOpen, setIsNavOpen] = useState(false); + const [navId] = useState(htmlIdGenerator()()); const breadcrumbsAppendExtension = useObservable(breadcrumbsAppendExtension$); if (!isVisible) { @@ -99,7 +100,6 @@ export function Header({ } const toggleCollapsibleNavRef = createRef void }>(); - const navId = htmlIdGenerator()(); const className = classnames('hide-for-sharing', 'headerGlobalNav'); const Breadcrumbs = ( diff --git a/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap b/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap index f5a1c51ccbe15..fbd09f3096854 100644 --- a/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap +++ b/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap @@ -26,7 +26,7 @@ Array [ ] `; -exports[`FlyoutService openFlyout() renders a flyout to the DOM 2`] = `"
    Flyout content
    "`; +exports[`FlyoutService openFlyout() renders a flyout to the DOM 2`] = `"
    Flyout content
    "`; exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 1`] = ` Array [ @@ -59,4 +59,4 @@ Array [ ] `; -exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 2`] = `"
    Flyout content 2
    "`; +exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 2`] = `"
    Flyout content 2
    "`; diff --git a/src/core/public/styles/_base.scss b/src/core/public/styles/_base.scss index 3386fa73f328a..de138cdf402e6 100644 --- a/src/core/public/styles/_base.scss +++ b/src/core/public/styles/_base.scss @@ -26,7 +26,7 @@ } .euiBody--collapsibleNavIsDocked .euiBottomBar { - margin-left: $euiCollapsibleNavWidth; + margin-left: 320px; // Hard-coded for now -- @cchaos } // Temporary fix for EuiPageHeader with a bottom border but no tabs or padding diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts index 534d7df9d9466..e1986c5bf1d92 100644 --- a/src/core/server/server.test.ts +++ b/src/core/server/server.test.ts @@ -114,6 +114,7 @@ test('runs services on "start"', async () => { expect(mockSavedObjectsService.start).not.toHaveBeenCalled(); expect(mockUiSettingsService.start).not.toHaveBeenCalled(); expect(mockMetricsService.start).not.toHaveBeenCalled(); + expect(mockStatusService.start).not.toHaveBeenCalled(); await server.start(); @@ -121,6 +122,7 @@ test('runs services on "start"', async () => { expect(mockSavedObjectsService.start).toHaveBeenCalledTimes(1); expect(mockUiSettingsService.start).toHaveBeenCalledTimes(1); expect(mockMetricsService.start).toHaveBeenCalledTimes(1); + expect(mockStatusService.start).toHaveBeenCalledTimes(1); }); test('does not fail on "setup" if there are unused paths detected', async () => { diff --git a/src/core/server/server.ts b/src/core/server/server.ts index adf794c390338..3f553dd90678e 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -248,6 +248,7 @@ export class Server { savedObjects: savedObjectsStart, exposedConfigsToUsage: this.plugins.getExposedPluginConfigsToUsage(), }); + this.status.start(); this.coreStart = { capabilities: capabilitiesStart, @@ -261,7 +262,6 @@ export class Server { await this.plugins.start(this.coreStart); - this.status.start(); await this.http.start(); startTransaction?.end(); diff --git a/src/core/server/status/plugins_status.test.ts b/src/core/server/status/plugins_status.test.ts index b0d9e47876940..9dc1ddcddca3e 100644 --- a/src/core/server/status/plugins_status.test.ts +++ b/src/core/server/status/plugins_status.test.ts @@ -8,7 +8,7 @@ import { PluginName } from '../plugins'; import { PluginsStatusService } from './plugins_status'; -import { of, Observable, BehaviorSubject } from 'rxjs'; +import { of, Observable, BehaviorSubject, ReplaySubject } from 'rxjs'; import { ServiceStatusLevels, CoreStatus, ServiceStatus } from './types'; import { first } from 'rxjs/operators'; import { ServiceStatusLevelSnapshotSerializer } from './test_utils'; @@ -34,6 +34,28 @@ describe('PluginStatusService', () => { ['c', ['a', 'b']], ]); + describe('set', () => { + it('throws an exception if called after registrations are blocked', () => { + const service = new PluginsStatusService({ + core$: coreAllAvailable$, + pluginDependencies, + }); + + service.blockNewRegistrations(); + expect(() => { + service.set( + 'a', + of({ + level: ServiceStatusLevels.available, + summary: 'fail!', + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Custom statuses cannot be registered after setup, plugin [a] attempted"` + ); + }); + }); + describe('getDerivedStatus$', () => { it(`defaults to core's most severe status`, async () => { const serviceAvailable = new PluginsStatusService({ @@ -231,6 +253,75 @@ describe('PluginStatusService', () => { { a: { level: ServiceStatusLevels.available, summary: 'a available' } }, ]); }); + + it('updates when a plugin status observable emits', async () => { + const service = new PluginsStatusService({ + core$: coreAllAvailable$, + pluginDependencies: new Map([['a', []]]), + }); + const statusUpdates: Array> = []; + const subscription = service + .getAll$() + .subscribe((pluginStatuses) => statusUpdates.push(pluginStatuses)); + + const aStatus$ = new BehaviorSubject({ + level: ServiceStatusLevels.degraded, + summary: 'a degraded', + }); + service.set('a', aStatus$); + aStatus$.next({ level: ServiceStatusLevels.unavailable, summary: 'a unavailable' }); + aStatus$.next({ level: ServiceStatusLevels.available, summary: 'a available' }); + subscription.unsubscribe(); + + expect(statusUpdates).toEqual([ + { a: { level: ServiceStatusLevels.available, summary: 'All dependencies are available' } }, + { a: { level: ServiceStatusLevels.degraded, summary: 'a degraded' } }, + { a: { level: ServiceStatusLevels.unavailable, summary: 'a unavailable' } }, + { a: { level: ServiceStatusLevels.available, summary: 'a available' } }, + ]); + }); + + it('emits an unavailable status if first emission times out, then continues future emissions', async () => { + jest.useFakeTimers(); + const service = new PluginsStatusService({ + core$: coreAllAvailable$, + pluginDependencies: new Map([ + ['a', []], + ['b', ['a']], + ]), + }); + + const pluginA$ = new ReplaySubject(1); + service.set('a', pluginA$); + const firstEmission = service.getAll$().pipe(first()).toPromise(); + jest.runAllTimers(); + + expect(await firstEmission).toEqual({ + a: { level: ServiceStatusLevels.unavailable, summary: 'Status check timed out after 30s' }, + b: { + level: ServiceStatusLevels.unavailable, + summary: '[a]: Status check timed out after 30s', + detail: 'See the status page for more information', + meta: { + affectedServices: { + a: { + level: ServiceStatusLevels.unavailable, + summary: 'Status check timed out after 30s', + }, + }, + }, + }, + }); + + pluginA$.next({ level: ServiceStatusLevels.available, summary: 'a available' }); + const secondEmission = service.getAll$().pipe(first()).toPromise(); + jest.runAllTimers(); + expect(await secondEmission).toEqual({ + a: { level: ServiceStatusLevels.available, summary: 'a available' }, + b: { level: ServiceStatusLevels.available, summary: 'All dependencies are available' }, + }); + jest.useRealTimers(); + }); }); describe('getDependenciesStatus$', () => { diff --git a/src/core/server/status/plugins_status.ts b/src/core/server/status/plugins_status.ts index 1aacbf3be56db..6a8ef1081e165 100644 --- a/src/core/server/status/plugins_status.ts +++ b/src/core/server/status/plugins_status.ts @@ -7,13 +7,22 @@ */ import { BehaviorSubject, Observable, combineLatest, of } from 'rxjs'; -import { map, distinctUntilChanged, switchMap, debounceTime } from 'rxjs/operators'; +import { + map, + distinctUntilChanged, + switchMap, + debounceTime, + timeoutWith, + startWith, +} from 'rxjs/operators'; import { isDeepStrictEqual } from 'util'; import { PluginName } from '../plugins'; -import { ServiceStatus, CoreStatus } from './types'; +import { ServiceStatus, CoreStatus, ServiceStatusLevels } from './types'; import { getSummaryStatus } from './get_summary_status'; +const STATUS_TIMEOUT_MS = 30 * 1000; // 30 seconds + interface Deps { core$: Observable; pluginDependencies: ReadonlyMap; @@ -23,6 +32,7 @@ export class PluginsStatusService { private readonly pluginStatuses = new Map>(); private readonly update$ = new BehaviorSubject(true); private readonly defaultInheritedStatus$: Observable; + private newRegistrationsAllowed = true; constructor(private readonly deps: Deps) { this.defaultInheritedStatus$ = this.deps.core$.pipe( @@ -35,10 +45,19 @@ export class PluginsStatusService { } public set(plugin: PluginName, status$: Observable) { + if (!this.newRegistrationsAllowed) { + throw new Error( + `Custom statuses cannot be registered after setup, plugin [${plugin}] attempted` + ); + } this.pluginStatuses.set(plugin, status$); this.update$.next(true); // trigger all existing Observables to update from the new source Observable } + public blockNewRegistrations() { + this.newRegistrationsAllowed = false; + } + public getAll$(): Observable> { return this.getPluginStatuses$([...this.deps.pluginDependencies.keys()]); } @@ -86,13 +105,22 @@ export class PluginsStatusService { return this.update$.pipe( switchMap(() => { const pluginStatuses = plugins - .map( - (depName) => - [depName, this.pluginStatuses.get(depName) ?? this.getDerivedStatus$(depName)] as [ - PluginName, - Observable - ] - ) + .map((depName) => { + const pluginStatus = this.pluginStatuses.get(depName) + ? this.pluginStatuses.get(depName)!.pipe( + timeoutWith( + STATUS_TIMEOUT_MS, + this.pluginStatuses.get(depName)!.pipe( + startWith({ + level: ServiceStatusLevels.unavailable, + summary: `Status check timed out after ${STATUS_TIMEOUT_MS / 1000}s`, + }) + ) + ) + ) + : this.getDerivedStatus$(depName); + return [depName, pluginStatus] as [PluginName, Observable]; + }) .map(([pName, status$]) => status$.pipe(map((status) => [pName, status] as [PluginName, ServiceStatus])) ); diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts index b8c19508a5d61..d4dc8ed3d4d72 100644 --- a/src/core/server/status/status_service.ts +++ b/src/core/server/status/status_service.ts @@ -135,9 +135,11 @@ export class StatusService implements CoreService { } public start() { - if (!this.overall$) { - throw new Error('cannot call `start` before `setup`'); + if (!this.pluginsStatus || !this.overall$) { + throw new Error(`StatusService#setup must be called before #start`); } + this.pluginsStatus.blockNewRegistrations(); + getOverallStatusChanges(this.overall$, this.stop$).subscribe((message) => { this.logger.info(message); }); diff --git a/src/core/server/status/types.ts b/src/core/server/status/types.ts index 411b942c8eb33..bfca4c74d9365 100644 --- a/src/core/server/status/types.ts +++ b/src/core/server/status/types.ts @@ -196,6 +196,9 @@ export interface StatusServiceSetup { * Completely overrides the default inherited status. * * @remarks + * The first emission from this Observable should occur within 30s, else this plugin's status will fallback to + * `unavailable` until the first emission. + * * See the {@link StatusServiceSetup.derivedStatus$} API for leveraging the default status * calculation that is provided by Core. */ diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index a9b2dd6aefdda..b520ab3070b15 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -69,7 +69,6 @@ kibana_vars=( logging.appenders logging.appenders.console logging.appenders.file - logging.appenders.rolling-file logging.dest logging.json logging.loggers @@ -204,8 +203,8 @@ kibana_vars=( xpack.actions.proxyUrl xpack.actions.rejectUnauthorized xpack.actions.responseTimeout - xpack.actions.tls.proxyVerificationMode - xpack.actions.tls.verificationMode + xpack.actions.ssl.proxyVerificationMode + xpack.actions.ssl.verificationMode xpack.alerting.healthCheck.interval xpack.alerting.invalidateApiKeysTask.interval xpack.alerting.invalidateApiKeysTask.removalDelay @@ -380,7 +379,8 @@ kibana_vars=( xpack.task_manager.monitored_aggregated_stats_refresh_rate xpack.task_manager.monitored_stats_required_freshness xpack.task_manager.monitored_stats_running_average_window - xpack.task_manager.monitored_stats_warn_delayed_task_start_in_seconds + xpack.task_manager.monitored_stats_health_verbose_log.enabled + xpack.task_manager.monitored_stats_health_verbose_log.warn_delayed_task_start_in_seconds xpack.task_manager.monitored_task_execution_thresholds xpack.task_manager.poll_interval xpack.task_manager.request_capacity diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts index f372cf052d368..2c54bb8dba179 100644 --- a/src/dev/typescript/projects.ts +++ b/src/dev/typescript/projects.ts @@ -58,6 +58,9 @@ export const PROJECTS = [ ...glob .sync('test/interpreter_functional/plugins/*/tsconfig.json', { cwd: REPO_ROOT }) .map((path) => new Project(resolve(REPO_ROOT, path))), + ...glob + .sync('test/server_integration/__fixtures__/plugins/*/tsconfig.json', { cwd: REPO_ROOT }) + .map((path) => new Project(resolve(REPO_ROOT, path))), ]; export function filterProjectsByFlag(projectFlag?: string) { diff --git a/src/plugins/console/public/application/components/welcome_panel.tsx b/src/plugins/console/public/application/components/welcome_panel.tsx index eb746e313d228..8514d41c04a51 100644 --- a/src/plugins/console/public/application/components/welcome_panel.tsx +++ b/src/plugins/console/public/application/components/welcome_panel.tsx @@ -27,7 +27,7 @@ interface Props { export function WelcomePanel(props: Props) { return ( - +

    diff --git a/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap index 9f56740fdac22..afe339f3f43a2 100644 --- a/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap +++ b/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap @@ -603,7 +603,7 @@ exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = ` } > -
    -
    +
    @@ -950,7 +950,7 @@ exports[`DashboardEmptyScreen renders correctly with view mode 1`] = ` } > -
    -
    +
    diff --git a/src/plugins/data/common/es_query/es_query/build_es_query.ts b/src/plugins/data/common/es_query/es_query/build_es_query.ts index 45724796c3518..d7b3c630d1a6e 100644 --- a/src/plugins/data/common/es_query/es_query/build_es_query.ts +++ b/src/plugins/data/common/es_query/es_query/build_es_query.ts @@ -10,9 +10,9 @@ import { groupBy, has, isEqual } from 'lodash'; import { buildQueryFromKuery } from './from_kuery'; import { buildQueryFromFilters } from './from_filters'; import { buildQueryFromLucene } from './from_lucene'; -import { IIndexPattern } from '../../index_patterns'; import { Filter } from '../filters'; import { Query } from '../../query/types'; +import { IndexPatternBase } from './types'; export interface EsQueryConfig { allowLeadingWildcards: boolean; @@ -36,7 +36,7 @@ function removeMatchAll(filters: T[]) { * config contains dateformat:tz */ export function buildEsQuery( - indexPattern: IIndexPattern | undefined, + indexPattern: IndexPatternBase | undefined, queries: Query | Query[], filters: Filter | Filter[], config: EsQueryConfig = { diff --git a/src/plugins/data/common/es_query/es_query/filter_matches_index.ts b/src/plugins/data/common/es_query/es_query/filter_matches_index.ts index 478263d5ce601..b376436756092 100644 --- a/src/plugins/data/common/es_query/es_query/filter_matches_index.ts +++ b/src/plugins/data/common/es_query/es_query/filter_matches_index.ts @@ -6,15 +6,16 @@ * Side Public License, v 1. */ -import { IIndexPattern, IFieldType } from '../../index_patterns'; +import { IFieldType } from '../../index_patterns'; import { Filter } from '../filters'; +import { IndexPatternBase } from './types'; /* * TODO: We should base this on something better than `filter.meta.key`. We should probably modify * this to check if `filter.meta.index` matches `indexPattern.id` instead, but that's a breaking * change. */ -export function filterMatchesIndex(filter: Filter, indexPattern?: IIndexPattern | null) { +export function filterMatchesIndex(filter: Filter, indexPattern?: IndexPatternBase | null) { if (!filter.meta?.key || !indexPattern) { return true; } diff --git a/src/plugins/data/common/es_query/es_query/from_filters.ts b/src/plugins/data/common/es_query/es_query/from_filters.ts index e50862235af1d..7b3c58d45a569 100644 --- a/src/plugins/data/common/es_query/es_query/from_filters.ts +++ b/src/plugins/data/common/es_query/es_query/from_filters.ts @@ -10,7 +10,7 @@ import { isUndefined } from 'lodash'; import { migrateFilter } from './migrate_filter'; import { filterMatchesIndex } from './filter_matches_index'; import { Filter, cleanFilter, isFilterDisabled } from '../filters'; -import { IIndexPattern } from '../../index_patterns'; +import { IndexPatternBase } from './types'; import { handleNestedFilter } from './handle_nested_filter'; /** @@ -45,7 +45,7 @@ const translateToQuery = (filter: Filter) => { export const buildQueryFromFilters = ( filters: Filter[] = [], - indexPattern: IIndexPattern | undefined, + indexPattern: IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex: boolean = false ) => { filters = filters.filter((filter) => filter && !isFilterDisabled(filter)); diff --git a/src/plugins/data/common/es_query/es_query/from_kuery.ts b/src/plugins/data/common/es_query/es_query/from_kuery.ts index afedaae45872b..3eccfd8776113 100644 --- a/src/plugins/data/common/es_query/es_query/from_kuery.ts +++ b/src/plugins/data/common/es_query/es_query/from_kuery.ts @@ -7,11 +7,11 @@ */ import { fromKueryExpression, toElasticsearchQuery, nodeTypes, KueryNode } from '../kuery'; -import { IIndexPattern } from '../../index_patterns'; +import { IndexPatternBase } from './types'; import { Query } from '../../query/types'; export function buildQueryFromKuery( - indexPattern: IIndexPattern | undefined, + indexPattern: IndexPatternBase | undefined, queries: Query[] = [], allowLeadingWildcards: boolean = false, dateFormatTZ?: string @@ -24,7 +24,7 @@ export function buildQueryFromKuery( } function buildQuery( - indexPattern: IIndexPattern | undefined, + indexPattern: IndexPatternBase | undefined, queryASTs: KueryNode[], config: Record = {} ) { diff --git a/src/plugins/data/common/es_query/es_query/handle_nested_filter.test.ts b/src/plugins/data/common/es_query/es_query/handle_nested_filter.test.ts index ee5305132042a..d312d034df564 100644 --- a/src/plugins/data/common/es_query/es_query/handle_nested_filter.test.ts +++ b/src/plugins/data/common/es_query/es_query/handle_nested_filter.test.ts @@ -9,13 +9,14 @@ import { handleNestedFilter } from './handle_nested_filter'; import { fields } from '../../index_patterns/mocks'; import { buildPhraseFilter, buildQueryFilter } from '../filters'; -import { IFieldType, IIndexPattern } from '../../index_patterns'; +import { IndexPatternBase } from './types'; +import { IFieldType } from '../../index_patterns'; describe('handleNestedFilter', function () { - const indexPattern: IIndexPattern = ({ + const indexPattern: IndexPatternBase = { id: 'logstash-*', fields, - } as unknown) as IIndexPattern; + }; it("should return the filter's query wrapped in nested query if the target field is nested", () => { const field = getField('nestedField.child'); diff --git a/src/plugins/data/common/es_query/es_query/handle_nested_filter.ts b/src/plugins/data/common/es_query/es_query/handle_nested_filter.ts index 93927d81565ef..60e92769503fb 100644 --- a/src/plugins/data/common/es_query/es_query/handle_nested_filter.ts +++ b/src/plugins/data/common/es_query/es_query/handle_nested_filter.ts @@ -7,9 +7,9 @@ */ import { getFilterField, cleanFilter, Filter } from '../filters'; -import { IIndexPattern } from '../../index_patterns'; +import { IndexPatternBase } from './types'; -export const handleNestedFilter = (filter: Filter, indexPattern?: IIndexPattern) => { +export const handleNestedFilter = (filter: Filter, indexPattern?: IndexPatternBase) => { if (!indexPattern) return filter; const fieldName = getFilterField(filter); diff --git a/src/plugins/data/common/es_query/es_query/index.ts b/src/plugins/data/common/es_query/es_query/index.ts index 31529480c8ac9..c10ea5846ae3f 100644 --- a/src/plugins/data/common/es_query/es_query/index.ts +++ b/src/plugins/data/common/es_query/es_query/index.ts @@ -11,3 +11,4 @@ export { buildQueryFromFilters } from './from_filters'; export { luceneStringToDsl } from './lucene_string_to_dsl'; export { decorateQuery } from './decorate_query'; export { getEsQueryConfig } from './get_es_query_config'; +export { IndexPatternBase } from './types'; diff --git a/src/plugins/data/common/es_query/es_query/migrate_filter.ts b/src/plugins/data/common/es_query/es_query/migrate_filter.ts index c7c44d019a31c..9bd78b092fc18 100644 --- a/src/plugins/data/common/es_query/es_query/migrate_filter.ts +++ b/src/plugins/data/common/es_query/es_query/migrate_filter.ts @@ -9,7 +9,7 @@ import { get, omit } from 'lodash'; import { getConvertedValueForField } from '../filters'; import { Filter } from '../filters'; -import { IIndexPattern } from '../../index_patterns'; +import { IndexPatternBase } from './types'; export interface DeprecatedMatchPhraseFilter extends Filter { query: { @@ -28,7 +28,7 @@ function isDeprecatedMatchPhraseFilter(filter: any): filter is DeprecatedMatchPh return Boolean(fieldName && get(filter, ['query', 'match', fieldName, 'type']) === 'phrase'); } -export function migrateFilter(filter: Filter, indexPattern?: IIndexPattern) { +export function migrateFilter(filter: Filter, indexPattern?: IndexPatternBase) { if (isDeprecatedMatchPhraseFilter(filter)) { const fieldName = Object.keys(filter.query.match)[0]; const params: Record = get(filter, ['query', 'match', fieldName]); diff --git a/src/plugins/data/common/es_query/es_query/types.ts b/src/plugins/data/common/es_query/es_query/types.ts new file mode 100644 index 0000000000000..2133736516049 --- /dev/null +++ b/src/plugins/data/common/es_query/es_query/types.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { IFieldType } from '../../index_patterns'; + +export interface IndexPatternBase { + fields: IFieldType[]; + id?: string; +} diff --git a/src/plugins/data/common/es_query/filters/build_filters.ts b/src/plugins/data/common/es_query/filters/build_filters.ts index ba1bd0a615493..369f9530fb92b 100644 --- a/src/plugins/data/common/es_query/filters/build_filters.ts +++ b/src/plugins/data/common/es_query/filters/build_filters.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IIndexPattern, IFieldType } from '../..'; +import { IFieldType, IndexPatternBase } from '../..'; import { Filter, FILTERS, @@ -19,7 +19,7 @@ import { } from '.'; export function buildFilter( - indexPattern: IIndexPattern, + indexPattern: IndexPatternBase, field: IFieldType, type: FILTERS, negate: boolean, @@ -59,7 +59,7 @@ export function buildCustomFilter( } function buildBaseFilter( - indexPattern: IIndexPattern, + indexPattern: IndexPatternBase, field: IFieldType, type: FILTERS, params: any diff --git a/src/plugins/data/common/es_query/filters/exists_filter.ts b/src/plugins/data/common/es_query/filters/exists_filter.ts index 441a6bcb924b7..4836950c3bb27 100644 --- a/src/plugins/data/common/es_query/filters/exists_filter.ts +++ b/src/plugins/data/common/es_query/filters/exists_filter.ts @@ -7,7 +7,8 @@ */ import { Filter, FilterMeta } from './meta_filter'; -import { IIndexPattern, IFieldType } from '../../index_patterns'; +import { IFieldType } from '../../index_patterns'; +import { IndexPatternBase } from '..'; export type ExistsFilterMeta = FilterMeta; @@ -26,7 +27,7 @@ export const getExistsFilterField = (filter: ExistsFilter) => { return filter.exists && filter.exists.field; }; -export const buildExistsFilter = (field: IFieldType, indexPattern: IIndexPattern) => { +export const buildExistsFilter = (field: IFieldType, indexPattern: IndexPatternBase) => { return { meta: { index: indexPattern.id, diff --git a/src/plugins/data/common/es_query/filters/index.ts b/src/plugins/data/common/es_query/filters/index.ts index 133f5cd232e6f..fe7cdadabaee3 100644 --- a/src/plugins/data/common/es_query/filters/index.ts +++ b/src/plugins/data/common/es_query/filters/index.ts @@ -14,10 +14,8 @@ export * from './custom_filter'; export * from './exists_filter'; export * from './geo_bounding_box_filter'; export * from './geo_polygon_filter'; -export * from './get_display_value'; export * from './get_filter_field'; export * from './get_filter_params'; -export * from './get_index_pattern_from_filter'; export * from './match_all_filter'; export * from './meta_filter'; export * from './missing_filter'; diff --git a/src/plugins/data/common/es_query/filters/phrase_filter.ts b/src/plugins/data/common/es_query/filters/phrase_filter.ts index 85562435e68d0..27c1e85562097 100644 --- a/src/plugins/data/common/es_query/filters/phrase_filter.ts +++ b/src/plugins/data/common/es_query/filters/phrase_filter.ts @@ -8,7 +8,8 @@ import type { estypes } from '@elastic/elasticsearch'; import { get, isPlainObject } from 'lodash'; import { Filter, FilterMeta } from './meta_filter'; -import { IIndexPattern, IFieldType } from '../../index_patterns'; +import { IFieldType } from '../../index_patterns'; +import { IndexPatternBase } from '..'; export type PhraseFilterMeta = FilterMeta & { params?: { @@ -60,7 +61,7 @@ export const getPhraseFilterValue = (filter: PhraseFilter): PhraseFilterValue => export const buildPhraseFilter = ( field: IFieldType, value: any, - indexPattern: IIndexPattern + indexPattern: IndexPatternBase ): PhraseFilter => { const convertedValue = getConvertedValueForField(field, value); diff --git a/src/plugins/data/common/es_query/filters/phrases_filter.ts b/src/plugins/data/common/es_query/filters/phrases_filter.ts index 849c1b3faef2a..8a79472154493 100644 --- a/src/plugins/data/common/es_query/filters/phrases_filter.ts +++ b/src/plugins/data/common/es_query/filters/phrases_filter.ts @@ -9,7 +9,8 @@ import { Filter, FilterMeta } from './meta_filter'; import { getPhraseScript } from './phrase_filter'; import { FILTERS } from './index'; -import { IIndexPattern, IFieldType } from '../../index_patterns'; +import { IFieldType } from '../../index_patterns'; +import { IndexPatternBase } from '../es_query'; export type PhrasesFilterMeta = FilterMeta & { params: string[]; // The unformatted values @@ -34,7 +35,7 @@ export const getPhrasesFilterField = (filter: PhrasesFilter) => { export const buildPhrasesFilter = ( field: IFieldType, params: any[], - indexPattern: IIndexPattern + indexPattern: IndexPatternBase ) => { const index = indexPattern.id; const type = FILTERS.PHRASES; diff --git a/src/plugins/data/common/es_query/filters/range_filter.ts b/src/plugins/data/common/es_query/filters/range_filter.ts index a082b93c0a79a..7bc7a8cff7487 100644 --- a/src/plugins/data/common/es_query/filters/range_filter.ts +++ b/src/plugins/data/common/es_query/filters/range_filter.ts @@ -8,7 +8,8 @@ import type { estypes } from '@elastic/elasticsearch'; import { map, reduce, mapValues, get, keys, pickBy } from 'lodash'; import { Filter, FilterMeta } from './meta_filter'; -import { IIndexPattern, IFieldType } from '../../index_patterns'; +import { IFieldType } from '../../index_patterns'; +import { IndexPatternBase } from '..'; const OPERANDS_IN_RANGE = 2; @@ -93,7 +94,7 @@ const format = (field: IFieldType, value: any) => export const buildRangeFilter = ( field: IFieldType, params: RangeFilterParams, - indexPattern: IIndexPattern, + indexPattern: IndexPatternBase, formattedValue?: string ): RangeFilter => { const filter: any = { meta: { index: indexPattern.id, params: {} } }; diff --git a/src/plugins/data/common/es_query/kuery/ast/ast.ts b/src/plugins/data/common/es_query/kuery/ast/ast.ts index be82128969968..3e7b25897cab7 100644 --- a/src/plugins/data/common/es_query/kuery/ast/ast.ts +++ b/src/plugins/data/common/es_query/kuery/ast/ast.ts @@ -10,10 +10,10 @@ import { JsonObject } from '@kbn/common-utils'; import { nodeTypes } from '../node_types/index'; import { KQLSyntaxError } from '../kuery_syntax_error'; import { KueryNode, DslQuery, KueryParseOptions } from '../types'; -import { IIndexPattern } from '../../../index_patterns/types'; // @ts-ignore import { parse as parseKuery } from './_generated_/kuery'; +import { IndexPatternBase } from '../..'; const fromExpression = ( expression: string | DslQuery, @@ -65,7 +65,7 @@ export const fromKueryExpression = ( */ export const toElasticsearchQuery = ( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config?: Record, context?: Record ): JsonObject => { diff --git a/src/plugins/data/common/es_query/kuery/functions/and.ts b/src/plugins/data/common/es_query/kuery/functions/and.ts index 1989704cb627e..ba7d5d1f6645b 100644 --- a/src/plugins/data/common/es_query/kuery/functions/and.ts +++ b/src/plugins/data/common/es_query/kuery/functions/and.ts @@ -7,7 +7,7 @@ */ import * as ast from '../ast'; -import { IIndexPattern, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../../..'; export function buildNodeParams(children: KueryNode[]) { return { @@ -17,7 +17,7 @@ export function buildNodeParams(children: KueryNode[]) { export function toElasticsearchQuery( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config: Record = {}, context: Record = {} ) { diff --git a/src/plugins/data/common/es_query/kuery/functions/exists.ts b/src/plugins/data/common/es_query/kuery/functions/exists.ts index 5238fb1d8ee7f..fa6c37e6ba18f 100644 --- a/src/plugins/data/common/es_query/kuery/functions/exists.ts +++ b/src/plugins/data/common/es_query/kuery/functions/exists.ts @@ -8,7 +8,7 @@ import { get } from 'lodash'; import * as literal from '../node_types/literal'; -import { IIndexPattern, KueryNode, IFieldType } from '../../..'; +import { KueryNode, IFieldType, IndexPatternBase } from '../../..'; export function buildNodeParams(fieldName: string) { return { @@ -18,7 +18,7 @@ export function buildNodeParams(fieldName: string) { export function toElasticsearchQuery( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config: Record = {}, context: Record = {} ) { diff --git a/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.ts b/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.ts index f2498f3ea2ad4..38a433b1b80ab 100644 --- a/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.ts +++ b/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.ts @@ -9,7 +9,7 @@ import _ from 'lodash'; import { nodeTypes } from '../node_types'; import * as ast from '../ast'; -import { IIndexPattern, KueryNode, IFieldType, LatLon } from '../../..'; +import { IndexPatternBase, KueryNode, IFieldType, LatLon } from '../../..'; export function buildNodeParams(fieldName: string, params: any) { params = _.pick(params, 'topLeft', 'bottomRight'); @@ -26,7 +26,7 @@ export function buildNodeParams(fieldName: string, params: any) { export function toElasticsearchQuery( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config: Record = {}, context: Record = {} ) { diff --git a/src/plugins/data/common/es_query/kuery/functions/geo_polygon.ts b/src/plugins/data/common/es_query/kuery/functions/geo_polygon.ts index 584a315930d9c..69de7248a7b38 100644 --- a/src/plugins/data/common/es_query/kuery/functions/geo_polygon.ts +++ b/src/plugins/data/common/es_query/kuery/functions/geo_polygon.ts @@ -8,7 +8,7 @@ import { nodeTypes } from '../node_types'; import * as ast from '../ast'; -import { IIndexPattern, KueryNode, IFieldType, LatLon } from '../../..'; +import { IndexPatternBase, KueryNode, IFieldType, LatLon } from '../../..'; import { LiteralTypeBuildNode } from '../node_types/types'; export function buildNodeParams(fieldName: string, points: LatLon[]) { @@ -25,7 +25,7 @@ export function buildNodeParams(fieldName: string, points: LatLon[]) { export function toElasticsearchQuery( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config: Record = {}, context: Record = {} ) { diff --git a/src/plugins/data/common/es_query/kuery/functions/is.ts b/src/plugins/data/common/es_query/kuery/functions/is.ts index a18ad230c3cae..55d036c2156f9 100644 --- a/src/plugins/data/common/es_query/kuery/functions/is.ts +++ b/src/plugins/data/common/es_query/kuery/functions/is.ts @@ -11,7 +11,7 @@ import { getPhraseScript } from '../../filters'; import { getFields } from './utils/get_fields'; import { getTimeZoneFromSettings } from '../../utils'; import { getFullFieldNameNode } from './utils/get_full_field_name_node'; -import { IIndexPattern, KueryNode, IFieldType } from '../../..'; +import { IndexPatternBase, KueryNode, IFieldType } from '../../..'; import * as ast from '../ast'; @@ -39,7 +39,7 @@ export function buildNodeParams(fieldName: string, value: any, isPhrase: boolean export function toElasticsearchQuery( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config: Record = {}, context: Record = {} ) { diff --git a/src/plugins/data/common/es_query/kuery/functions/nested.ts b/src/plugins/data/common/es_query/kuery/functions/nested.ts index bfd01ef39764c..46ceeaf3e5de6 100644 --- a/src/plugins/data/common/es_query/kuery/functions/nested.ts +++ b/src/plugins/data/common/es_query/kuery/functions/nested.ts @@ -8,7 +8,7 @@ import * as ast from '../ast'; import * as literal from '../node_types/literal'; -import { IIndexPattern, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../../..'; export function buildNodeParams(path: any, child: any) { const pathNode = @@ -20,7 +20,7 @@ export function buildNodeParams(path: any, child: any) { export function toElasticsearchQuery( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config: Record = {}, context: Record = {} ) { diff --git a/src/plugins/data/common/es_query/kuery/functions/not.ts b/src/plugins/data/common/es_query/kuery/functions/not.ts index ef4456897bcdd..f837cd261c814 100644 --- a/src/plugins/data/common/es_query/kuery/functions/not.ts +++ b/src/plugins/data/common/es_query/kuery/functions/not.ts @@ -7,7 +7,7 @@ */ import * as ast from '../ast'; -import { IIndexPattern, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../../..'; export function buildNodeParams(child: KueryNode) { return { @@ -17,7 +17,7 @@ export function buildNodeParams(child: KueryNode) { export function toElasticsearchQuery( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config: Record = {}, context: Record = {} ) { diff --git a/src/plugins/data/common/es_query/kuery/functions/or.ts b/src/plugins/data/common/es_query/kuery/functions/or.ts index 416687e7cde9c..7365cc39595e6 100644 --- a/src/plugins/data/common/es_query/kuery/functions/or.ts +++ b/src/plugins/data/common/es_query/kuery/functions/or.ts @@ -7,7 +7,7 @@ */ import * as ast from '../ast'; -import { IIndexPattern, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../../..'; export function buildNodeParams(children: KueryNode[]) { return { @@ -17,7 +17,7 @@ export function buildNodeParams(children: KueryNode[]) { export function toElasticsearchQuery( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config: Record = {}, context: Record = {} ) { diff --git a/src/plugins/data/common/es_query/kuery/functions/range.ts b/src/plugins/data/common/es_query/kuery/functions/range.ts index 06b345e5821c3..caefa7e5373ca 100644 --- a/src/plugins/data/common/es_query/kuery/functions/range.ts +++ b/src/plugins/data/common/es_query/kuery/functions/range.ts @@ -13,7 +13,7 @@ import { getRangeScript, RangeFilterParams } from '../../filters'; import { getFields } from './utils/get_fields'; import { getTimeZoneFromSettings } from '../../utils'; import { getFullFieldNameNode } from './utils/get_full_field_name_node'; -import { IIndexPattern, KueryNode, IFieldType } from '../../..'; +import { IndexPatternBase, KueryNode, IFieldType } from '../../..'; export function buildNodeParams(fieldName: string, params: RangeFilterParams) { const paramsToMap = _.pick(params, 'gt', 'lt', 'gte', 'lte', 'format'); @@ -33,7 +33,7 @@ export function buildNodeParams(fieldName: string, params: RangeFilterParams) { export function toElasticsearchQuery( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config: Record = {}, context: Record = {} ) { diff --git a/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.ts b/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.ts index 4002a36648f04..7dac1262d5062 100644 --- a/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.ts +++ b/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.ts @@ -8,10 +8,10 @@ import * as literal from '../../node_types/literal'; import * as wildcard from '../../node_types/wildcard'; -import { KueryNode, IIndexPattern } from '../../../..'; +import { KueryNode, IndexPatternBase } from '../../../..'; import { LiteralTypeBuildNode } from '../../node_types/types'; -export function getFields(node: KueryNode, indexPattern?: IIndexPattern) { +export function getFields(node: KueryNode, indexPattern?: IndexPatternBase) { if (!indexPattern) return []; if (node.type === 'literal') { const fieldName = literal.toElasticsearchQuery(node as LiteralTypeBuildNode); diff --git a/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.ts b/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.ts index e623579226861..644791637aa70 100644 --- a/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.ts +++ b/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.ts @@ -7,11 +7,11 @@ */ import { getFields } from './get_fields'; -import { IIndexPattern, IFieldType, KueryNode } from '../../../..'; +import { IndexPatternBase, IFieldType, KueryNode } from '../../../..'; export function getFullFieldNameNode( rootNameNode: any, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, nestedPath?: string ): KueryNode { const fullFieldNameNode = { diff --git a/src/plugins/data/common/es_query/kuery/node_types/function.ts b/src/plugins/data/common/es_query/kuery/node_types/function.ts index b9b7379dfb23d..642089a101f31 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/function.ts +++ b/src/plugins/data/common/es_query/kuery/node_types/function.ts @@ -9,7 +9,7 @@ import _ from 'lodash'; import { functions } from '../functions'; -import { IIndexPattern, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../../..'; import { FunctionName, FunctionTypeBuildNode } from './types'; export function buildNode(functionName: FunctionName, ...args: any[]) { @@ -45,7 +45,7 @@ export function buildNodeWithArgumentNodes( export function toElasticsearchQuery( node: KueryNode, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config?: Record, context?: Record ) { diff --git a/src/plugins/data/common/es_query/kuery/node_types/types.ts b/src/plugins/data/common/es_query/kuery/node_types/types.ts index b3247a0ad8dc2..ea8eb5e8a0618 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/types.ts +++ b/src/plugins/data/common/es_query/kuery/node_types/types.ts @@ -11,8 +11,8 @@ */ import { JsonValue } from '@kbn/common-utils'; -import { IIndexPattern } from '../../../index_patterns'; import { KueryNode } from '..'; +import { IndexPatternBase } from '../..'; export type FunctionName = | 'is' @@ -30,7 +30,7 @@ interface FunctionType { buildNodeWithArgumentNodes: (functionName: FunctionName, args: any[]) => FunctionTypeBuildNode; toElasticsearchQuery: ( node: any, - indexPattern?: IIndexPattern, + indexPattern?: IndexPatternBase, config?: Record, context?: Record ) => JsonValue; diff --git a/src/plugins/data/common/field_formats/converters/string.ts b/src/plugins/data/common/field_formats/converters/string.ts index ec92d75910522..64367df5d90dd 100644 --- a/src/plugins/data/common/field_formats/converters/string.ts +++ b/src/plugins/data/common/field_formats/converters/string.ts @@ -13,6 +13,10 @@ import { FieldFormat } from '../field_format'; import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; import { shortenDottedString } from '../../utils'; +export const emptyLabel = i18n.translate('data.fieldFormats.string.emptyLabel', { + defaultMessage: '(empty)', +}); + const TRANSFORM_OPTIONS = [ { kind: false, @@ -103,6 +107,9 @@ export class StringFormat extends FieldFormat { } textConvert: TextContextTypeConvert = (val) => { + if (val === '') { + return emptyLabel; + } switch (this.param('transform')) { case 'lower': return String(val).toLowerCase(); diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 07aa8967b905e..a88f029c0c7cd 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -9,6 +9,7 @@ import type { estypes } from '@elastic/elasticsearch'; import { ToastInputFields, ErrorToastOptions } from 'src/core/public/notifications'; // eslint-disable-next-line import type { SavedObject } from 'src/core/server'; +import type { IndexPatternBase } from '../es_query'; import { IFieldType } from './fields'; import { RUNTIME_FIELD_TYPES } from './constants'; import { SerializedFieldFormat } from '../../../expressions/common'; @@ -29,10 +30,8 @@ export interface RuntimeField { * IIndexPattern allows for an IndexPattern OR an index pattern saved object * Use IndexPattern or IndexPatternSpec instead */ -export interface IIndexPattern { - fields: IFieldType[]; +export interface IIndexPattern extends IndexPatternBase { title: string; - id?: string; /** * Type is used for identifying rollup indices, otherwise left undefined */ diff --git a/src/plugins/data/config.ts b/src/plugins/data/config.ts index 9306b64019bbc..1b7bfbc09ad16 100644 --- a/src/plugins/data/config.ts +++ b/src/plugins/data/config.ts @@ -44,10 +44,20 @@ export const searchSessionsConfigSchema = schema.object({ */ pageSize: schema.number({ defaultValue: 100 }), /** - * trackingInterval controls how often we track search session objects progress + * trackingInterval controls how often we track persisted search session objects progress */ trackingInterval: schema.duration({ defaultValue: '10s' }), + /** + * cleanupInterval controls how often we track non-persisted search session objects for cleanup + */ + cleanupInterval: schema.duration({ defaultValue: '60s' }), + + /** + * expireInterval controls how often we track persisted search session objects for expiration + */ + expireInterval: schema.duration({ defaultValue: '60m' }), + /** * monitoringTaskTimeout controls for how long task manager waits for search session monitoring task to complete before considering it timed out, * If tasks timeouts it receives cancel signal and next task starts in "trackingInterval" time diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 078dd3a9b7c5a..d7667f20d517e 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -23,7 +23,6 @@ import { disableFilter, FILTERS, FilterStateStore, - getDisplayValueFromFilter, getPhraseFilterField, getPhraseFilterValue, isExistsFilter, @@ -43,6 +42,7 @@ import { FilterLabel } from './ui'; import { FilterItem } from './ui/filter_bar'; import { + getDisplayValueFromFilter, generateFilters, onlyDisabledFiltersChanged, changeTimeFilter, diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 7a5f323e51459..2849b93b14483 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -808,11 +808,11 @@ export const esFilters: { FILTERS: typeof FILTERS; FilterStateStore: typeof FilterStateStore; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("../common").Filter; - buildPhrasesFilter: (field: import("../common").IFieldType, params: any[], indexPattern: import("../common").IIndexPattern) => import("../common").PhrasesFilter; - buildExistsFilter: (field: import("../common").IFieldType, indexPattern: import("../common").IIndexPattern) => import("../common").ExistsFilter; - buildPhraseFilter: (field: import("../common").IFieldType, value: any, indexPattern: import("../common").IIndexPattern) => import("../common").PhraseFilter; + buildPhrasesFilter: (field: import("../common").IFieldType, params: any[], indexPattern: import("../common").IndexPatternBase) => import("../common").PhrasesFilter; + buildExistsFilter: (field: import("../common").IFieldType, indexPattern: import("../common").IndexPatternBase) => import("../common").ExistsFilter; + buildPhraseFilter: (field: import("../common").IFieldType, value: any, indexPattern: import("../common").IndexPatternBase) => import("../common").PhraseFilter; buildQueryFilter: (query: any, index: string, alias: string) => import("../common").QueryStringFilter; - buildRangeFilter: (field: import("../common").IFieldType, params: import("../common").RangeFilterParams, indexPattern: import("../common").IIndexPattern, formattedValue?: string | undefined) => import("../common").RangeFilter; + buildRangeFilter: (field: import("../common").IFieldType, params: import("../common").RangeFilterParams, indexPattern: import("../common").IndexPatternBase, formattedValue?: string | undefined) => import("../common").RangeFilter; isPhraseFilter: (filter: any) => filter is import("../common").PhraseFilter; isExistsFilter: (filter: any) => filter is import("../common").ExistsFilter; isPhrasesFilter: (filter: any) => filter is import("../common").PhrasesFilter; @@ -858,7 +858,7 @@ export const esFilters: { export const esKuery: { nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; + toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IndexPatternBase | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; }; // Warning: (ae-missing-release-tag) "esQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -867,7 +867,7 @@ export const esKuery: { export const esQuery: { buildEsQuery: typeof buildEsQuery; getEsQueryConfig: typeof getEsQueryConfig; - buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").IIndexPattern | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { + buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { must: never[]; filter: import("../common").Filter[]; should: never[]; @@ -1286,22 +1286,19 @@ export interface IFieldType { visualizable?: boolean; } +// Warning: (ae-forgotten-export) The symbol "IndexPatternBase" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IIndexPattern" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @deprecated (undocumented) -export interface IIndexPattern { +export interface IIndexPattern extends IndexPatternBase { // Warning: (ae-forgotten-export) The symbol "SerializedFieldFormat" needs to be exported by the entry point index.d.ts // // (undocumented) fieldFormatMap?: Record | undefined>; - // (undocumented) - fields: IFieldType[]; getFormatterForField?: (field: IndexPatternField | IndexPatternField['spec'] | IFieldType) => FieldFormat; // (undocumented) getTimeField?(): IFieldType | undefined; // (undocumented) - id?: string; - // (undocumented) timeFieldName?: string; // (undocumented) title: string; @@ -2731,13 +2728,13 @@ export interface WaitUntilNextSessionCompletesOptions { // Warnings were encountered during analysis: // -// src/plugins/data/common/es_query/filters/exists_filter.ts:19:3 - (ae-forgotten-export) The symbol "ExistsFilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/exists_filter.ts:20:3 - (ae-forgotten-export) The symbol "FilterExistsProperty" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/exists_filter.ts:20:3 - (ae-forgotten-export) The symbol "ExistsFilterMeta" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/exists_filter.ts:21:3 - (ae-forgotten-export) The symbol "FilterExistsProperty" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/match_all_filter.ts:17:3 - (ae-forgotten-export) The symbol "MatchAllFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/meta_filter.ts:44:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/phrase_filter.ts:22:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/phrases_filter.ts:20:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/phrase_filter.ts:23:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/phrases_filter.ts:21:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:169:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/query/filter_manager/index.ts b/src/plugins/data/public/query/filter_manager/index.ts index 327b9763541ac..55dba640b07b6 100644 --- a/src/plugins/data/public/query/filter_manager/index.ts +++ b/src/plugins/data/public/query/filter_manager/index.ts @@ -11,3 +11,5 @@ export { FilterManager } from './filter_manager'; export { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; export { onlyDisabledFiltersChanged } from './lib/only_disabled'; export { generateFilters } from './lib/generate_filters'; +export { getDisplayValueFromFilter } from './lib/get_display_value'; +export { getIndexPatternFromFilter } from './lib/get_index_pattern_from_filter'; diff --git a/src/plugins/data/common/es_query/filters/get_display_value.ts b/src/plugins/data/public/query/filter_manager/lib/get_display_value.ts similarity index 95% rename from src/plugins/data/common/es_query/filters/get_display_value.ts rename to src/plugins/data/public/query/filter_manager/lib/get_display_value.ts index ee719843ae879..45c6167f600bc 100644 --- a/src/plugins/data/common/es_query/filters/get_display_value.ts +++ b/src/plugins/data/public/query/filter_manager/lib/get_display_value.ts @@ -7,9 +7,8 @@ */ import { i18n } from '@kbn/i18n'; -import { IIndexPattern } from '../..'; +import { Filter, IIndexPattern } from '../../../../common'; import { getIndexPatternFromFilter } from './get_index_pattern_from_filter'; -import { Filter } from '../filters'; function getValueFormatter(indexPattern?: IIndexPattern, key?: string) { // checking getFormatterForField exists because there is at least once case where an index pattern diff --git a/src/plugins/data/common/es_query/filters/get_index_pattern_from_filter.test.ts b/src/plugins/data/public/query/filter_manager/lib/get_index_pattern_from_filter.test.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/get_index_pattern_from_filter.test.ts rename to src/plugins/data/public/query/filter_manager/lib/get_index_pattern_from_filter.test.ts diff --git a/src/plugins/data/common/es_query/filters/get_index_pattern_from_filter.ts b/src/plugins/data/public/query/filter_manager/lib/get_index_pattern_from_filter.ts similarity index 88% rename from src/plugins/data/common/es_query/filters/get_index_pattern_from_filter.ts rename to src/plugins/data/public/query/filter_manager/lib/get_index_pattern_from_filter.ts index bceeb5f2793ec..7a2ce29102e51 100644 --- a/src/plugins/data/common/es_query/filters/get_index_pattern_from_filter.ts +++ b/src/plugins/data/public/query/filter_manager/lib/get_index_pattern_from_filter.ts @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -import { Filter } from '../filters'; -import { IIndexPattern } from '../..'; +import { Filter, IIndexPattern } from '../../../../common'; export function getIndexPatternFromFilter( filter: Filter, diff --git a/src/plugins/data/public/search/session/session_service.test.ts b/src/plugins/data/public/search/session/session_service.test.ts index 39680c4948366..7f388a29cd454 100644 --- a/src/plugins/data/public/search/session/session_service.test.ts +++ b/src/plugins/data/public/search/session/session_service.test.ts @@ -98,6 +98,14 @@ describe('Session service', () => { expect(nowProvider.reset).toHaveBeenCalled(); }); + it("Can clear other apps' session", async () => { + sessionService.start(); + expect(sessionService.getSessionId()).not.toBeUndefined(); + currentAppId$.next('change'); + sessionService.clear(); + expect(sessionService.getSessionId()).toBeUndefined(); + }); + it("Can start a new session in case there is other apps' stale session", async () => { const s1 = sessionService.start(); expect(sessionService.getSessionId()).not.toBeUndefined(); diff --git a/src/plugins/data/public/ui/apply_filters/apply_filter_popover_content.tsx b/src/plugins/data/public/ui/apply_filters/apply_filter_popover_content.tsx index 23de8327ce1f1..9cc9af04409f1 100644 --- a/src/plugins/data/public/ui/apply_filters/apply_filter_popover_content.tsx +++ b/src/plugins/data/public/ui/apply_filters/apply_filter_popover_content.tsx @@ -20,9 +20,9 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component } from 'react'; import { IIndexPattern } from '../..'; -import { getDisplayValueFromFilter, Filter } from '../../../common'; +import { Filter } from '../../../common'; import { FilterLabel } from '../filter_bar'; -import { mapAndFlattenFilters } from '../../query'; +import { mapAndFlattenFilters, getDisplayValueFromFilter } from '../../query'; interface Props { filters: Filter[]; diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx index 2b8978a125bca..734161ea87232 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx @@ -37,10 +37,10 @@ import { Operator } from './lib/filter_operators'; import { PhraseValueInput } from './phrase_value_input'; import { PhrasesValuesInput } from './phrases_values_input'; import { RangeValueInput } from './range_value_input'; +import { getIndexPatternFromFilter } from '../../../query'; import { IIndexPattern, IFieldType } from '../../..'; import { Filter, - getIndexPatternFromFilter, FieldFilter, buildFilter, buildCustomFilter, diff --git a/src/plugins/data/public/ui/filter_bar/filter_item.tsx b/src/plugins/data/public/ui/filter_bar/filter_item.tsx index 9e5090f945182..09e0571c2a870 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_item.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_item.tsx @@ -14,14 +14,13 @@ import { IUiSettingsClient } from 'src/core/public'; import { FilterEditor } from './filter_editor'; import { FilterView } from './filter_view'; import { IIndexPattern } from '../..'; +import { getDisplayValueFromFilter, getIndexPatternFromFilter } from '../../query'; import { Filter, isFilterPinned, - getDisplayValueFromFilter, toggleFilterNegated, toggleFilterPinned, toggleFilterDisabled, - getIndexPatternFromFilter, } from '../../../common'; import { getIndexPatterns } from '../../services'; diff --git a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap index a0a7e54d27532..0ab3f8a4e3466 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap +++ b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap @@ -176,27 +176,27 @@ exports[`Inspector Data View component should render empty state 1`] = `
    + +

    + + No data available + +

    +
    - -

    - - No data available - -

    -
    diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 768c44d3e3e95..5ca19f9e1e509 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -447,11 +447,11 @@ export const esFilters: { buildQueryFilter: (query: any, index: string, alias: string) => import("../common").QueryStringFilter; buildCustomFilter: typeof buildCustomFilter; buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("../common").Filter; - buildExistsFilter: (field: import("../common").IFieldType, indexPattern: import("../common").IIndexPattern) => import("../common").ExistsFilter; + buildExistsFilter: (field: import("../common").IFieldType, indexPattern: import("../common").IndexPatternBase) => import("../common").ExistsFilter; buildFilter: typeof buildFilter; - buildPhraseFilter: (field: import("../common").IFieldType, value: any, indexPattern: import("../common").IIndexPattern) => import("../common").PhraseFilter; - buildPhrasesFilter: (field: import("../common").IFieldType, params: any[], indexPattern: import("../common").IIndexPattern) => import("../common").PhrasesFilter; - buildRangeFilter: (field: import("../common").IFieldType, params: import("../common").RangeFilterParams, indexPattern: import("../common").IIndexPattern, formattedValue?: string | undefined) => import("../common").RangeFilter; + buildPhraseFilter: (field: import("../common").IFieldType, value: any, indexPattern: import("../common").IndexPatternBase) => import("../common").PhraseFilter; + buildPhrasesFilter: (field: import("../common").IFieldType, params: any[], indexPattern: import("../common").IndexPatternBase) => import("../common").PhrasesFilter; + buildRangeFilter: (field: import("../common").IFieldType, params: import("../common").RangeFilterParams, indexPattern: import("../common").IndexPatternBase, formattedValue?: string | undefined) => import("../common").RangeFilter; isFilterDisabled: (filter: import("../common").Filter) => boolean; }; @@ -461,14 +461,14 @@ export const esFilters: { export const esKuery: { nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; + toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IndexPatternBase | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; }; // Warning: (ae-missing-release-tag) "esQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export const esQuery: { - buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").IIndexPattern | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { + buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { must: never[]; filter: import("../common").Filter[]; should: never[]; diff --git a/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx b/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx index 210313aac5366..f1967d5b10b3e 100644 --- a/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx +++ b/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; import moment from 'moment'; import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiSpacer } from '@elastic/eui'; import { IUiSettingsClient } from 'kibana/public'; @@ -47,8 +47,21 @@ export function DiscoverChart({ stateContainer: GetStateReturn; timefield?: string; }) { + const chartRef = useRef<{ element: HTMLElement | null; moveFocus: boolean }>({ + element: null, + moveFocus: false, + }); + + useEffect(() => { + if (chartRef.current.moveFocus && chartRef.current.element) { + chartRef.current.element.focus(); + } + }, [state.hideChart]); + const toggleHideChart = useCallback(() => { - stateContainer.setAppState({ hideChart: !state.hideChart }); + const newHideChart = !state.hideChart; + stateContainer.setAppState({ hideChart: newHideChart }); + chartRef.current.moveFocus = !newHideChart; }, [state, stateContainer]); const onChangeInterval = useCallback( @@ -102,9 +115,7 @@ export function DiscoverChart({ { - toggleHideChart(); - }} + onClick={toggleHideChart} data-test-subj="discoverChartToggle" > {!state.hideChart @@ -122,6 +133,8 @@ export function DiscoverChart({ {!state.hideChart && chartData && (
    (chartRef.current.element = element)} + tabIndex={-1} aria-label={i18n.translate('discover.histogramOfFoundDocumentsAriaLabel', { defaultMessage: 'Histogram of found documents', })} diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/discover_field_search.tsx b/src/plugins/discover/public/application/apps/main/components/sidebar/discover_field_search.tsx index e11c1716efe6b..4abfa6ecea55a 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/discover_field_search.tsx +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/discover_field_search.tsx @@ -204,15 +204,21 @@ export function DiscoverFieldSearch({ onChange, value, types, useNewFieldsApi }: return [ { id: `${id}-any`, - label: 'any', + label: i18n.translate('discover.fieldChooser.filter.toggleButton.any', { + defaultMessage: 'any', + }), }, { id: `${id}-true`, - label: 'yes', + label: i18n.translate('discover.fieldChooser.filter.toggleButton.yes', { + defaultMessage: 'yes', + }), }, { id: `${id}-false`, - label: 'no', + label: i18n.translate('discover.fieldChooser.filter.toggleButton.no', { + defaultMessage: 'no', + }), }, ]; }; diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx index 965d3cb6a30c4..de3c55ad7a869 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx @@ -9,14 +9,21 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; import { findTestSubject } from '@elastic/eui/lib/test'; -import { FilterInBtn, FilterOutBtn } from './discover_grid_cell_actions'; +import { FilterInBtn, FilterOutBtn, buildCellActions } from './discover_grid_cell_actions'; import { DiscoverGridContext } from './discover_grid_context'; import { indexPatternMock } from '../../../__mocks__/index_pattern'; import { esHits } from '../../../__mocks__/es_hits'; import { EuiButton } from '@elastic/eui'; +import { IndexPatternField } from 'src/plugins/data/common'; describe('Discover cell actions ', function () { + it('should not show cell actions for unfilterable fields', async () => { + expect( + buildCellActions({ name: 'foo', filterable: false } as IndexPatternField) + ).toBeUndefined(); + }); + it('triggers filter function when FilterInBtn is clicked', async () => { const contextMock = { expanded: undefined, diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx index 4e9218f0881cd..ab80cd3e7b461 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx @@ -79,7 +79,7 @@ export const FilterOutBtn = ({ }; export function buildCellActions(field: IndexPatternField) { - if (!field.aggregatable && !field.searchable) { + if (!field.filterable) { return undefined; } diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.test.tsx index 60841799b1398..50be2473a441e 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.test.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.test.tsx @@ -144,7 +144,9 @@ describe('Discover flyout', function () { expect(props.setExpandedDoc.mock.calls[0][0]._id).toBe('4'); }); - it('allows navigating with arrow keys through documents', () => { + // EuiFlyout is mocked in Jest environments. + // EUI team to reinstate `onKeyDown`: https://github.com/elastic/eui/issues/4883 + it.skip('allows navigating with arrow keys through documents', () => { const props = getProps(); const component = mountWithIntl(); findTestSubject(component, 'docTableDetailsFlyout').simulate('keydown', { key: 'ArrowRight' }); diff --git a/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap b/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap index f40dbbbae1f87..68786871825ac 100644 --- a/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap +++ b/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap @@ -147,27 +147,27 @@ exports[`Source Viewer component renders error state 1`] = ` />
    + +

    + An Error Occurred +

    +
    - -

    - An Error Occurred -

    -
    diff --git a/src/plugins/home/common/instruction_variant.ts b/src/plugins/home/common/instruction_variant.ts index 310ee23460a08..f27b2c97bdc1e 100644 --- a/src/plugins/home/common/instruction_variant.ts +++ b/src/plugins/home/common/instruction_variant.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; + export const INSTRUCTION_VARIANT = { ESC: 'esc', OSX: 'osx', @@ -24,6 +26,7 @@ export const INSTRUCTION_VARIANT = { DOTNET: 'dotnet', LINUX: 'linux', PHP: 'php', + FLEET: 'fleet', }; const DISPLAY_MAP = { @@ -44,6 +47,9 @@ const DISPLAY_MAP = { [INSTRUCTION_VARIANT.DOTNET]: '.NET', [INSTRUCTION_VARIANT.LINUX]: 'Linux', [INSTRUCTION_VARIANT.PHP]: 'PHP', + [INSTRUCTION_VARIANT.FLEET]: i18n.translate('home.tutorial.instruction_variant.fleet', { + defaultMessage: 'Elastic APM (beta) in Fleet', + }), }; /** diff --git a/src/plugins/home/public/application/application.tsx b/src/plugins/home/public/application/application.tsx index 9ab720b47ab92..18f3089c14d11 100644 --- a/src/plugins/home/public/application/application.tsx +++ b/src/plugins/home/public/application/application.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { i18n } from '@kbn/i18n'; import { ScopedHistory, CoreStart } from 'kibana/public'; -import { KibanaContextProvider } from '../../../kibana_react/public'; +import { KibanaContextProvider, RedirectAppLinks } from '../../../kibana_react/public'; // @ts-ignore import { HomeApp } from './components/home_app'; import { getServices } from './kibana_services'; @@ -44,9 +44,11 @@ export const renderApp = async ( }); render( - - - , + + + + + , element ); diff --git a/src/plugins/home/public/application/components/tutorial/instruction.js b/src/plugins/home/public/application/components/tutorial/instruction.js index 42c22b057b1e2..373f8c318a504 100644 --- a/src/plugins/home/public/application/components/tutorial/instruction.js +++ b/src/plugins/home/public/application/components/tutorial/instruction.js @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { Suspense, useMemo } from 'react'; import PropTypes from 'prop-types'; import { Content } from './content'; @@ -17,11 +17,23 @@ import { EuiSpacer, EuiCopy, EuiButton, + EuiLoadingSpinner, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -export function Instruction({ commands, paramValues, textPost, textPre, replaceTemplateStrings }) { +import { getServices } from '../../kibana_services'; + +export function Instruction({ + commands, + paramValues, + textPost, + textPre, + replaceTemplateStrings, + customComponentName, +}) { + const { tutorialService, http, uiSettings, getBasePath } = getServices(); + let pre; if (textPre) { pre = ; @@ -36,6 +48,13 @@ export function Instruction({ commands, paramValues, textPost, textPre, replaceT
    ); } + const customComponent = tutorialService.getCustomComponent(customComponentName); + //Memoize the custom component so it wont rerender everytime + const LazyCustomComponent = useMemo(() => { + if (customComponent) { + return React.lazy(() => customComponent()); + } + }, [customComponent]); let copyButton; let commandBlock; @@ -79,6 +98,16 @@ export function Instruction({ commands, paramValues, textPost, textPre, replaceT {post} + {LazyCustomComponent && ( + }> + + + )} +
    ); @@ -90,4 +119,5 @@ Instruction.propTypes = { textPost: PropTypes.string, textPre: PropTypes.string, replaceTemplateStrings: PropTypes.func.isRequired, + customComponentName: PropTypes.string, }; diff --git a/src/plugins/home/public/application/components/tutorial/instruction_set.js b/src/plugins/home/public/application/components/tutorial/instruction_set.js index f16e276ed4c56..da368120d493c 100644 --- a/src/plugins/home/public/application/components/tutorial/instruction_set.js +++ b/src/plugins/home/public/application/components/tutorial/instruction_set.js @@ -186,6 +186,7 @@ class InstructionSetUi extends React.Component { textPre={instruction.textPre} textPost={instruction.textPost} replaceTemplateStrings={this.props.replaceTemplateStrings} + customComponentName={instruction.customComponentName} /> ); return { @@ -298,6 +299,7 @@ const statusCheckConfigShape = PropTypes.shape({ title: PropTypes.string, text: PropTypes.string, btnLabel: PropTypes.string, + customStatusCheck: PropTypes.string, }); InstructionSetUi.propTypes = { diff --git a/src/plugins/home/public/application/components/tutorial/tutorial.js b/src/plugins/home/public/application/components/tutorial/tutorial.js index 81a75d8881e2d..92bbb92fa0850 100644 --- a/src/plugins/home/public/application/components/tutorial/tutorial.js +++ b/src/plugins/home/public/application/components/tutorial/tutorial.js @@ -67,7 +67,6 @@ class TutorialUi extends React.Component { async componentDidMount() { const tutorial = await this.props.getTutorial(this.props.tutorialId); - if (!this._isMounted) { return; } @@ -172,15 +171,39 @@ class TutorialUi extends React.Component { const instructionSet = this.getInstructionSets()[instructionSetIndex]; const esHitsCheckConfig = _.get(instructionSet, `statusCheck.esHitsCheck`); - if (esHitsCheckConfig) { - const statusCheckState = await this.fetchEsHitsStatus(esHitsCheckConfig); + //Checks if a custom status check callback was registered in the CLIENT + //that matches the same name registered in the SERVER (customStatusCheckName) + const customStatusCheckCallback = getServices().tutorialService.getCustomStatusCheck( + this.state.tutorial.customStatusCheckName + ); - this.setState((prevState) => ({ - statusCheckStates: { - ...prevState.statusCheckStates, - [instructionSetIndex]: statusCheckState, - }, - })); + const [esHitsStatusCheck, customStatusCheck] = await Promise.all([ + ...(esHitsCheckConfig ? [this.fetchEsHitsStatus(esHitsCheckConfig)] : []), + ...(customStatusCheckCallback + ? [this.fetchCustomStatusCheck(customStatusCheckCallback)] + : []), + ]); + + const nextStatusCheckState = + esHitsStatusCheck === StatusCheckStates.HAS_DATA || + customStatusCheck === StatusCheckStates.HAS_DATA + ? StatusCheckStates.HAS_DATA + : StatusCheckStates.NO_DATA; + + this.setState((prevState) => ({ + statusCheckStates: { + ...prevState.statusCheckStates, + [instructionSetIndex]: nextStatusCheckState, + }, + })); + }; + + fetchCustomStatusCheck = async (customStatusCheckCallback) => { + try { + const response = await customStatusCheckCallback(); + return response ? StatusCheckStates.HAS_DATA : StatusCheckStates.NO_DATA; + } catch (e) { + return StatusCheckStates.ERROR; } }; diff --git a/src/plugins/home/public/application/components/tutorial/tutorial.test.js b/src/plugins/home/public/application/components/tutorial/tutorial.test.js index 490ecfd8edd78..e9c0b49451e23 100644 --- a/src/plugins/home/public/application/components/tutorial/tutorial.test.js +++ b/src/plugins/home/public/application/components/tutorial/tutorial.test.js @@ -13,12 +13,23 @@ import { Tutorial } from './tutorial'; jest.mock('../../kibana_services', () => ({ getServices: () => ({ + http: { + post: jest.fn().mockImplementation(async () => ({ count: 1 })), + }, getBasePath: jest.fn(() => 'path'), chrome: { setBreadcrumbs: () => {}, }, tutorialService: { getModuleNotices: () => [], + getCustomComponent: jest.fn(), + getCustomStatusCheck: (name) => { + const customStatusCheckMock = { + custom_status_check_has_data: async () => true, + custom_status_check_no_data: async () => false, + }; + return customStatusCheckMock[name]; + }, }, }), })); @@ -54,6 +65,7 @@ const tutorial = { elasticCloud: buildInstructionSet('elasticCloud'), onPrem: buildInstructionSet('onPrem'), onPremElasticCloud: buildInstructionSet('onPremElasticCloud'), + customStatusCheckName: 'custom_status_check_has_data', }; const loadTutorialPromise = Promise.resolve(tutorial); const getTutorial = () => { @@ -143,3 +155,104 @@ test('should render ELASTIC_CLOUD instructions when isCloudEnabled is true', asy component.update(); expect(component).toMatchSnapshot(); // eslint-disable-line }); + +describe('custom status check', () => { + test('should return has_data when custom status check callback is set and returns true', async () => { + const component = mountWithIntl( + {}} + /> + ); + await loadTutorialPromise; + component.update(); + await component.instance().checkInstructionSetStatus(0); + expect(component.state('statusCheckStates')[0]).toEqual('has_data'); + }); + test('should return no_data when custom status check callback is set and returns false', async () => { + const tutorialWithCustomStatusCheckNoData = { + ...tutorial, + customStatusCheckName: 'custom_status_check_no_data', + }; + const component = mountWithIntl( + tutorialWithCustomStatusCheckNoData} + replaceTemplateStrings={replaceTemplateStrings} + tutorialId={'my_testing_tutorial'} + bulkCreate={() => {}} + /> + ); + await loadTutorialPromise; + component.update(); + await component.instance().checkInstructionSetStatus(0); + expect(component.state('statusCheckStates')[0]).toEqual('NO_DATA'); + }); + + test('should return no_data when custom status check callback is not defined', async () => { + const tutorialWithoutCustomStatusCheck = { + ...tutorial, + customStatusCheckName: undefined, + }; + const component = mountWithIntl( + tutorialWithoutCustomStatusCheck} + replaceTemplateStrings={replaceTemplateStrings} + tutorialId={'my_testing_tutorial'} + bulkCreate={() => {}} + /> + ); + await loadTutorialPromise; + component.update(); + await component.instance().checkInstructionSetStatus(0); + expect(component.state('statusCheckStates')[0]).toEqual('NO_DATA'); + }); + + test('should return has_data if esHits or customStatusCheck returns true', async () => { + const { instructionSets } = tutorial.elasticCloud; + const tutorialWithStatusCheckAndCustomStatusCheck = { + ...tutorial, + customStatusCheckName: undefined, + elasticCloud: { + instructionSets: [ + { + ...instructionSets[0], + statusCheck: { + title: 'check status', + text: 'check status', + esHitsCheck: { + index: 'foo', + query: { + bool: { + filter: [{ term: { 'processor.event': 'onboarding' } }], + }, + }, + }, + }, + }, + ], + }, + }; + const component = mountWithIntl( + tutorialWithStatusCheckAndCustomStatusCheck} + replaceTemplateStrings={replaceTemplateStrings} + tutorialId={'my_testing_tutorial'} + bulkCreate={() => {}} + /> + ); + await loadTutorialPromise; + component.update(); + await component.instance().checkInstructionSetStatus(0); + expect(component.state('statusCheckStates')[0]).toEqual('has_data'); + }); +}); diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts b/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts index ac48168a360d4..0c109d61912ca 100644 --- a/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts +++ b/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts @@ -15,6 +15,8 @@ const createSetupMock = (): jest.Mocked => { registerDirectoryNotice: jest.fn(), registerDirectoryHeaderLink: jest.fn(), registerModuleNotice: jest.fn(), + registerCustomStatusCheck: jest.fn(), + registerCustomComponent: jest.fn(), }; return setup; }; @@ -26,6 +28,8 @@ const createMock = (): jest.Mocked> => { getDirectoryNotices: jest.fn(() => []), getDirectoryHeaderLinks: jest.fn(() => []), getModuleNotices: jest.fn(() => []), + getCustomStatusCheck: jest.fn(), + getCustomComponent: jest.fn(), }; service.setup.mockImplementation(createSetupMock); return service; diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx b/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx index 69d24b66ec6bf..a88cf526e3716 100644 --- a/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx +++ b/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx @@ -138,4 +138,44 @@ describe('TutorialService', () => { expect(service.getModuleNotices()).toEqual(notices); }); }); + + describe('custom status check', () => { + test('returns undefined when name is customStatusCheckName is empty', () => { + const service = new TutorialService(); + expect(service.getCustomStatusCheck('')).toBeUndefined(); + }); + test('returns undefined when custom status check was not registered', () => { + const service = new TutorialService(); + expect(service.getCustomStatusCheck('foo')).toBeUndefined(); + }); + test('returns custom status check', () => { + const service = new TutorialService(); + const callback = jest.fn(); + service.setup().registerCustomStatusCheck('foo', callback); + const customStatusCheckCallback = service.getCustomStatusCheck('foo'); + expect(customStatusCheckCallback).toBeDefined(); + customStatusCheckCallback(); + expect(callback).toHaveBeenCalled(); + }); + }); + + describe('custom component', () => { + test('returns undefined when name is customComponentName is empty', () => { + const service = new TutorialService(); + expect(service.getCustomComponent('')).toBeUndefined(); + }); + test('returns undefined when custom component was not registered', () => { + const service = new TutorialService(); + expect(service.getCustomComponent('foo')).toBeUndefined(); + }); + test('returns custom component', async () => { + const service = new TutorialService(); + const customComponent =
    foo
    ; + service.setup().registerCustomComponent('foo', async () => customComponent); + const customStatusCheckCallback = service.getCustomComponent('foo'); + expect(customStatusCheckCallback).toBeDefined(); + const result = await customStatusCheckCallback(); + expect(result).toEqual(customComponent); + }); + }); }); diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.ts b/src/plugins/home/public/services/tutorials/tutorial_service.ts index 8ba766d34da53..839b0702a499e 100644 --- a/src/plugins/home/public/services/tutorials/tutorial_service.ts +++ b/src/plugins/home/public/services/tutorials/tutorial_service.ts @@ -22,6 +22,9 @@ export type TutorialModuleNoticeComponent = React.FC<{ moduleName: string; }>; +type CustomStatusCheckCallback = () => Promise; +type CustomComponent = () => Promise; + export class TutorialService { private tutorialVariables: TutorialVariables = {}; private tutorialDirectoryNotices: { [key: string]: TutorialDirectoryNoticeComponent } = {}; @@ -29,6 +32,8 @@ export class TutorialService { [key: string]: TutorialDirectoryHeaderLinkComponent; } = {}; private tutorialModuleNotices: { [key: string]: TutorialModuleNoticeComponent } = {}; + private customStatusCheck: Record = {}; + private customComponent: Record = {}; public setup() { return { @@ -74,6 +79,14 @@ export class TutorialService { } this.tutorialModuleNotices[id] = component; }, + + registerCustomStatusCheck: (name: string, fnCallback: CustomStatusCheckCallback) => { + this.customStatusCheck[name] = fnCallback; + }, + + registerCustomComponent: (name: string, component: CustomComponent) => { + this.customComponent[name] = component; + }, }; } @@ -92,6 +105,14 @@ export class TutorialService { public getModuleNotices() { return Object.values(this.tutorialModuleNotices); } + + public getCustomStatusCheck(customStatusCheckName: string) { + return this.customStatusCheck[customStatusCheckName]; + } + + public getCustomComponent(customComponentName: string) { + return this.customComponent[customComponentName]; + } } export type TutorialServiceSetup = ReturnType; diff --git a/src/plugins/home/server/index.ts b/src/plugins/home/server/index.ts index 840a5944a1343..9523766596fed 100644 --- a/src/plugins/home/server/index.ts +++ b/src/plugins/home/server/index.ts @@ -27,4 +27,9 @@ export const plugin = (initContext: PluginInitializerContext) => new HomeServerP export { INSTRUCTION_VARIANT } from '../common/instruction_variant'; export { TutorialsCategory } from './services/tutorials'; -export type { ArtifactsSchema } from './services/tutorials'; +export type { + ArtifactsSchema, + TutorialSchema, + InstructionSetSchema, + InstructionsSchema, +} from './services/tutorials'; diff --git a/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts b/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts index 5efbe067f6ece..76b045173a876 100644 --- a/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts +++ b/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts @@ -56,6 +56,7 @@ const instructionSchema = schema.object({ textPre: schema.maybe(schema.string()), commands: schema.maybe(schema.arrayOf(schema.string())), textPost: schema.maybe(schema.string()), + customComponentName: schema.maybe(schema.string()), }); export type Instruction = TypeOf; @@ -100,7 +101,7 @@ const instructionsSchema = schema.object({ instructionSets: schema.arrayOf(instructionSetSchema), params: schema.maybe(schema.arrayOf(paramSchema)), }); -export type InstructionsSchema = TypeOf; +export type InstructionsSchema = TypeOf; const tutorialIdRegExp = /^[a-zA-Z0-9-]+$/; export const tutorialSchema = schema.object({ @@ -152,6 +153,7 @@ export const tutorialSchema = schema.object({ // saved objects used by data module. savedObjects: schema.maybe(schema.arrayOf(schema.any())), savedObjectsInstallMsg: schema.maybe(schema.string()), + customStatusCheckName: schema.maybe(schema.string()), }); export type TutorialSchema = TypeOf; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx index fc25879b128ec..77ef0903bc6fc 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx @@ -216,7 +216,11 @@ const FieldEditorComponent = ({ Boolean(field?.type) && field?.type !== (updatedType && updatedType[0].value); return ( -
    + {/* Name */} diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap index 40170c39942e5..79c1a11cfef84 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap @@ -153,7 +153,7 @@ exports[`UrlFormatEditor should render normally 1`] = ` class="euiFormControlLayout__childrenWrapper" > diff --git a/src/plugins/inspector/public/ui/__snapshots__/inspector_panel.test.tsx.snap b/src/plugins/inspector/public/ui/__snapshots__/inspector_panel.test.tsx.snap index 5ad8205365146..67d2cf72c5375 100644 --- a/src/plugins/inspector/public/ui/__snapshots__/inspector_panel.test.tsx.snap +++ b/src/plugins/inspector/public/ui/__snapshots__/inspector_panel.test.tsx.snap @@ -329,6 +329,7 @@ exports[`InspectorPanel should render as expected 1`] = ` >
    & { +export type KibanaPageTemplateSolutionNavProps = Partial> & { /** * Name of the solution, i.e. "Observability" */ diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index a6b79a9e2c009..ff637b6686612 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -396,6 +396,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, + 'visualization:visualize:legacyPieChartsLibrary': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'doc_table:legacy': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 8448b359ce607..b59abc3aa7158 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -26,6 +26,7 @@ export interface UsageStats { 'autocomplete:useTimeRange': boolean; 'search:timeout': number; 'visualization:visualize:legacyChartsLibrary': boolean; + 'visualization:visualize:legacyPieChartsLibrary': boolean; 'doc_table:legacy': boolean; 'discover:modifyColumnsOnSwitch': boolean; 'discover:searchFieldsFromSource': boolean; diff --git a/src/plugins/presentation_util/public/components/labs/labs_flyout.tsx b/src/plugins/presentation_util/public/components/labs/labs_flyout.tsx index 5b424c7e95f18..1af85da983085 100644 --- a/src/plugins/presentation_util/public/components/labs/labs_flyout.tsx +++ b/src/plugins/presentation_util/public/components/labs/labs_flyout.tsx @@ -20,7 +20,6 @@ import { EuiFlexItem, EuiFlexGroup, EuiIcon, - EuiOverlayMask, } from '@elastic/eui'; import { SolutionName, ProjectStatus, ProjectID, Project, EnvironmentName } from '../../../common'; @@ -124,30 +123,32 @@ export const LabsFlyout = (props: Props) => { ); return ( - onClose()} headerZindexLocation="below"> - - - -

    - - - - - {strings.getTitleLabel()} - -

    -
    - - -

    {strings.getDescriptionMessage()}

    -
    -
    - - - - {footer} -
    -
    + + + +

    + + + + + {strings.getTitleLabel()} + +

    +
    + + +

    {strings.getDescriptionMessage()}

    +
    +
    + + + + {footer} +
    ); }; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap index 5239a92543539..5a8cd06b8ecc0 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap @@ -47,20 +47,30 @@ exports[`Intro component renders correctly 1`] = `
    -
    - +
    - Modifying objects is for advanced users only. Object properties are not validated and invalid objects could cause errors, data loss, or worse. Unless someone with intimate knowledge of the code told you to be in here, you probably shouldn’t be. - -
    +
    + + Modifying objects is for advanced users only. Object properties are not validated and invalid objects could cause errors, data loss, or worse. Unless someone with intimate knowledge of the code told you to be in here, you probably shouldn’t be. + +
    +
    +
    diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap index bddfe000008d4..f977c17df41d3 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap @@ -49,29 +49,39 @@ exports[`NotFoundErrors component renders correctly for index-pattern type 1`] =
    -
    - - The index pattern associated with this object no longer exists. - -
    -
    - +
    - If you know what this error means, go ahead and fix it — otherwise click the delete button above. - -
    +
    + + The index pattern associated with this object no longer exists. + +
    +
    + + If you know what this error means, go ahead and fix it — otherwise click the delete button above. + +
    +
    +
    @@ -128,29 +138,39 @@ exports[`NotFoundErrors component renders correctly for index-pattern-field type
    -
    - - A field associated with this object no longer exists in the index pattern. - -
    -
    - +
    - If you know what this error means, go ahead and fix it — otherwise click the delete button above. - -
    +
    + + A field associated with this object no longer exists in the index pattern. + +
    +
    + + If you know what this error means, go ahead and fix it — otherwise click the delete button above. + +
    +
    +
    @@ -207,29 +227,39 @@ exports[`NotFoundErrors component renders correctly for search type 1`] = `
    -
    - - The saved search associated with this object no longer exists. - -
    -
    - +
    - If you know what this error means, go ahead and fix it — otherwise click the delete button above. - -
    +
    + + The saved search associated with this object no longer exists. + +
    +
    + + If you know what this error means, go ahead and fix it — otherwise click the delete button above. + +
    +
    +
    @@ -286,21 +316,31 @@ exports[`NotFoundErrors component renders correctly for unknown type 1`] = `
    -
    -
    - +
    - If you know what this error means, go ahead and fix it — otherwise click the delete button above. - -
    +
    +
    + + If you know what this error means, go ahead and fix it — otherwise click the delete button above. + +
    +
    +
    diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap index a68e8891b5ad1..bd97f2e6bffb1 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap @@ -2,6 +2,7 @@ exports[`Flyout conflicts should allow conflict resolution 1`] = ` @@ -277,6 +278,7 @@ exports[`Flyout conflicts should allow conflict resolution 2`] = ` exports[`Flyout legacy conflicts should allow conflict resolution 1`] = ` @@ -548,6 +550,7 @@ Array [ exports[`Flyout should render import step 1`] = ` diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx index 62e0cd0504e8e..f6c8d5fb69408 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx @@ -960,7 +960,7 @@ export class Flyout extends Component { } return ( - +

    diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 99c6dcb40e57d..496335a3b0dc8 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -8594,6 +8594,12 @@ "description": "Non-default value of setting." } }, + "visualization:visualize:legacyPieChartsLibrary": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "doc_table:legacy": { "type": "boolean", "_meta": { diff --git a/src/plugins/vis_type_pie/common/index.ts b/src/plugins/vis_type_pie/common/index.ts index 1aa1680530b32..a02a2b2ba10f2 100644 --- a/src/plugins/vis_type_pie/common/index.ts +++ b/src/plugins/vis_type_pie/common/index.ts @@ -7,3 +7,4 @@ */ export const DEFAULT_PERCENT_DECIMALS = 2; +export const LEGACY_PIE_CHARTS_LIBRARY = 'visualization:visualize:legacyPieChartsLibrary'; diff --git a/src/plugins/vis_type_pie/kibana.json b/src/plugins/vis_type_pie/kibana.json index ee312fd19e8d5..eebefc42681b7 100644 --- a/src/plugins/vis_type_pie/kibana.json +++ b/src/plugins/vis_type_pie/kibana.json @@ -2,8 +2,10 @@ "id": "visTypePie", "version": "kibana", "ui": true, + "server": true, "requiredPlugins": ["charts", "data", "expressions", "visualizations", "usageCollection"], "requiredBundles": ["visDefaultEditor"], + "extraPublicDirs": ["common/index"], "owner": { "name": "Kibana App", "githubTeam": "kibana-app" diff --git a/src/plugins/vis_type_pie/public/plugin.ts b/src/plugins/vis_type_pie/public/plugin.ts index 440a3a75a2eb1..787f49c19aca3 100644 --- a/src/plugins/vis_type_pie/public/plugin.ts +++ b/src/plugins/vis_type_pie/public/plugin.ts @@ -12,7 +12,7 @@ import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; import { ChartsPluginSetup } from '../../charts/public'; import { UsageCollectionSetup } from '../../usage_collection/public'; import { DataPublicPluginStart } from '../../data/public'; -import { LEGACY_CHARTS_LIBRARY } from '../../visualizations/common/constants'; +import { LEGACY_PIE_CHARTS_LIBRARY } from '../common'; import { pieLabels as pieLabelsExpressionFunction } from './expression_functions/pie_labels'; import { createPieVisFn } from './pie_fn'; import { getPieVisRenderer } from './pie_renderer'; @@ -43,7 +43,7 @@ export class VisTypePiePlugin { core: CoreSetup, { expressions, visualizations, charts, usageCollection }: VisTypePieSetupDependencies ) { - if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, false)) { + if (!core.uiSettings.get(LEGACY_PIE_CHARTS_LIBRARY, false)) { const getStartDeps = async () => { const [coreStart, deps] = await core.getStartServices(); return { diff --git a/src/plugins/vis_type_pie/public/utils/get_layers.ts b/src/plugins/vis_type_pie/public/utils/get_layers.ts index 27dcf2d379811..5a82871bf3688 100644 --- a/src/plugins/vis_type_pie/public/utils/get_layers.ts +++ b/src/plugins/vis_type_pie/public/utils/get_layers.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; import { Datum, PartitionFillLabel, @@ -125,11 +124,6 @@ export const getLayers = ( }, showAccessor: (d: Datum) => d !== EMPTY_SLICE, nodeLabel: (d: unknown) => { - if (d === '') { - return i18n.translate('visTypePie.emptyLabelValue', { - defaultMessage: '(empty)', - }); - } if (col.format) { const formattedLabel = formatter.deserialize(col.format).convert(d) ?? ''; if (visParams.labels.truncate && formattedLabel.length <= visParams.labels.truncate) { diff --git a/src/plugins/vis_type_pie/server/index.ts b/src/plugins/vis_type_pie/server/index.ts new file mode 100644 index 0000000000000..201071fbb5fca --- /dev/null +++ b/src/plugins/vis_type_pie/server/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { VisTypePieServerPlugin } from './plugin'; + +export const plugin = () => new VisTypePieServerPlugin(); diff --git a/src/plugins/vis_type_pie/server/plugin.ts b/src/plugins/vis_type_pie/server/plugin.ts new file mode 100644 index 0000000000000..48576bdff5d33 --- /dev/null +++ b/src/plugins/vis_type_pie/server/plugin.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + +import { CoreSetup, Plugin, UiSettingsParams } from 'kibana/server'; + +import { LEGACY_PIE_CHARTS_LIBRARY } from '../common'; + +export const getUiSettingsConfig: () => Record> = () => ({ + // TODO: Remove this when vis_type_vislib is removed + // https://github.com/elastic/kibana/issues/56143 + [LEGACY_PIE_CHARTS_LIBRARY]: { + name: i18n.translate('visTypePie.advancedSettings.visualization.legacyPieChartsLibrary.name', { + defaultMessage: 'Pie legacy charts library', + }), + requiresPageReload: true, + value: false, + description: i18n.translate( + 'visTypePie.advancedSettings.visualization.legacyPieChartsLibrary.description', + { + defaultMessage: 'Enables legacy charts library for pie charts in visualize.', + } + ), + deprecation: { + message: i18n.translate( + 'visTypePie.advancedSettings.visualization.legacyPieChartsLibrary.deprecation', + { + defaultMessage: + 'The legacy charts library for pie in visualize is deprecated and will not be supported as of 8.0.', + } + ), + docLinksKey: 'visualizationSettings', + }, + category: ['visualization'], + schema: schema.boolean(), + }, +}); + +export class VisTypePieServerPlugin implements Plugin { + public setup(core: CoreSetup) { + core.uiSettings.register(getUiSettingsConfig()); + + return {}; + } + + public start() { + return {}; + } +} diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx b/src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx index 8e975f9904256..50d3e8c38e389 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx @@ -36,7 +36,7 @@ describe('ColorPicker', () => { const props = { ...defaultProps, value: '#68BC00' }; component = mount(); component.find('.tvbColorPicker button').simulate('click'); - const input = findTestSubject(component, 'topColorPickerInput'); + const input = findTestSubject(component, 'euiColorPickerInput_top'); expect(input.props().value).toBe('#68BC00'); }); @@ -44,7 +44,7 @@ describe('ColorPicker', () => { const props = { ...defaultProps, value: 'rgba(85,66,177,1)' }; component = mount(); component.find('.tvbColorPicker button').simulate('click'); - const input = findTestSubject(component, 'topColorPickerInput'); + const input = findTestSubject(component, 'euiColorPickerInput_top'); expect(input.props().value).toBe('85,66,177,1'); }); diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/reorder.js b/src/plugins/vis_type_timeseries/public/application/components/lib/reorder.ts similarity index 85% rename from src/plugins/vis_type_timeseries/public/application/components/lib/reorder.js rename to src/plugins/vis_type_timeseries/public/application/components/lib/reorder.ts index 15c21e19af2a5..a026b5bb2051e 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/reorder.js +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/reorder.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export const reorder = (list, startIndex, endIndex) => { +export const reorder = (list: unknown[], startIndex: number, endIndex: number) => { const result = Array.from(list); const [removed] = result.splice(startIndex, 1); result.splice(endIndex, 0, removed); diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.test.js b/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.test.js rename to src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.js b/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.ts similarity index 77% rename from src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.js rename to src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.ts index 458866f2098a0..2862fe933bfb7 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.js +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.ts @@ -6,20 +6,30 @@ * Side Public License, v 1. */ -import _ from 'lodash'; -import handlebars from 'handlebars/dist/handlebars'; -import { emptyLabel } from '../../../../common/empty_label'; +import handlebars from 'handlebars'; import { i18n } from '@kbn/i18n'; +import { emptyLabel } from '../../../../common/empty_label'; + +type CompileOptions = Parameters[1]; -export function replaceVars(str, args = {}, vars = {}) { +export function replaceVars( + str: string, + args: Record = {}, + vars: Record = {}, + compileOptions: Partial = {} +) { try { - // we need add '[]' for emptyLabel because this value contains special characters. (https://handlebarsjs.com/guide/expressions.html#literal-segments) + /** we need add '[]' for emptyLabel because this value contains special characters. + * @see (https://handlebarsjs.com/guide/expressions.html#literal-segments) **/ const template = handlebars.compile(str.split(emptyLabel).join(`[${emptyLabel}]`), { strict: true, knownHelpersOnly: true, + ...compileOptions, + }); + const string = template({ + ...vars, + args, }); - - const string = template(_.assign({}, vars, { args })); return string; } catch (e) { diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js b/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js index 70529be78567d..c1d82a182e509 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import handlebars from 'handlebars/dist/handlebars'; import { isNumber } from 'lodash'; +import handlebars from 'handlebars'; import { isEmptyValue, DISPLAY_EMPTY_VALUE } from '../../../../common/last_value_utils'; import { inputFormats, outputFormats, isDuration } from '../lib/durations'; import { getFieldFormats } from '../../../services'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js index 8e59e8e1bb628..097b0a7b5e332 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js @@ -51,7 +51,9 @@ class TimeseriesVisualization extends Component { }; applyDocTo = (template) => (doc) => { - const vars = replaceVars(template, null, doc); + const vars = replaceVars(template, null, doc, { + noEscape: true, + }); if (vars instanceof Error) { this.showToastNotification = vars.error.caused_by; diff --git a/src/plugins/vis_type_vislib/public/plugin.ts b/src/plugins/vis_type_vislib/public/plugin.ts index 52faf8a74778c..cdc02aacafa3b 100644 --- a/src/plugins/vis_type_vislib/public/plugin.ts +++ b/src/plugins/vis_type_vislib/public/plugin.ts @@ -13,7 +13,8 @@ import { VisualizationsSetup } from '../../visualizations/public'; import { ChartsPluginSetup } from '../../charts/public'; import { DataPublicPluginStart } from '../../data/public'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; -import { LEGACY_CHARTS_LIBRARY } from '../../visualizations/common/constants'; +import { LEGACY_CHARTS_LIBRARY } from '../../vis_type_xy/common/index'; +import { LEGACY_PIE_CHARTS_LIBRARY } from '../../vis_type_pie/common/index'; import { createVisTypeVislibVisFn } from './vis_type_vislib_vis_fn'; import { createPieVisFn } from './pie_fn'; @@ -50,17 +51,18 @@ export class VisTypeVislibPlugin core: VisTypeVislibCoreSetup, { expressions, visualizations, charts }: VisTypeVislibPluginSetupDependencies ) { - if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, false)) { - // Register only non-replaced vis types - convertedTypeDefinitions.forEach(visualizations.createBaseVisualization); - expressions.registerRenderer(getVislibVisRenderer(core, charts)); - expressions.registerFunction(createVisTypeVislibVisFn()); - } else { - // Register all vis types - visLibVisTypeDefinitions.forEach(visualizations.createBaseVisualization); + const typeDefinitions = !core.uiSettings.get(LEGACY_CHARTS_LIBRARY, false) + ? convertedTypeDefinitions + : visLibVisTypeDefinitions; + // register vislib XY axis charts + typeDefinitions.forEach(visualizations.createBaseVisualization); + expressions.registerRenderer(getVislibVisRenderer(core, charts)); + expressions.registerFunction(createVisTypeVislibVisFn()); + + if (core.uiSettings.get(LEGACY_PIE_CHARTS_LIBRARY, false)) { + // register vislib pie chart visualizations.createBaseVisualization(pieVisTypeDefinition); - expressions.registerRenderer(getVislibVisRenderer(core, charts)); - [createVisTypeVislibVisFn(), createPieVisFn()].forEach(expressions.registerFunction); + expressions.registerFunction(createPieVisFn()); } } diff --git a/src/plugins/vis_type_xy/common/index.ts b/src/plugins/vis_type_xy/common/index.ts index f17bc8476d9a6..a80946f7c62fa 100644 --- a/src/plugins/vis_type_xy/common/index.ts +++ b/src/plugins/vis_type_xy/common/index.ts @@ -19,3 +19,5 @@ export enum ChartType { * Type of xy visualizations */ export type XyVisType = ChartType | 'horizontal_bar'; + +export const LEGACY_CHARTS_LIBRARY = 'visualization:visualize:legacyChartsLibrary'; diff --git a/src/plugins/vis_type_xy/kibana.json b/src/plugins/vis_type_xy/kibana.json index 1d7fd6a0813b4..c25f035fb6d4b 100644 --- a/src/plugins/vis_type_xy/kibana.json +++ b/src/plugins/vis_type_xy/kibana.json @@ -2,8 +2,10 @@ "id": "visTypeXy", "version": "kibana", "ui": true, + "server": true, "requiredPlugins": ["charts", "data", "expressions", "visualizations", "usageCollection"], "requiredBundles": ["kibanaUtils", "visDefaultEditor"], + "extraPublicDirs": ["common/index"], "owner": { "name": "Kibana App", "githubTeam": "kibana-app" diff --git a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx b/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx index c9ed82fcf58e5..fb6b4bb41d9ba 100644 --- a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx +++ b/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx @@ -19,7 +19,6 @@ import { import { Aspects } from '../types'; import './_detailed_tooltip.scss'; -import { fillEmptyValue } from '../utils/get_series_name_fn'; import { COMPLEX_SPLIT_ACCESSOR, isRangeAggType } from '../utils/accessors'; interface TooltipData { @@ -100,8 +99,7 @@ export const getTooltipData = ( return data; }; -const renderData = ({ label, value: rawValue }: TooltipData, index: number) => { - const value = fillEmptyValue(rawValue); +const renderData = ({ label, value }: TooltipData, index: number) => { return label && value ? ( diff --git a/src/plugins/vis_type_xy/public/components/xy_settings.tsx b/src/plugins/vis_type_xy/public/components/xy_settings.tsx index 8922f512522a0..8d6a7eecdfe52 100644 --- a/src/plugins/vis_type_xy/public/components/xy_settings.tsx +++ b/src/plugins/vis_type_xy/public/components/xy_settings.tsx @@ -29,7 +29,6 @@ import { renderEndzoneTooltip } from '../../../charts/public'; import { getThemeService, getUISettings } from '../services'; import { VisConfig } from '../types'; -import { fillEmptyValue } from '../utils/get_series_name_fn'; declare global { interface Window { @@ -134,7 +133,7 @@ export const XYSettings: FC = ({ }; const headerValueFormatter: TickFormatter | undefined = xAxis.ticks?.formatter - ? (value) => fillEmptyValue(xAxis.ticks?.formatter?.(value)) ?? '' + ? (value) => xAxis.ticks?.formatter?.(value) ?? '' : undefined; const headerFormatter = isTimeChart && xDomain && adjustedXDomain diff --git a/src/plugins/vis_type_xy/public/config/get_axis.ts b/src/plugins/vis_type_xy/public/config/get_axis.ts index 08b17c882eea6..71d33cc20d057 100644 --- a/src/plugins/vis_type_xy/public/config/get_axis.ts +++ b/src/plugins/vis_type_xy/public/config/get_axis.ts @@ -27,7 +27,6 @@ import { YScaleType, SeriesParam, } from '../types'; -import { fillEmptyValue } from '../utils/get_series_name_fn'; export function getAxis( { type, title: axisTitle, labels, scale: axisScale, ...axis }: CategoryAxis, @@ -90,8 +89,7 @@ function getLabelFormatter( } return (value: any) => { - const formattedStringValue = `${formatter ? formatter(value) : value}`; - const finalValue = fillEmptyValue(formattedStringValue); + const finalValue = `${formatter ? formatter(value) : value}`; if (finalValue.length > truncate) { return `${finalValue.slice(0, truncate)}...`; diff --git a/src/plugins/vis_type_xy/public/plugin.ts b/src/plugins/vis_type_xy/public/plugin.ts index e8d53127765b4..b595d3172f143 100644 --- a/src/plugins/vis_type_xy/public/plugin.ts +++ b/src/plugins/vis_type_xy/public/plugin.ts @@ -23,7 +23,7 @@ import { } from './services'; import { visTypesDefinitions } from './vis_types'; -import { LEGACY_CHARTS_LIBRARY } from '../../visualizations/common/constants'; +import { LEGACY_CHARTS_LIBRARY } from '../common/'; import { xyVisRenderer } from './vis_renderer'; import * as expressionFunctions from './expression_functions'; diff --git a/src/plugins/vis_type_xy/public/utils/get_series_name_fn.ts b/src/plugins/vis_type_xy/public/utils/get_series_name_fn.ts index 0e54650e22f75..137f8a5558010 100644 --- a/src/plugins/vis_type_xy/public/utils/get_series_name_fn.ts +++ b/src/plugins/vis_type_xy/public/utils/get_series_name_fn.ts @@ -8,21 +8,10 @@ import { memoize } from 'lodash'; -import { i18n } from '@kbn/i18n'; import { XYChartSeriesIdentifier, SeriesName } from '@elastic/charts'; import { VisConfig } from '../types'; -const emptyTextLabel = i18n.translate('visTypeXy.emptyTextColumnValue', { - defaultMessage: '(empty)', -}); - -/** - * Returns empty values - */ -export const fillEmptyValue = (value: T) => - value === '' ? emptyTextLabel : value; - function getSplitValues( splitAccessors: XYChartSeriesIdentifier['splitAccessors'], seriesAspects?: VisConfig['aspects']['series'] @@ -36,7 +25,7 @@ function getSplitValues( const split = (seriesAspects ?? []).find(({ accessor }) => accessor === key); splitValues.push(split?.formatter ? split?.formatter(value) : value); }); - return splitValues.map(fillEmptyValue); + return splitValues; } export const getSeriesNameFn = (aspects: VisConfig['aspects'], multipleY = false) => diff --git a/src/plugins/vis_type_xy/server/index.ts b/src/plugins/vis_type_xy/server/index.ts new file mode 100644 index 0000000000000..a27ac49c0ea49 --- /dev/null +++ b/src/plugins/vis_type_xy/server/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { VisTypeXyServerPlugin } from './plugin'; + +export const plugin = () => new VisTypeXyServerPlugin(); diff --git a/src/plugins/vis_type_xy/server/plugin.ts b/src/plugins/vis_type_xy/server/plugin.ts new file mode 100644 index 0000000000000..46d6531204c24 --- /dev/null +++ b/src/plugins/vis_type_xy/server/plugin.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + +import { CoreSetup, Plugin, UiSettingsParams } from 'kibana/server'; + +import { LEGACY_CHARTS_LIBRARY } from '../common'; + +export const getUiSettingsConfig: () => Record> = () => ({ + // TODO: Remove this when vis_type_vislib is removed + // https://github.com/elastic/kibana/issues/56143 + [LEGACY_CHARTS_LIBRARY]: { + name: i18n.translate('visTypeXy.advancedSettings.visualization.legacyChartsLibrary.name', { + defaultMessage: 'XY axis legacy charts library', + }), + requiresPageReload: true, + value: false, + description: i18n.translate( + 'visTypeXy.advancedSettings.visualization.legacyChartsLibrary.description', + { + defaultMessage: 'Enables legacy charts library for area, line and bar charts in visualize.', + } + ), + deprecation: { + message: i18n.translate( + 'visTypeXy.advancedSettings.visualization.legacyChartsLibrary.deprecation', + { + defaultMessage: + 'The legacy charts library for area, line and bar charts in visualize is deprecated and will not be supported as of 7.16.', + } + ), + docLinksKey: 'visualizationSettings', + }, + category: ['visualization'], + schema: schema.boolean(), + }, +}); + +export class VisTypeXyServerPlugin implements Plugin { + public setup(core: CoreSetup) { + core.uiSettings.register(getUiSettingsConfig()); + + return {}; + } + + public start() { + return {}; + } +} diff --git a/src/plugins/visualizations/common/constants.ts b/src/plugins/visualizations/common/constants.ts index a33e74b498a2c..a8a0963ac8948 100644 --- a/src/plugins/visualizations/common/constants.ts +++ b/src/plugins/visualizations/common/constants.ts @@ -7,4 +7,3 @@ */ export const VISUALIZE_ENABLE_LABS_SETTING = 'visualize:enableLabs'; -export const LEGACY_CHARTS_LIBRARY = 'visualization:visualize:legacyChartsLibrary'; diff --git a/src/plugins/visualizations/public/components/__snapshots__/visualization_noresults.test.js.snap b/src/plugins/visualizations/public/components/__snapshots__/visualization_noresults.test.js.snap index 25ec05c83a8c6..56e2cb1b60f3c 100644 --- a/src/plugins/visualizations/public/components/__snapshots__/visualization_noresults.test.js.snap +++ b/src/plugins/visualizations/public/components/__snapshots__/visualization_noresults.test.js.snap @@ -14,7 +14,7 @@ exports[`VisualizationNoResults should render according to snapshot 1`] = ` data-euiicon-type="visualizeApp" />
    { await PageObjects.settings.clickEditFieldFormat(); await a11y.testAppSnapshot(); + await PageObjects.settings.clickCloseEditFieldFormatFlyout(); }); it('Advanced settings', async () => { diff --git a/test/api_integration/apis/ui_counters/ui_counters.ts b/test/api_integration/apis/ui_counters/ui_counters.ts index 2be6ea4341fb0..019dcfd621655 100644 --- a/test/api_integration/apis/ui_counters/ui_counters.ts +++ b/test/api_integration/apis/ui_counters/ui_counters.ts @@ -56,7 +56,8 @@ export default function ({ getService }: FtrProviderContext) { return savedObject; }; - describe('UI Counters API', () => { + // FLAKY: https://github.com/elastic/kibana/issues/98240 + describe.skip('UI Counters API', () => { const dayDate = moment().format('DDMMYYYY'); before(async () => await esArchiver.emptyKibanaIndex()); diff --git a/test/functional/apps/context/index.js b/test/functional/apps/context/index.js index 7612dae338d9f..031171a58718b 100644 --- a/test/functional/apps/context/index.js +++ b/test/functional/apps/context/index.js @@ -15,16 +15,18 @@ export default function ({ getService, getPageObjects, loadTestFile }) { describe('context app', function () { this.tags('ciGroup1'); - before(async function () { + before(async () => { await browser.setWindowSize(1200, 800); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await esArchiver.load('test/functional/fixtures/es_archiver/visualize'); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/visualize.json'); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); await PageObjects.common.navigateToApp('discover'); }); - after(function unloadMakelogs() { - return esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + after(async () => { + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/visualize.json' + ); }); loadTestFile(require.resolve('./_context_navigation')); diff --git a/test/functional/apps/dashboard/dashboard_state.ts b/test/functional/apps/dashboard/dashboard_state.ts index 047681e1a8ace..6c259f5a71efa 100644 --- a/test/functional/apps/dashboard/dashboard_state.ts +++ b/test/functional/apps/dashboard/dashboard_state.ts @@ -53,6 +53,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { if (isNewChartsLibraryEnabled) { await kibanaServer.uiSettings.update({ 'visualization:visualize:legacyChartsLibrary': false, + 'visualization:visualize:legacyPieChartsLibrary': false, }); await browser.refresh(); } diff --git a/test/functional/apps/dashboard/index.ts b/test/functional/apps/dashboard/index.ts index 4b83b2ac92deb..e4dc04282e4ac 100644 --- a/test/functional/apps/dashboard/index.ts +++ b/test/functional/apps/dashboard/index.ts @@ -123,6 +123,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await loadLogstash(); await kibanaServer.uiSettings.update({ 'visualization:visualize:legacyChartsLibrary': false, + 'visualization:visualize:legacyPieChartsLibrary': false, }); await browser.refresh(); }); @@ -131,6 +132,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await unloadLogstash(); await kibanaServer.uiSettings.update({ 'visualization:visualize:legacyChartsLibrary': true, + 'visualization:visualize:legacyPieChartsLibrary': true, }); await browser.refresh(); }); diff --git a/test/functional/apps/discover/_indexpattern_with_unmapped_fields.ts b/test/functional/apps/discover/_indexpattern_with_unmapped_fields.ts index e986429a15d26..264885490cdfc 100644 --- a/test/functional/apps/discover/_indexpattern_with_unmapped_fields.ts +++ b/test/functional/apps/discover/_indexpattern_with_unmapped_fields.ts @@ -12,26 +12,31 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); - const log = getService('log'); + const security = getService('security'); const retry = getService('retry'); const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); describe('index pattern with unmapped fields', () => { before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/unmapped_fields'); + await security.testUser.setRoles(['kibana_admin', 'test-index-unmapped-fields']); + const fromTime = 'Jan 20, 2021 @ 00:00:00.000'; + const toTime = 'Jan 25, 2021 @ 00:00:00.000'; + await kibanaServer.uiSettings.replace({ defaultIndex: 'test-index-unmapped-fields', 'discover:searchFieldsFromSource': false, + 'timepicker:timeDefaults': `{ "from": "${fromTime}", "to": "${toTime}"}`, }); - log.debug('discover'); - const fromTime = 'Jan 20, 2021 @ 00:00:00.000'; - const toTime = 'Jan 25, 2021 @ 00:00:00.000'; + await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); }); after(async () => { await esArchiver.unload('test/functional/fixtures/es_archiver/unmapped_fields'); + await kibanaServer.uiSettings.unset('defaultIndex'); + await kibanaServer.uiSettings.unset('discover:searchFieldsFromSource'); + await kibanaServer.uiSettings.unset('timepicker:timeDefaults'); }); it('unmapped fields exist on a new saved search', async () => { diff --git a/test/functional/apps/discover/_sidebar.ts b/test/functional/apps/discover/_sidebar.ts index 8179f4e44e8b8..d8701261126c4 100644 --- a/test/functional/apps/discover/_sidebar.ts +++ b/test/functional/apps/discover/_sidebar.ts @@ -14,8 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); const testSubjects = getService('testSubjects'); - // Failing: See https://github.com/elastic/kibana/issues/101449 - describe.skip('discover sidebar', function describeIndexTests() { + describe('discover sidebar', function describeIndexTests() { before(async function () { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/discover'); diff --git a/test/functional/apps/discover/_source_filters.ts b/test/functional/apps/discover/_source_filters.ts index f3793dc3e0288..6c6979b39702c 100644 --- a/test/functional/apps/discover/_source_filters.ts +++ b/test/functional/apps/discover/_source_filters.ts @@ -23,8 +23,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { defaultIndex: 'logstash-*', }); - log.debug('load kibana index with default index pattern'); - await esArchiver.load('test/functional/fixtures/es_archiver/visualize_source-filters'); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/visualize.json'); // and load a set of makelogs data await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); @@ -43,6 +42,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.sleep(1000); }); + after(async () => { + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/visualize.json' + ); + }); + it('should not get the field referer', async function () { const fieldNames = await PageObjects.discover.getAllFieldNames(); expect(fieldNames).to.not.contain('referer'); diff --git a/test/functional/apps/getting_started/_shakespeare.ts b/test/functional/apps/getting_started/_shakespeare.ts index 945c1fdcbdcf4..ae6841b85c98d 100644 --- a/test/functional/apps/getting_started/_shakespeare.ts +++ b/test/functional/apps/getting_started/_shakespeare.ts @@ -57,6 +57,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { if (isNewChartsLibraryEnabled) { await kibanaServer.uiSettings.update({ 'visualization:visualize:legacyChartsLibrary': false, + 'visualization:visualize:legacyPieChartsLibrary': false, }); await browser.refresh(); } diff --git a/test/functional/apps/getting_started/index.ts b/test/functional/apps/getting_started/index.ts index b75a30037d065..4c1c052ef15a2 100644 --- a/test/functional/apps/getting_started/index.ts +++ b/test/functional/apps/getting_started/index.ts @@ -24,6 +24,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { before(async () => { await kibanaServer.uiSettings.update({ 'visualization:visualize:legacyChartsLibrary': false, + 'visualization:visualize:legacyPieChartsLibrary': false, }); await browser.refresh(); }); @@ -31,6 +32,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { after(async () => { await kibanaServer.uiSettings.update({ 'visualization:visualize:legacyChartsLibrary': true, + 'visualization:visualize:legacyPieChartsLibrary': true, }); await browser.refresh(); }); diff --git a/test/functional/apps/management/_import_objects.ts b/test/functional/apps/management/_import_objects.ts index 0278955c577a1..6ef0bfd5a09e8 100644 --- a/test/functional/apps/management/_import_objects.ts +++ b/test/functional/apps/management/_import_objects.ts @@ -419,14 +419,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'index-pattern-test-1' ); - await testSubjects.click('pagination-button-next'); + const flyout = await testSubjects.find('importSavedObjectsFlyout'); + + await (await flyout.findByTestSubject('pagination-button-next')).click(); await PageObjects.savedObjects.setOverriddenIndexPatternValue( 'missing-index-pattern-7', 'index-pattern-test-2' ); - await testSubjects.click('pagination-button-previous'); + await (await flyout.findByTestSubject('pagination-button-previous')).click(); const selectedIdForMissingIndexPattern1 = await testSubjects.getAttribute( 'managementChangeIndexSelection-missing-index-pattern-1', @@ -435,7 +437,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(selectedIdForMissingIndexPattern1).to.eql('f1e4c910-a2e6-11e7-bb30-233be9be6a20'); - await testSubjects.click('pagination-button-next'); + await (await flyout.findByTestSubject('pagination-button-next')).click(); const selectedIdForMissingIndexPattern7 = await testSubjects.getAttribute( 'managementChangeIndexSelection-missing-index-pattern-7', diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index cecd206abd1db..bc6160eba3846 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -31,6 +31,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { before(async () => { await kibanaServer.uiSettings.update({ 'visualization:visualize:legacyChartsLibrary': false, + 'visualization:visualize:legacyPieChartsLibrary': false, }); await browser.refresh(); }); @@ -38,6 +39,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { after(async () => { await kibanaServer.uiSettings.update({ 'visualization:visualize:legacyChartsLibrary': true, + 'visualization:visualize:legacyPieChartsLibrary': true, }); await browser.refresh(); }); diff --git a/test/functional/config.js b/test/functional/config.js index bab1148cf372a..670488003e56c 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -58,6 +58,7 @@ export default async function ({ readConfigFile }) { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', 'visualization:visualize:legacyChartsLibrary': true, + 'visualization:visualize:legacyPieChartsLibrary': true, }, }, @@ -292,6 +293,21 @@ export default async function ({ readConfigFile }) { kibana: [], }, + 'test-index-unmapped-fields': { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['test-index-unmapped-fields'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + animals: { elasticsearch: { cluster: [], diff --git a/test/functional/fixtures/es_archiver/visualize/data.json b/test/functional/fixtures/es_archiver/visualize/data.json deleted file mode 100644 index d48aa3e98d18a..0000000000000 --- a/test/functional/fixtures/es_archiver/visualize/data.json +++ /dev/null @@ -1,388 +0,0 @@ -{ - "type": "doc", - "value": { - "id": "index-pattern:logstash-*", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "index-pattern": { - "fieldAttrs": "{\"utc_time\":{\"customLabel\":\"UTC time\"}}", - "fieldFormatMap": "{\"bytes\":{\"id\":\"bytes\"}}", - "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", - "timeFieldName": "@timestamp", - "title": "logstash-*" - }, - "migrationVersion": { - "index-pattern": "7.11.0" - }, - "references": [ - ], - "type": "index-pattern" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "index-pattern:logstash*", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "index-pattern": { - "fieldFormatMap": "{\"bytes\":{\"id\":\"bytes\"}}", - "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", - "title": "logstash*" - }, - "migrationVersion": { - "index-pattern": "7.11.0" - }, - "references": [ - ], - "type": "index-pattern" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "index-pattern:long-window-logstash-*", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "index-pattern": { - "fieldFormatMap": "{\"bytes\":{\"id\":\"bytes\"}}", - "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", - "timeFieldName": "@timestamp", - "title": "long-window-logstash-*" - }, - "migrationVersion": { - "index-pattern": "7.11.0" - }, - "references": [ - ], - "type": "index-pattern" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:Shared-Item-Visualization-AreaChart", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "logstash-*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "visualization": { - "description": "AreaChart", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Shared-Item Visualization AreaChart", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"New Visualization\",\"type\":\"area\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"smoothLines\":false,\"scale\":\"linear\",\"interpolate\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:Visualization-AreaChart", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "logstash-*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "visualization": { - "description": "AreaChart", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Visualization AreaChart", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Visualization AreaChart\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"labels\":{},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"now-15m\",\"to\":\"now\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:68305470-87bc-11e9-a991-3b492a7c3e09", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "logstash-*", - "name": "control_0_index_pattern", - "type": "index-pattern" - }, - { - "id": "logstash-*", - "name": "control_1_index_pattern", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2019-06-05T18:04:48.310Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" - }, - "title": "chained input control", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"chained input control\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1559757816862\",\"fieldName\":\"geo.src\",\"parent\":\"\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1559757836347\",\"fieldName\":\"clientip\",\"parent\":\"1559757816862\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_1_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:64983230-87bf-11e9-a991-3b492a7c3e09", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "logstash-*", - "name": "control_0_index_pattern", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2019-06-05T18:26:10.771Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" - }, - "title": "dynamic options input control", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"dynamic options input control\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1559759127876\",\"fieldName\":\"geo.src\",\"parent\":\"\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:5d2de430-87c0-11e9-a991-3b492a7c3e09", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "logstash-*", - "name": "control_0_index_pattern", - "type": "index-pattern" - }, - { - "id": "logstash-*", - "name": "control_1_index_pattern", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2019-06-05T18:33:07.827Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" - }, - "title": "chained input control with dynamic options", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"chained input control with dynamic options\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1559759550755\",\"fieldName\":\"machine.os.raw\",\"parent\":\"\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1559759557302\",\"fieldName\":\"geo.src\",\"parent\":\"1559759550755\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_1_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "index-pattern:test_index*", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "index-pattern": { - "fields": "[{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"message.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"message\"}}},{\"name\":\"user\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"user.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"user\"}}}]", - "title": "test_index*" - }, - "migrationVersion": { - "index-pattern": "7.11.0" - }, - "references": [ - ], - "type": "index-pattern" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:AreaChart-no-date-field", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "test_index*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "visualization": { - "description": "AreaChart", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "AreaChart [no date field]", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"AreaChart [no date field]\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"addTooltip\":true,\"addLegend\":true,\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "index-pattern:log*", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "index-pattern": { - "fieldFormatMap": "{\"bytes\":{\"id\":\"bytes\"}}", - "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", - "title": "log*" - }, - "migrationVersion": { - "index-pattern": "7.11.0" - }, - "references": [ - ], - "type": "index-pattern" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:AreaChart-no-time-filter", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "log*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "visualization": { - "description": "AreaChart", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "AreaChart [no time filter]", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"AreaChart [no time filter]\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"addTooltip\":true,\"addLegend\":true,\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:VegaMap", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "visualization": { - "description": "VegaMap", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" - }, - "title": "VegaMap", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"aggs\":[],\"params\":{\"spec\":\"{\\n $schema: https://vega.github.io/schema/vega/v5.json\\n config: {\\n kibana: {type: \\\"map\\\", latitude: 25, longitude: -70, zoom: 3}\\n }\\n data: [\\n {\\n name: table\\n url: {\\n index: kibana_sample_data_flights\\n %context%: true\\n // Uncomment to enable time filtering\\n // %timefield%: timestamp\\n body: {\\n size: 0\\n aggs: {\\n origins: {\\n terms: {field: \\\"OriginAirportID\\\", size: 10000}\\n aggs: {\\n originLocation: {\\n top_hits: {\\n size: 1\\n _source: {\\n includes: [\\\"OriginLocation\\\", \\\"Origin\\\"]\\n }\\n }\\n }\\n distinations: {\\n terms: {field: \\\"DestAirportID\\\", size: 10000}\\n aggs: {\\n destLocation: {\\n top_hits: {\\n size: 1\\n _source: {\\n includes: [\\\"DestLocation\\\"]\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n format: {property: \\\"aggregations.origins.buckets\\\"}\\n transform: [\\n {\\n type: geopoint\\n projection: projection\\n fields: [\\n originLocation.hits.hits[0]._source.OriginLocation.lon\\n originLocation.hits.hits[0]._source.OriginLocation.lat\\n ]\\n }\\n ]\\n }\\n {\\n name: selectedDatum\\n on: [\\n {trigger: \\\"!selected\\\", remove: true}\\n {trigger: \\\"selected\\\", insert: \\\"selected\\\"}\\n ]\\n }\\n ]\\n signals: [\\n {\\n name: selected\\n value: null\\n on: [\\n {events: \\\"@airport:mouseover\\\", update: \\\"datum\\\"}\\n {events: \\\"@airport:mouseout\\\", update: \\\"null\\\"}\\n ]\\n }\\n ]\\n scales: [\\n {\\n name: airportSize\\n type: linear\\n domain: {data: \\\"table\\\", field: \\\"doc_count\\\"}\\n range: [\\n {signal: \\\"zoom*zoom*0.2+1\\\"}\\n {signal: \\\"zoom*zoom*10+1\\\"}\\n ]\\n }\\n ]\\n marks: [\\n {\\n type: group\\n from: {\\n facet: {\\n name: facetedDatum\\n data: selectedDatum\\n field: distinations.buckets\\n }\\n }\\n data: [\\n {\\n name: facetDatumElems\\n source: facetedDatum\\n transform: [\\n {\\n type: geopoint\\n projection: projection\\n fields: [\\n destLocation.hits.hits[0]._source.DestLocation.lon\\n destLocation.hits.hits[0]._source.DestLocation.lat\\n ]\\n }\\n {type: \\\"formula\\\", expr: \\\"{x:parent.x, y:parent.y}\\\", as: \\\"source\\\"}\\n {type: \\\"formula\\\", expr: \\\"{x:datum.x, y:datum.y}\\\", as: \\\"target\\\"}\\n {type: \\\"linkpath\\\", shape: \\\"diagonal\\\"}\\n ]\\n }\\n ]\\n scales: [\\n {\\n name: lineThickness\\n type: log\\n clamp: true\\n range: [1, 8]\\n }\\n {\\n name: lineOpacity\\n type: log\\n clamp: true\\n range: [0.2, 0.8]\\n }\\n ]\\n marks: [\\n {\\n from: {data: \\\"facetDatumElems\\\"}\\n type: path\\n interactive: false\\n encode: {\\n update: {\\n path: {field: \\\"path\\\"}\\n stroke: {value: \\\"black\\\"}\\n strokeWidth: {scale: \\\"lineThickness\\\", field: \\\"doc_count\\\"}\\n strokeOpacity: {scale: \\\"lineOpacity\\\", field: \\\"doc_count\\\"}\\n }\\n }\\n }\\n ]\\n }\\n {\\n name: airport\\n type: symbol\\n from: {data: \\\"table\\\"}\\n encode: {\\n update: {\\n size: {scale: \\\"airportSize\\\", field: \\\"doc_count\\\"}\\n xc: {signal: \\\"datum.x\\\"}\\n yc: {signal: \\\"datum.y\\\"}\\n tooltip: {\\n signal: \\\"{title: datum.originLocation.hits.hits[0]._source.Origin + ' (' + datum.key + ')', connnections: length(datum.distinations.buckets), flights: datum.doc_count}\\\"\\n }\\n }\\n }\\n }\\n ]\\n}\"},\"title\":\"[Flights] Airport Connections (Hover Over Airport)\",\"type\":\"vega\"}" - } - }, - "type": "_doc" - } -} \ No newline at end of file diff --git a/test/functional/fixtures/es_archiver/visualize/mappings.json b/test/functional/fixtures/es_archiver/visualize/mappings.json deleted file mode 100644 index d032352d9a688..0000000000000 --- a/test/functional/fixtures/es_archiver/visualize/mappings.json +++ /dev/null @@ -1,487 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana_$KIBANA_PACKAGE_VERSION": {}, - ".kibana": {} - }, - "index": ".kibana_$KIBANA_PACKAGE_VERSION_001", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd", - "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", - "application_usage_transactional": "3d1b76c39bfb2cc8296b024d73854724", - "config": "c63748b75f39d0c54de12d12c1ccbc20", - "core-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", - "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", - "dashboard": "40554caf09725935e2c02e02563a2d07", - "index-pattern": "45915a1ad866812242df474eb0479052", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "legacy-url-alias": "6155300fd11a00e23d5cbaa39f0fce0a", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "namespace": "2f4316de49999235636386fe51dc06c1", - "namespaces": "2f4316de49999235636386fe51dc06c1", - "originId": "2f4316de49999235636386fe51dc06c1", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "db2c00e39b36f40930a3b9fc71c823e1", - "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", - "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "usage-counters": "8cc260bdceffec4ffc3ad165c97dc1b4", - "visualization": "f819cf6636b75c9e76ba733a0c6ef355" - } - }, - "dynamic": "strict", - "properties": { - "application_usage_daily": { - "dynamic": "false", - "properties": { - "timestamp": { - "type": "date" - } - } - }, - "application_usage_totals": { - "dynamic": "false", - "type": "object" - }, - "application_usage_transactional": { - "dynamic": "false", - "type": "object" - }, - "config": { - "dynamic": "false", - "properties": { - "buildNum": { - "type": "keyword" - } - } - }, - "core-usage-stats": { - "dynamic": "false", - "type": "object" - }, - "coreMigrationVersion": { - "type": "keyword" - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "doc_values": false, - "index": false, - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "index": false, - "type": "text" - } - } - }, - "optionsJSON": { - "index": false, - "type": "text" - }, - "panelsJSON": { - "index": false, - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "pause": { - "doc_values": false, - "index": false, - "type": "boolean" - }, - "section": { - "doc_values": false, - "index": false, - "type": "integer" - }, - "value": { - "doc_values": false, - "index": false, - "type": "integer" - } - } - }, - "timeFrom": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "timeRestore": { - "doc_values": false, - "index": false, - "type": "boolean" - }, - "timeTo": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "index-pattern": { - "dynamic": "false", - "properties": { - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "legacy-url-alias": { - "dynamic": "false", - "properties": { - "disabled": { - "type": "boolean" - }, - "sourceId": { - "type": "keyword" - }, - "targetType": { - "type": "keyword" - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "index-pattern": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "visualization": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "namespace": { - "type": "keyword" - }, - "namespaces": { - "type": "keyword" - }, - "originId": { - "type": "keyword" - }, - "query": { - "properties": { - "description": { - "type": "text" - }, - "filters": { - "enabled": false, - "type": "object" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "index": false, - "type": "keyword" - } - } - }, - "timefilter": { - "enabled": false, - "type": "object" - }, - "title": { - "type": "text" - } - } - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" - }, - "unInstallCount": { - "type": "long" - } - } - }, - "search": { - "properties": { - "columns": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "description": { - "type": "text" - }, - "grid": { - "enabled": false, - "type": "object" - }, - "hideChart": { - "doc_values": false, - "index": false, - "type": "boolean" - }, - "hits": { - "doc_values": false, - "index": false, - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "index": false, - "type": "text" - } - } - }, - "sort": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "search-telemetry": { - "dynamic": "false", - "type": "object" - }, - "server": { - "dynamic": "false", - "type": "object" - }, - "telemetry": { - "properties": { - "allowChangingOptInStatus": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "lastReported": { - "type": "date" - }, - "lastVersionChecked": { - "type": "keyword" - }, - "reportFailureCount": { - "type": "integer" - }, - "reportFailureVersion": { - "type": "keyword" - }, - "sendUsageFrom": { - "type": "keyword" - }, - "userHasSeenNotice": { - "type": "boolean" - } - } - }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "type": { - "type": "keyword" - }, - "ui-counter": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "usage-counters": { - "dynamic": "false", - "properties": { - "domainId": { - "type": "keyword" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "index": false, - "type": "text" - } - } - }, - "savedSearchRefName": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "index": false, - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "index": false, - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "0", - "number_of_shards": "1", - "priority": "10", - "refresh_interval": "1s", - "routing_partition_size": "1" - } - } - } -} \ No newline at end of file diff --git a/test/functional/fixtures/kbn_archiver/visualize.json b/test/functional/fixtures/kbn_archiver/visualize.json index 758841e8d81ef..660da856964b4 100644 --- a/test/functional/fixtures/kbn_archiver/visualize.json +++ b/test/functional/fixtures/kbn_archiver/visualize.json @@ -6,14 +6,14 @@ "timeFieldName": "@timestamp", "title": "logstash-*" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "logstash-*", "migrationVersion": { "index-pattern": "7.11.0" }, "references": [], "type": "index-pattern", - "version": "WzI2LDJd" + "version": "WzEzLDFd" } { @@ -27,10 +27,10 @@ "version": 1, "visState": "{\"title\":\"chained input control with dynamic options\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1559759550755\",\"fieldName\":\"machine.os.raw\",\"parent\":\"\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1559759557302\",\"fieldName\":\"geo.src\",\"parent\":\"1559759550755\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_1_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "5d2de430-87c0-11e9-a991-3b492a7c3e09", "migrationVersion": { - "visualization": "7.13.0" + "visualization": "7.14.0" }, "references": [ { @@ -46,7 +46,7 @@ ], "type": "visualization", "updated_at": "2019-06-05T18:33:07.827Z", - "version": "WzMzLDJd" + "version": "WzIwLDFd" } { @@ -60,10 +60,10 @@ "version": 1, "visState": "{\"title\":\"dynamic options input control\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1559759127876\",\"fieldName\":\"geo.src\",\"parent\":\"\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "64983230-87bf-11e9-a991-3b492a7c3e09", "migrationVersion": { - "visualization": "7.13.0" + "visualization": "7.14.0" }, "references": [ { @@ -74,7 +74,7 @@ ], "type": "visualization", "updated_at": "2019-06-05T18:26:10.771Z", - "version": "WzMyLDJd" + "version": "WzE5LDFd" } { @@ -88,10 +88,10 @@ "version": 1, "visState": "{\"title\":\"chained input control\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1559757816862\",\"fieldName\":\"geo.src\",\"parent\":\"\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1559757836347\",\"fieldName\":\"clientip\",\"parent\":\"1559757816862\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_1_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "68305470-87bc-11e9-a991-3b492a7c3e09", "migrationVersion": { - "visualization": "7.13.0" + "visualization": "7.14.0" }, "references": [ { @@ -107,7 +107,7 @@ ], "type": "visualization", "updated_at": "2019-06-05T18:04:48.310Z", - "version": "WzMxLDJd" + "version": "WzE4LDFd" } { @@ -115,10 +115,14 @@ "fields": "[{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"message.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"message\"}}},{\"name\":\"user\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"user.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"user\"}}}]", "title": "test_index*" }, + "coreMigrationVersion": "7.14.0", "id": "test_index*", + "migrationVersion": { + "index-pattern": "7.11.0" + }, "references": [], "type": "index-pattern", - "version": "WzI1LDJd" + "version": "WzIxLDFd" } { @@ -132,10 +136,10 @@ "version": 1, "visState": "{\"title\":\"AreaChart [no date field]\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"addTooltip\":true,\"addLegend\":true,\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}]}" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "AreaChart-no-date-field", "migrationVersion": { - "visualization": "7.13.0" + "visualization": "7.14.0" }, "references": [ { @@ -145,7 +149,7 @@ } ], "type": "visualization", - "version": "WzM0LDJd" + "version": "WzIyLDFd" } { @@ -154,14 +158,14 @@ "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", "title": "log*" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "log*", "migrationVersion": { "index-pattern": "7.11.0" }, "references": [], "type": "index-pattern", - "version": "WzM1LDJd" + "version": "WzIzLDFd" } { @@ -175,10 +179,10 @@ "version": 1, "visState": "{\"title\":\"AreaChart [no time filter]\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"addTooltip\":true,\"addLegend\":true,\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}]}" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "AreaChart-no-time-filter", "migrationVersion": { - "visualization": "7.13.0" + "visualization": "7.14.0" }, "references": [ { @@ -188,7 +192,7 @@ } ], "type": "visualization", - "version": "WzM2LDJd" + "version": "WzI0LDFd" } { @@ -202,10 +206,10 @@ "version": 1, "visState": "{\"title\":\"New Visualization\",\"type\":\"area\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"smoothLines\":false,\"scale\":\"linear\",\"interpolate\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "Shared-Item-Visualization-AreaChart", "migrationVersion": { - "visualization": "7.13.0" + "visualization": "7.14.0" }, "references": [ { @@ -215,7 +219,7 @@ } ], "type": "visualization", - "version": "WzI5LDJd" + "version": "WzE2LDFd" } { @@ -229,14 +233,14 @@ "version": 1, "visState": "{\"aggs\":[],\"params\":{\"spec\":\"{\\n $schema: https://vega.github.io/schema/vega/v5.json\\n config: {\\n kibana: {type: \\\"map\\\", latitude: 25, longitude: -70, zoom: 3}\\n }\\n data: [\\n {\\n name: table\\n url: {\\n index: kibana_sample_data_flights\\n %context%: true\\n // Uncomment to enable time filtering\\n // %timefield%: timestamp\\n body: {\\n size: 0\\n aggs: {\\n origins: {\\n terms: {field: \\\"OriginAirportID\\\", size: 10000}\\n aggs: {\\n originLocation: {\\n top_hits: {\\n size: 1\\n _source: {\\n includes: [\\\"OriginLocation\\\", \\\"Origin\\\"]\\n }\\n }\\n }\\n distinations: {\\n terms: {field: \\\"DestAirportID\\\", size: 10000}\\n aggs: {\\n destLocation: {\\n top_hits: {\\n size: 1\\n _source: {\\n includes: [\\\"DestLocation\\\"]\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n format: {property: \\\"aggregations.origins.buckets\\\"}\\n transform: [\\n {\\n type: geopoint\\n projection: projection\\n fields: [\\n originLocation.hits.hits[0]._source.OriginLocation.lon\\n originLocation.hits.hits[0]._source.OriginLocation.lat\\n ]\\n }\\n ]\\n }\\n {\\n name: selectedDatum\\n on: [\\n {trigger: \\\"!selected\\\", remove: true}\\n {trigger: \\\"selected\\\", insert: \\\"selected\\\"}\\n ]\\n }\\n ]\\n signals: [\\n {\\n name: selected\\n value: null\\n on: [\\n {events: \\\"@airport:mouseover\\\", update: \\\"datum\\\"}\\n {events: \\\"@airport:mouseout\\\", update: \\\"null\\\"}\\n ]\\n }\\n ]\\n scales: [\\n {\\n name: airportSize\\n type: linear\\n domain: {data: \\\"table\\\", field: \\\"doc_count\\\"}\\n range: [\\n {signal: \\\"zoom*zoom*0.2+1\\\"}\\n {signal: \\\"zoom*zoom*10+1\\\"}\\n ]\\n }\\n ]\\n marks: [\\n {\\n type: group\\n from: {\\n facet: {\\n name: facetedDatum\\n data: selectedDatum\\n field: distinations.buckets\\n }\\n }\\n data: [\\n {\\n name: facetDatumElems\\n source: facetedDatum\\n transform: [\\n {\\n type: geopoint\\n projection: projection\\n fields: [\\n destLocation.hits.hits[0]._source.DestLocation.lon\\n destLocation.hits.hits[0]._source.DestLocation.lat\\n ]\\n }\\n {type: \\\"formula\\\", expr: \\\"{x:parent.x, y:parent.y}\\\", as: \\\"source\\\"}\\n {type: \\\"formula\\\", expr: \\\"{x:datum.x, y:datum.y}\\\", as: \\\"target\\\"}\\n {type: \\\"linkpath\\\", shape: \\\"diagonal\\\"}\\n ]\\n }\\n ]\\n scales: [\\n {\\n name: lineThickness\\n type: log\\n clamp: true\\n range: [1, 8]\\n }\\n {\\n name: lineOpacity\\n type: log\\n clamp: true\\n range: [0.2, 0.8]\\n }\\n ]\\n marks: [\\n {\\n from: {data: \\\"facetDatumElems\\\"}\\n type: path\\n interactive: false\\n encode: {\\n update: {\\n path: {field: \\\"path\\\"}\\n stroke: {value: \\\"black\\\"}\\n strokeWidth: {scale: \\\"lineThickness\\\", field: \\\"doc_count\\\"}\\n strokeOpacity: {scale: \\\"lineOpacity\\\", field: \\\"doc_count\\\"}\\n }\\n }\\n }\\n ]\\n }\\n {\\n name: airport\\n type: symbol\\n from: {data: \\\"table\\\"}\\n encode: {\\n update: {\\n size: {scale: \\\"airportSize\\\", field: \\\"doc_count\\\"}\\n xc: {signal: \\\"datum.x\\\"}\\n yc: {signal: \\\"datum.y\\\"}\\n tooltip: {\\n signal: \\\"{title: datum.originLocation.hits.hits[0]._source.Origin + ' (' + datum.key + ')', connnections: length(datum.distinations.buckets), flights: datum.doc_count}\\\"\\n }\\n }\\n }\\n }\\n ]\\n}\"},\"title\":\"[Flights] Airport Connections (Hover Over Airport)\",\"type\":\"vega\"}" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "VegaMap", "migrationVersion": { - "visualization": "7.13.0" + "visualization": "7.14.0" }, "references": [], "type": "visualization", - "version": "WzM3LDJd" + "version": "WzI1LDFd" } { @@ -250,10 +254,10 @@ "version": 1, "visState": "{\"title\":\"Visualization AreaChart\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"labels\":{},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"now-15m\",\"to\":\"now\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}}]}" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "Visualization-AreaChart", "migrationVersion": { - "visualization": "7.13.0" + "visualization": "7.14.0" }, "references": [ { @@ -263,7 +267,7 @@ } ], "type": "visualization", - "version": "WzMwLDJd" + "version": "WzE3LDFd" } { @@ -272,14 +276,14 @@ "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", "title": "logstash*" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "logstash*", "migrationVersion": { "index-pattern": "7.11.0" }, "references": [], "type": "index-pattern", - "version": "WzI3LDJd" + "version": "WzE0LDFd" } { @@ -289,12 +293,12 @@ "timeFieldName": "@timestamp", "title": "long-window-logstash-*" }, - "coreMigrationVersion": "8.0.0", + "coreMigrationVersion": "7.14.0", "id": "long-window-logstash-*", "migrationVersion": { "index-pattern": "7.11.0" }, "references": [], "type": "index-pattern", - "version": "WzI4LDJd" + "version": "WzE1LDFd" } \ No newline at end of file diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index 65b899d2e2fb0..dc3a04568316e 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -448,7 +448,10 @@ export class DiscoverPageObject extends FtrService { public async closeSidebarFieldFilter() { await this.testSubjects.click('toggleFieldFilterButton'); - await this.testSubjects.missingOrFail('filterSelectionPanel'); + + await this.retry.waitFor('sidebar filter closed', async () => { + return !(await this.testSubjects.exists('filterSelectionPanel')); + }); } public async waitForChartLoadingComplete(renderCount: number) { diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index 88951bb04c956..cb8f198177017 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -739,6 +739,10 @@ export class SettingsPageObject extends FtrService { await this.testSubjects.click('editFieldFormat'); } + async clickCloseEditFieldFormatFlyout() { + await this.testSubjects.click('euiFlyoutCloseButton'); + } + async associateIndexPattern(oldIndexPatternId: string, newIndexPatternTitle: string) { await this.find.clickByCssSelector( `select[data-test-subj="managementChangeIndexSelection-${oldIndexPatternId}"] > diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 6e263dd1cdbbf..7f1ea64bcd979 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -563,7 +563,7 @@ export class VisualBuilderPageObject extends FtrService { public async checkColorPickerPopUpIsPresent(): Promise { this.log.debug(`Check color picker popup is present`); - await this.testSubjects.existOrFail('colorPickerPopover', { timeout: 5000 }); + await this.testSubjects.existOrFail('euiColorPickerPopover', { timeout: 5000 }); } public async changePanelPreview(nth: number = 0): Promise { diff --git a/test/functional/page_objects/visualize_chart_page.ts b/test/functional/page_objects/visualize_chart_page.ts index c8587f4ffd346..64b8c363fa6c2 100644 --- a/test/functional/page_objects/visualize_chart_page.ts +++ b/test/functional/page_objects/visualize_chart_page.ts @@ -37,7 +37,8 @@ export class VisualizeChartPageObject extends FtrService { public async isNewChartsLibraryEnabled(): Promise { const legacyChartsLibrary = Boolean( - await this.kibanaServer.uiSettings.get('visualization:visualize:legacyChartsLibrary') + (await this.kibanaServer.uiSettings.get('visualization:visualize:legacyChartsLibrary')) && + (await this.kibanaServer.uiSettings.get('visualization:visualize:legacyPieChartsLibrary')) ) ?? true; const enabled = !legacyChartsLibrary; this.log.debug(`-- isNewChartsLibraryEnabled = ${enabled}`); diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts index a11a254509e7a..e930406cdcce8 100644 --- a/test/functional/page_objects/visualize_page.ts +++ b/test/functional/page_objects/visualize_page.ts @@ -57,6 +57,7 @@ export class VisualizePageObject extends FtrService { defaultIndex: 'logstash-*', [UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0,0.[000]b', 'visualization:visualize:legacyChartsLibrary': !isNewLibrary, + 'visualization:visualize:legacyPieChartsLibrary': !isNewLibrary, }); } diff --git a/test/functional/services/dashboard/panel_actions.ts b/test/functional/services/dashboard/panel_actions.ts index 9aca790b0b437..4340f16492a7c 100644 --- a/test/functional/services/dashboard/panel_actions.ts +++ b/test/functional/services/dashboard/panel_actions.ts @@ -211,36 +211,29 @@ export class DashboardPanelActionsService extends FtrService { await this.testSubjects.click('confirmSaveSavedObjectButton'); } - async expectExistsRemovePanelAction() { - this.log.debug('expectExistsRemovePanelAction'); - await this.expectExistsPanelAction(REMOVE_PANEL_DATA_TEST_SUBJ); - } - - async expectExistsPanelAction(testSubject: string) { + async expectExistsPanelAction(testSubject: string, title?: string) { this.log.debug('expectExistsPanelAction', testSubject); - await this.openContextMenu(); - if (await this.testSubjects.exists(CLONE_PANEL_DATA_TEST_SUBJ)) return; - if (await this.hasContextMenuMoreItem()) { - await this.clickContextMenuMoreItem(); + + const panelWrapper = title ? await this.getPanelHeading(title) : undefined; + await this.openContextMenu(panelWrapper); + + if (!(await this.testSubjects.exists(testSubject))) { + if (await this.hasContextMenuMoreItem()) { + await this.clickContextMenuMoreItem(); + } + await this.testSubjects.existOrFail(testSubject); } - await this.testSubjects.existOrFail(CLONE_PANEL_DATA_TEST_SUBJ); - await this.toggleContextMenu(); + await this.toggleContextMenu(panelWrapper); } - async expectMissingPanelAction(testSubject: string) { - this.log.debug('expectMissingPanelAction', testSubject); - await this.openContextMenu(); - await this.testSubjects.missingOrFail(testSubject); - if (await this.hasContextMenuMoreItem()) { - await this.clickContextMenuMoreItem(); - await this.testSubjects.missingOrFail(testSubject); - } - await this.toggleContextMenu(); + async expectExistsRemovePanelAction() { + this.log.debug('expectExistsRemovePanelAction'); + await this.expectExistsPanelAction(REMOVE_PANEL_DATA_TEST_SUBJ); } - async expectExistsEditPanelAction() { + async expectExistsEditPanelAction(title?: string) { this.log.debug('expectExistsEditPanelAction'); - await this.expectExistsPanelAction(EDIT_PANEL_DATA_TEST_SUBJ); + await this.expectExistsPanelAction(EDIT_PANEL_DATA_TEST_SUBJ, title); } async expectExistsReplacePanelAction() { @@ -253,6 +246,22 @@ export class DashboardPanelActionsService extends FtrService { await this.expectExistsPanelAction(CLONE_PANEL_DATA_TEST_SUBJ); } + async expectExistsToggleExpandAction() { + this.log.debug('expectExistsToggleExpandAction'); + await this.expectExistsPanelAction(TOGGLE_EXPAND_PANEL_DATA_TEST_SUBJ); + } + + async expectMissingPanelAction(testSubject: string) { + this.log.debug('expectMissingPanelAction', testSubject); + await this.openContextMenu(); + await this.testSubjects.missingOrFail(testSubject); + if (await this.hasContextMenuMoreItem()) { + await this.clickContextMenuMoreItem(); + await this.testSubjects.missingOrFail(testSubject); + } + await this.toggleContextMenu(); + } + async expectMissingEditPanelAction() { this.log.debug('expectMissingEditPanelAction'); await this.expectMissingPanelAction(EDIT_PANEL_DATA_TEST_SUBJ); @@ -273,11 +282,6 @@ export class DashboardPanelActionsService extends FtrService { await this.expectMissingPanelAction(REMOVE_PANEL_DATA_TEST_SUBJ); } - async expectExistsToggleExpandAction() { - this.log.debug('expectExistsToggleExpandAction'); - await this.expectExistsPanelAction(TOGGLE_EXPAND_PANEL_DATA_TEST_SUBJ); - } - async getPanelHeading(title: string) { return await this.testSubjects.find(`embeddablePanelHeading-${title.replace(/\s/g, '')}`); } diff --git a/test/interpreter_functional/test_suites/run_pipeline/index.ts b/test/interpreter_functional/test_suites/run_pipeline/index.ts index 9cf7e0deba2fa..f8c37bab02b86 100644 --- a/test/interpreter_functional/test_suites/run_pipeline/index.ts +++ b/test/interpreter_functional/test_suites/run_pipeline/index.ts @@ -21,7 +21,7 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await esArchiver.load('test/functional/fixtures/es_archiver/visualize_embedding'); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/visualize.json'); await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'Australia/North', defaultIndex: 'logstash-*', @@ -32,6 +32,12 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid await testSubjects.find('pluginContent'); }); + after(async () => { + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/visualize.json' + ); + }); + loadTestFile(require.resolve('./basic')); loadTestFile(require.resolve('./tag_cloud')); loadTestFile(require.resolve('./metric')); diff --git a/test/plugin_functional/test_suites/core_plugins/status.ts b/test/plugin_functional/test_suites/core_plugins/status.ts new file mode 100644 index 0000000000000..2b0f15cb39273 --- /dev/null +++ b/test/plugin_functional/test_suites/core_plugins/status.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { ServiceStatusLevels } from '../../../../src/core/server'; +import { PluginFunctionalProviderContext } from '../../services'; + +export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { + const supertest = getService('supertest'); + const log = getService('log'); + + const delay = (ms: number) => new Promise((r) => setTimeout(r, ms)); + const getStatus = async (pluginName?: string) => { + const resp = await supertest.get('/api/status?v8format=true'); + + if (pluginName) { + return resp.body.status.plugins[pluginName]; + } else { + return resp.body.status.overall; + } + }; + + const setStatus = async (level: T) => + supertest + .post(`/internal/core_plugin_a/status/set?level=${level}`) + .set('kbn-xsrf', 'xxx') + .expect(200); + + describe('status service', () => { + // This test must comes first because the timeout only applies to the initial emission + it("returns a timeout for status check that doesn't emit after 30s", async () => { + let aStatus = await getStatus('corePluginA'); + expect(aStatus.level).to.eql('unavailable'); + + // Status will remain in unavailable due to core services until custom status timesout + // Keep polling until that condition ends, up to a timeout + const start = Date.now(); + while ('elasticsearch' in (aStatus.meta?.affectedServices ?? {})) { + aStatus = await getStatus('corePluginA'); + expect(aStatus.level).to.eql('unavailable'); + + // If it's been more than 40s, break out of this loop + if (Date.now() - start >= 40_000) { + throw new Error(`Timed out waiting for status timeout after 40s`); + } + + log.info('Waiting for status check to timeout...'); + await delay(2000); + } + + expect(aStatus.summary).to.eql('Status check timed out after 30s'); + }); + + it('propagates status issues to dependencies', async () => { + await setStatus('degraded'); + await delay(1000); + expect((await getStatus('corePluginA')).level).to.eql('degraded'); + expect((await getStatus('corePluginB')).level).to.eql('degraded'); + + await setStatus('available'); + await delay(1000); + expect((await getStatus('corePluginA')).level).to.eql('available'); + expect((await getStatus('corePluginB')).level).to.eql('available'); + }); + }); +} diff --git a/test/plugin_functional/test_suites/custom_visualizations/index.js b/test/plugin_functional/test_suites/custom_visualizations/index.js index 0998b97da67ff..22b0f21fb983a 100644 --- a/test/plugin_functional/test_suites/custom_visualizations/index.js +++ b/test/plugin_functional/test_suites/custom_visualizations/index.js @@ -14,7 +14,7 @@ export default function ({ getService, loadTestFile }) { describe('custom visualizations', function () { before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/visualize'); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/visualize.json'); await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'Australia/North', defaultIndex: 'logstash-*', @@ -22,6 +22,12 @@ export default function ({ getService, loadTestFile }) { await browser.setWindowSize(1300, 900); }); + after(async () => { + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/visualize.json' + ); + }); + loadTestFile(require.resolve('./self_changing_vis')); }); } diff --git a/test/scripts/test/server_integration.sh b/test/scripts/test/server_integration.sh index 1ff4a772bb6e0..6ec08c7727e20 100755 --- a/test/scripts/test/server_integration.sh +++ b/test/scripts/test/server_integration.sh @@ -12,3 +12,10 @@ checks-reporter-with-killswitch "Server Integration Tests" \ --bail \ --debug \ --kibana-install-dir $KIBANA_INSTALL_DIR + +# Tests that must be run against source in order to build test plugins +checks-reporter-with-killswitch "Status Integration Tests" \ + node scripts/functional_tests \ + --config test/server_integration/http/platform/config.status.ts \ + --bail \ + --debug \ diff --git a/test/server_integration/__fixtures__/plugins/status_plugin_a/kibana.json b/test/server_integration/__fixtures__/plugins/status_plugin_a/kibana.json new file mode 100644 index 0000000000000..36981d446c9f9 --- /dev/null +++ b/test/server_integration/__fixtures__/plugins/status_plugin_a/kibana.json @@ -0,0 +1,7 @@ +{ + "id": "statusPluginA", + "version": "0.0.1", + "kibanaVersion": "kibana", + "server": true, + "ui": false +} diff --git a/test/server_integration/__fixtures__/plugins/status_plugin_a/package.json b/test/server_integration/__fixtures__/plugins/status_plugin_a/package.json new file mode 100644 index 0000000000000..5c73bca024f4e --- /dev/null +++ b/test/server_integration/__fixtures__/plugins/status_plugin_a/package.json @@ -0,0 +1,14 @@ +{ + "name": "status_plugin_a", + "version": "1.0.0", + "main": "target/test/server_integration/__fixtures__/plugins/status_plugin_a", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "SSPL-1.0 OR Elastic License 2.0", + "scripts": { + "kbn": "node ../../../../../../scripts/kbn.js", + "build": "rm -rf './target' && ../../../../../../node_modules/.bin/tsc" + } +} \ No newline at end of file diff --git a/test/server_integration/__fixtures__/plugins/status_plugin_a/server/index.ts b/test/server_integration/__fixtures__/plugins/status_plugin_a/server/index.ts new file mode 100644 index 0000000000000..cf221c00e32b0 --- /dev/null +++ b/test/server_integration/__fixtures__/plugins/status_plugin_a/server/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { StatusPluginAPlugin } from './plugin'; + +export const plugin = () => new StatusPluginAPlugin(); diff --git a/test/server_integration/__fixtures__/plugins/status_plugin_a/server/plugin.ts b/test/server_integration/__fixtures__/plugins/status_plugin_a/server/plugin.ts new file mode 100644 index 0000000000000..b2e4f0dd322c4 --- /dev/null +++ b/test/server_integration/__fixtures__/plugins/status_plugin_a/server/plugin.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { schema } from '@kbn/config-schema'; +import { Subject } from 'rxjs'; +import { + Plugin, + CoreSetup, + ServiceStatus, + ServiceStatusLevels, +} from '../../../../../../src/core/server'; + +export class StatusPluginAPlugin implements Plugin { + private status$ = new Subject(); + + public setup(core: CoreSetup, deps: {}) { + // Set a custom status that will not emit immediately to force a timeout + core.status.set(this.status$); + + const router = core.http.createRouter(); + + router.post( + { + path: '/internal/status_plugin_a/status/set', + validate: { + query: schema.object({ + level: schema.oneOf([ + schema.literal('available'), + schema.literal('degraded'), + schema.literal('unavailable'), + schema.literal('critical'), + ]), + }), + }, + }, + (context, req, res) => { + const { level } = req.query; + + this.status$.next({ + level: ServiceStatusLevels[level], + summary: `statusPluginA is ${level}`, + }); + + return res.ok(); + } + ); + } + + public start() {} + public stop() {} +} diff --git a/test/server_integration/__fixtures__/plugins/status_plugin_a/tsconfig.json b/test/server_integration/__fixtures__/plugins/status_plugin_a/tsconfig.json new file mode 100644 index 0000000000000..5069db62589c7 --- /dev/null +++ b/test/server_integration/__fixtures__/plugins/status_plugin_a/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true, + "composite": true + }, + "include": [ + "index.ts", + "server/**/*.ts", + "../../../../../../typings/**/*", + ], + "exclude": [], + "references": [ + { "path": "../../../../../src/core/tsconfig.json" } + ] +} diff --git a/test/server_integration/__fixtures__/plugins/status_plugin_b/kibana.json b/test/server_integration/__fixtures__/plugins/status_plugin_b/kibana.json new file mode 100644 index 0000000000000..fa02f42d500af --- /dev/null +++ b/test/server_integration/__fixtures__/plugins/status_plugin_b/kibana.json @@ -0,0 +1,8 @@ +{ + "id": "statusPluginB", + "version": "0.0.1", + "kibanaVersion": "kibana", + "server": true, + "ui": false, + "requiredPlugins": ["statusPluginA"] +} diff --git a/test/server_integration/__fixtures__/plugins/status_plugin_b/package.json b/test/server_integration/__fixtures__/plugins/status_plugin_b/package.json new file mode 100644 index 0000000000000..3799d5d470754 --- /dev/null +++ b/test/server_integration/__fixtures__/plugins/status_plugin_b/package.json @@ -0,0 +1,14 @@ +{ + "name": "status_plugin_b", + "version": "1.0.0", + "main": "target/test/server_integration/__fixtures__/plugins/status_plugin_b", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "SSPL-1.0 OR Elastic License 2.0", + "scripts": { + "kbn": "node ../../../../../../scripts/kbn.js", + "build": "rm -rf './target' && ../../../../../../node_modules/.bin/tsc" + } +} \ No newline at end of file diff --git a/test/server_integration/__fixtures__/plugins/status_plugin_b/server/index.ts b/test/server_integration/__fixtures__/plugins/status_plugin_b/server/index.ts new file mode 100644 index 0000000000000..2002d234827b9 --- /dev/null +++ b/test/server_integration/__fixtures__/plugins/status_plugin_b/server/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { StatusPluginBPlugin } from './plugin'; + +export const plugin = () => new StatusPluginBPlugin(); diff --git a/test/server_integration/__fixtures__/plugins/status_plugin_b/server/plugin.ts b/test/server_integration/__fixtures__/plugins/status_plugin_b/server/plugin.ts new file mode 100644 index 0000000000000..191e8135f69a9 --- /dev/null +++ b/test/server_integration/__fixtures__/plugins/status_plugin_b/server/plugin.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Plugin } from 'kibana/server'; + +export class StatusPluginBPlugin implements Plugin { + public setup() {} + public start() {} + public stop() {} +} diff --git a/test/server_integration/__fixtures__/plugins/status_plugin_b/tsconfig.json b/test/server_integration/__fixtures__/plugins/status_plugin_b/tsconfig.json new file mode 100644 index 0000000000000..224aa42ef68d2 --- /dev/null +++ b/test/server_integration/__fixtures__/plugins/status_plugin_b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true, + "composite": true + }, + "include": [ + "index.ts", + "server/**/*.ts", + "../../../../../typings/**/*", + ], + "exclude": [], + "references": [ + { "path": "../../../../../src/core/tsconfig.json" } + ] +} diff --git a/test/server_integration/http/platform/config.status.ts b/test/server_integration/http/platform/config.status.ts new file mode 100644 index 0000000000000..8cc76c901f47c --- /dev/null +++ b/test/server_integration/http/platform/config.status.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import fs from 'fs'; +import path from 'path'; +import { FtrConfigProviderContext } from '@kbn/test'; + +/* + * These tests exist in a separate configuration because: + * 1) It must run as the first test after Kibana launches to clear the unavailable status. A separate config makes this + * easier to manage and prevent from breaking. + * 2) The other server_integration tests run against a built distributable, however the FTR does not support building + * and installing plugins against built Kibana. This test must be run against source only in order to build the + * fixture plugins + */ +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const httpConfig = await readConfigFile(require.resolve('../../config')); + + // Find all folders in __fixtures__/plugins since we treat all them as plugin folder + const allFiles = fs.readdirSync(path.resolve(__dirname, '../../__fixtures__/plugins')); + const plugins = allFiles.filter((file) => + fs.statSync(path.resolve(__dirname, '../../__fixtures__/plugins', file)).isDirectory() + ); + + return { + testFiles: [ + // Status test should be first to resolve manually created "unavailable" plugin + require.resolve('./status'), + ], + services: httpConfig.get('services'), + servers: httpConfig.get('servers'), + junit: { + reportName: 'Kibana Platform Status Integration Tests', + }, + esTestCluster: httpConfig.get('esTestCluster'), + kbnTestServer: { + ...httpConfig.get('kbnTestServer'), + serverArgs: [ + ...httpConfig.get('kbnTestServer.serverArgs'), + ...plugins.map( + (pluginDir) => + `--plugin-path=${path.resolve(__dirname, '../../__fixtures__/plugins', pluginDir)}` + ), + ], + runOptions: { + ...httpConfig.get('kbnTestServer.runOptions'), + // Don't wait for Kibana to be completely ready so that we can test the status timeouts + wait: /\[Kibana\]\[http\] http server running/, + }, + }, + }; +} diff --git a/test/server_integration/http/platform/status.ts b/test/server_integration/http/platform/status.ts new file mode 100644 index 0000000000000..0dcf82c9bea9e --- /dev/null +++ b/test/server_integration/http/platform/status.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import type { ServiceStatus, ServiceStatusLevels } from '../../../../src/core/server'; +import { FtrProviderContext } from '../../services/types'; + +type ServiceStatusSerialized = Omit & { level: string }; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const retry = getService('retry'); + + const getStatus = async (pluginName: string): Promise => { + const resp = await supertest.get('/api/status?v8format=true'); + + return resp.body.status.plugins[pluginName]; + }; + + const setStatus = async (level: T) => + supertest + .post(`/internal/status_plugin_a/status/set?level=${level}`) + .set('kbn-xsrf', 'xxx') + .expect(200); + + describe('status service', () => { + // This test must comes first because the timeout only applies to the initial emission + it("returns a timeout for status check that doesn't emit after 30s", async () => { + let aStatus = await getStatus('statusPluginA'); + expect(aStatus.level).to.eql('unavailable'); + + // Status will remain in unavailable until the custom status check times out + // Keep polling until that condition ends, up to a timeout + await retry.waitForWithTimeout(`Status check to timeout`, 40_000, async () => { + aStatus = await getStatus('statusPluginA'); + return aStatus.summary === 'Status check timed out after 30s'; + }); + + expect(aStatus.level).to.eql('unavailable'); + expect(aStatus.summary).to.eql('Status check timed out after 30s'); + }); + + it('propagates status issues to dependencies', async () => { + await setStatus('degraded'); + await retry.waitForWithTimeout( + `statusPluginA status to update`, + 5_000, + async () => (await getStatus('statusPluginA')).level === 'degraded' + ); + expect((await getStatus('statusPluginA')).level).to.eql('degraded'); + expect((await getStatus('statusPluginB')).level).to.eql('degraded'); + + await setStatus('available'); + await retry.waitForWithTimeout( + `statusPluginA status to update`, + 5_000, + async () => (await getStatus('statusPluginA')).level === 'available' + ); + expect((await getStatus('statusPluginA')).level).to.eql('available'); + expect((await getStatus('statusPluginB')).level).to.eql('available'); + }); + }); +} diff --git a/test/tsconfig.json b/test/tsconfig.json index 3e02283946080..8cf33d93a4067 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -17,7 +17,12 @@ "api_integration/apis/telemetry/fixtures/*.json", "api_integration/apis/telemetry/fixtures/*.json", ], - "exclude": ["target/**/*", "plugin_functional/plugins/**/*", "interpreter_functional/plugins/**/*"], + "exclude": [ + "target/**/*", + "interpreter_functional/plugins/**/*", + "plugin_functional/plugins/**/*", + "server_integration/__fixtures__/plugins/**/*", + ], "references": [ { "path": "../src/core/tsconfig.json" }, { "path": "../src/plugins/telemetry_management_section/tsconfig.json" }, @@ -52,5 +57,7 @@ { "path": "../src/plugins/visualize/tsconfig.json" }, { "path": "plugin_functional/plugins/core_app_status/tsconfig.json" }, { "path": "plugin_functional/plugins/core_provider_plugin/tsconfig.json" }, + { "path": "server_integration/__fixtures__/plugins/status_plugin_a/tsconfig.json" }, + { "path": "server_integration/__fixtures__/plugins/status_plugin_b/tsconfig.json" }, ] } diff --git a/test/visual_regression/tests/vega/vega_map_visualization.ts b/test/visual_regression/tests/vega/vega_map_visualization.ts index 96b08467e4a8f..d891e7f2bab6b 100644 --- a/test/visual_regression/tests/vega/vega_map_visualization.ts +++ b/test/visual_regression/tests/vega/vega_map_visualization.ts @@ -10,6 +10,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'visualize', 'visChart', 'visEditor', 'vegaChart']); const visualTesting = getService('visualTesting'); @@ -18,12 +19,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await esArchiver.loadIfNeeded( 'test/functional/fixtures/es_archiver/kibana_sample_data_flights' ); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/visualize'); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/visualize.json'); }); after(async () => { await esArchiver.unload('test/functional/fixtures/es_archiver/kibana_sample_data_flights'); - await esArchiver.unload('test/functional/fixtures/es_archiver/visualize'); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/visualize.json' + ); }); it('should show map with vega layer', async function () { diff --git a/x-pack/package.json b/x-pack/package.json index 1397a3da81072..1af3d569e41ab 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -26,7 +26,6 @@ "yarn": "^1.21.1" }, "devDependencies": { - "@kbn/plugin-helpers": "link:../packages/kbn-plugin-helpers", "@kbn/test": "link:../packages/kbn-test" } } \ No newline at end of file diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index 5b4a197eea462..b19e89a599840 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -19,7 +19,7 @@ Table of Contents - [Usage](#usage) - [Kibana Actions Configuration](#kibana-actions-configuration) - [Configuration Options](#configuration-options) - - [Adding Built-in Action Types to allowedHosts](#adding-built-in-action-types-to-allowedhosts) + - [**allowedHosts** configuration](#allowedhosts-configuration) - [Configuration Utilities](#configuration-utilities) - [Action types](#action-types) - [Methods](#methods) @@ -54,6 +54,9 @@ Table of Contents - [`subActionParams (getFields)`](#subactionparams-getfields-2) - [`subActionParams (incidentTypes)`](#subactionparams-incidenttypes) - [`subActionParams (severity)`](#subactionparams-severity) + - [Swimlane](#swimlane) + - [`params`](#params-3) + - [| severity | The severity of the incident. | string _(optional)_ |](#-severity-----the-severity-of-the-incident-----string-optional-) - [Command Line Utility](#command-line-utility) - [Developing New Action Types](#developing-new-action-types) - [licensing](#licensing) @@ -102,8 +105,8 @@ This module provides utilities for interacting with the configuration. | ensureUriAllowed | _uri_: The URI you wish to validate is allowed | Validates whether the URI is allowed. This checks the configuration and validates that the hostname of the URI is in the list of allowed Hosts and throws an error if it is not allowed. If the configuration says that all URI's are allowed (using an "\*") then it will never throw. | No return value, throws if URI isn't allowed | | ensureHostnameAllowed | _hostname_: The Hostname you wish to validate is allowed | Validates whether the Hostname is allowed. This checks the configuration and validates that the hostname is in the list of allowed Hosts and throws an error if it is not allowed. If the configuration says that all Hostnames are allowed (using an "\*") then it will never throw | No return value, throws if Hostname isn't allowed . | | ensureActionTypeEnabled | _actionType_: The actionType to check to see if it's enabled | Throws an error if the actionType is not enabled | No return value, throws if actionType isn't enabled | -| isRejectUnauthorizedCertificatesEnabled | _none_ | Returns value of `rejectUnauthorized` from configuration. | Boolean | -| getProxySettings | _none_ | If `proxyUrl` is set in the configuration, returns the proxy settings `proxyUrl`, `proxyHeaders` and `proxyRejectUnauthorizedCertificates`. Otherwise returns _undefined_. | Undefined or ProxySettings | +| isRejectUnauthorizedCertificatesEnabled | _none_ | Returns value of `rejectUnauthorized` from configuration. | Boolean | +| getProxySettings | _none_ | If `proxyUrl` is set in the configuration, returns the proxy settings `proxyUrl`, `proxyHeaders` and `proxyRejectUnauthorizedCertificates`. Otherwise returns _undefined_. | Undefined or ProxySettings | ## Action types @@ -113,17 +116,17 @@ This module provides utilities for interacting with the configuration. The following table describes the properties of the `options` object. -| Property | Description | Type | -| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -| id | Unique identifier for the action type. For convention, ids starting with `.` are reserved for built in action types. We recommend using a convention like `.mySpecialAction` for your action types. | string | -| name | A user-friendly name for the action type. These will be displayed in dropdowns when chosing action types. | string | -| maxAttempts | The maximum number of times this action will attempt to execute when scheduled. | number | -| minimumLicenseRequired | The license required to use the action type. | string | +| Property | Description | Type | +| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| id | Unique identifier for the action type. For convention, ids starting with `.` are reserved for built in action types. We recommend using a convention like `.mySpecialAction` for your action types. | string | +| name | A user-friendly name for the action type. These will be displayed in dropdowns when chosing action types. | string | +| maxAttempts | The maximum number of times this action will attempt to execute when scheduled. | number | +| minimumLicenseRequired | The license required to use the action type. | string | | validate.params | When developing an action type, it needs to accept parameters to know what to do with the action. (Example `to`, `from`, `subject`, `body` of an email). See the current built-in email action type for an example of the state-of-the-art validation.

    Technically, the value of this property should have a property named `validate()` which is a function that takes a params object to validate and returns a sanitized version of that object to pass to the execution function. Validation errors should be thrown from the `validate()` function and will be available as an error message | schema / validation function | -| validate.config | Similar to params, a config may be required when creating an action (for example `host` and `port` for an email server). | schema / validation function | -| validate.secrets | Similar to params, a secrets object may be required when creating an action (for example `user` and `password` for an email server). | schema / validation function | -| executor | This is where the code of an action type lives. This is a function gets called for executing an action from either alerting or manually by using the exposed function (see firing actions). For full details, see executor section below. | Function | -| renderParameterTemplates | Optionally define a function to provide custom rendering for this action type. | Function | +| validate.config | Similar to params, a config may be required when creating an action (for example `host` and `port` for an email server). | schema / validation function | +| validate.secrets | Similar to params, a secrets object may be required when creating an action (for example `user` and `password` for an email server). | schema / validation function | +| executor | This is where the code of an action type lives. This is a function gets called for executing an action from either alerting or manually by using the exposed function (see firing actions). For full details, see executor section below. | Function | +| renderParameterTemplates | Optionally define a function to provide custom rendering for this action type. | Function | **Important** - The config object is persisted in ElasticSearch and updated via the ElasticSearch update document API. This API allows "partial updates" - and this can cause issues with the encryption used on specified properties. So, a `validate()` function should return values for all configuration properties, so that partial updates do not occur. Setting property values to `null` rather than `undefined`, or not including a property in the config object, is all you need to do to ensure partial updates won't occur. @@ -133,15 +136,15 @@ This is the primary function for an action type. Whenever the action needs to ex **executor(options)** -| Property | Description | -| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| actionId | The action saved object id that the action type is executing for. | -| config | The action configuration. If you would like to validate the config before being passed to the executor, define `validate.config` within the action type. | -| secrets | The decrypted secrets object given to an action. This comes from the action saved object that is partially or fully encrypted within the data store. If you would like to validate the secrets object before being passed to the executor, define `validate.secrets` within the action type. | -| params | Parameters for the execution. These will be given at execution time by either an alert or manually provided when calling the plugin provided execute function. | -| services.scopedClusterClient | Use this to do Elasticsearch queries on the cluster Kibana connects to. Serves the same purpose as the normal IClusterClient, but exposes an additional `asCurrentUser` method that doesn't use credentials of the Kibana internal user (as `asInternalUser` does) to request Elasticsearch API, but rather passes HTTP headers extracted from the current user request to the API instead.| -| services.savedObjectsClient | This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.

    The scope of the saved objects client is tied to the user in context calling the execute API or the API key provided to the execute plugin function (only when security isenabled). | -| services.log(tags, [data], [timestamp]) | Use this to create server logs. (This is the same function as server.log) +| Property | Description | +| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| actionId | The action saved object id that the action type is executing for. | +| config | The action configuration. If you would like to validate the config before being passed to the executor, define `validate.config` within the action type. | +| secrets | The decrypted secrets object given to an action. This comes from the action saved object that is partially or fully encrypted within the data store. If you would like to validate the secrets object before being passed to the executor, define `validate.secrets` within the action type. | +| params | Parameters for the execution. These will be given at execution time by either an alert or manually provided when calling the plugin provided execute function. | +| services.scopedClusterClient | Use this to do Elasticsearch queries on the cluster Kibana connects to. Serves the same purpose as the normal IClusterClient, but exposes an additional `asCurrentUser` method that doesn't use credentials of the Kibana internal user (as `asInternalUser` does) to request Elasticsearch API, but rather passes HTTP headers extracted from the current user request to the API instead. | +| services.savedObjectsClient | This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.

    The scope of the saved objects client is tied to the user in context calling the execute API or the API key provided to the execute plugin function (only when security isenabled). | +| services.log(tags, [data], [timestamp]) | Use this to create server logs. (This is the same function as server.log) | ### Example @@ -262,16 +265,16 @@ The [ServiceNow user documentation `params`](https://www.elastic.co/guide/en/kib The following table describes the properties of the `incident` object. -| Property | Description | Type | -| ----------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------- | -| short_description | The title of the incident. | string | -| description | The description of the incident. | string _(optional)_ | +| Property | Description | Type | +| ----------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------- | +| short_description | The title of the incident. | string | +| description | The description of the incident. | string _(optional)_ | | externalId | The ID of the incident in ServiceNow. If present, the incident is updated. Otherwise, a new incident is created. | string _(optional)_ | -| severity | The severity in ServiceNow. | string _(optional)_ | -| urgency | The urgency in ServiceNow. | string _(optional)_ | -| impact | The impact in ServiceNow. | string _(optional)_ | -| category | The category in ServiceNow. | string _(optional)_ | -| subcategory | The subcategory in ServiceNow. | string _(optional)_ | +| severity | The severity in ServiceNow. | string _(optional)_ | +| urgency | The urgency in ServiceNow. | string _(optional)_ | +| impact | The impact in ServiceNow. | string _(optional)_ | +| category | The category in ServiceNow. | string _(optional)_ | +| subcategory | The subcategory in ServiceNow. | string _(optional)_ | #### `subActionParams (getFields)` @@ -311,20 +314,20 @@ The [Jira user documentation `params`](https://www.elastic.co/guide/en/kibana/ma The following table describes the properties of the `incident` object. -| Property | Description | Type | -| ----------- | ---------------------------------------------------------------------------------------------------------------- | --------------------- | -| summary | The title of the issue. | string | -| description | The description of the issue. | string _(optional)_ | +| Property | Description | Type | +| ----------- | ------------------------------------------------------------------------------------------------------- | --------------------- | +| summary | The title of the issue. | string | +| description | The description of the issue. | string _(optional)_ | | externalId | The ID of the issue in Jira. If present, the incident is updated. Otherwise, a new incident is created. | string _(optional)_ | -| issueType | The ID of the issue type in Jira. | string _(optional)_ | -| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ | -| labels | An array of labels. Labels cannot contain spaces. | string[] _(optional)_ | -| parent | The ID or key of the parent issue. Only for `Sub-task` issue types. | string _(optional)_ | +| issueType | The ID of the issue type in Jira. | string _(optional)_ | +| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ | +| labels | An array of labels. Labels cannot contain spaces. | string[] _(optional)_ | +| parent | The ID or key of the parent issue. Only for `Sub-task` issue types. | string _(optional)_ | #### `subActionParams (getIncident)` -| Property | Description | Type | -| ---------- | --------------------------- | ------ | +| Property | Description | Type | +| ---------- | ---------------------------- | ------ | | externalId | The ID of the issue in Jira. | string | #### `subActionParams (issueTypes)` @@ -333,20 +336,20 @@ No parameters for the `issueTypes` subaction. Provide an empty object `{}`. #### `subActionParams (fieldsByIssueType)` -| Property | Description | Type | -| -------- | -------------------------------- | ------ | +| Property | Description | Type | +| -------- | --------------------------------- | ------ | | id | The ID of the issue type in Jira. | string | #### `subActionParams (issues)` -| Property | Description | Type | -| -------- | ----------------------- | ------ | +| Property | Description | Type | +| -------- | ------------------------ | ------ | | title | The title to search for. | string | #### `subActionParams (issue)` -| Property | Description | Type | -| -------- | --------------------------- | ------ | +| Property | Description | Type | +| -------- | ---------------------------- | ------ | | id | The ID of the issue in Jira. | string | #### `subActionParams (getFields)` @@ -360,10 +363,10 @@ The [IBM Resilient user documentation `params`](https://www.elastic.co/guide/en/ ### `params` -| Property | Description | Type | -| --------------- | -------------------------------------------------------------------------------------------------- | ------ | +| Property | Description | Type | +| --------------- | ------------------------------------------------------------------------------------------------- | ------ | | subAction | The subaction to perform. It can be `pushToService`, `getFields`, `incidentTypes`, and `severity. | string | -| subActionParams | The parameters of the subaction. | object | +| subActionParams | The parameters of the subaction. | object | #### `subActionParams (pushToService)` @@ -374,13 +377,13 @@ The [IBM Resilient user documentation `params`](https://www.elastic.co/guide/en/ The following table describes the properties of the `incident` object. -| Property | Description | Type | -| ------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| name | The title of the incident. | string _(optional)_ | -| description | The description of the incident. | string _(optional)_ | +| Property | Description | Type | +| ------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------- | +| name | The title of the incident. | string _(optional)_ | +| description | The description of the incident. | string _(optional)_ | | externalId | The ID of the incident in IBM Resilient. If present, the incident is updated. Otherwise, a new incident is created. | string _(optional)_ | -| incidentTypes | An array with the IDs of IBM Resilient incident types. | number[] _(optional)_ | -| severityCode | IBM Resilient ID of the severity code. | number _(optional)_ | +| incidentTypes | An array with the IDs of IBM Resilient incident types. | number[] _(optional)_ | +| severityCode | IBM Resilient ID of the severity code. | number _(optional)_ | #### `subActionParams (getFields)` @@ -394,6 +397,36 @@ No parameters for the `incidentTypes` subaction. Provide an empty object `{}`. No parameters for the `severity` subaction. Provide an empty object `{}`. +--- +## Swimlane + + +### `params` + +| Property | Description | Type | +| --------------- | ---------------------------------------------------- | ------ | +| subAction | The subaction to perform. It can be `pushToService`. | string | +| subActionParams | The parameters of the subaction. | object | + + +`subActionParams (pushToService)` + +| Property | Description | Type | +| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- | +| incident | The Swimlane incident. | object | +| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ | + + +The following table describes the properties of the `incident` object. + +| Property | Description | Type | +| ----------- | -------------------------------- | ------------------- | +| alertId | The alert id. | string _(optional)_ | +| caseId | The case id of the incident. | string _(optional)_ | +| caseName | The case name of the incident. | string _(optional)_ | +| description | The description of the incident. | string _(optional)_ | +| ruleName | The rule name. | string _(optional)_ | +| severity | The severity of the incident. | string _(optional)_ | --- # Command Line Utility diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 16388b2faf52e..012cd1a58de7e 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -429,7 +429,7 @@ describe('create()', () => { idleInterval: schema.duration().validate('1h'), pageSize: 100, }, - tls: { + ssl: { verificationMode: 'full', proxyVerificationMode: 'full', }, diff --git a/x-pack/plugins/actions/server/actions_config.mock.ts b/x-pack/plugins/actions/server/actions_config.mock.ts index 19a43951377b6..36298d84acabc 100644 --- a/x-pack/plugins/actions/server/actions_config.mock.ts +++ b/x-pack/plugins/actions/server/actions_config.mock.ts @@ -15,7 +15,7 @@ const createActionsConfigMock = () => { ensureHostnameAllowed: jest.fn().mockReturnValue({}), ensureUriAllowed: jest.fn().mockReturnValue({}), ensureActionTypeEnabled: jest.fn().mockReturnValue({}), - getTLSSettings: jest.fn().mockReturnValue({ + getSSLSettings: jest.fn().mockReturnValue({ verificationMode: 'full', }), getProxySettings: jest.fn().mockReturnValue(undefined), diff --git a/x-pack/plugins/actions/server/actions_config.test.ts b/x-pack/plugins/actions/server/actions_config.test.ts index 93dad226e0c99..51cd9e5599472 100644 --- a/x-pack/plugins/actions/server/actions_config.test.ts +++ b/x-pack/plugins/actions/server/actions_config.test.ts @@ -37,7 +37,7 @@ const defaultActionsConfig: ActionsConfig = { idleInterval: schema.duration().validate('1h'), pageSize: 100, }, - tls: { + ssl: { proxyVerificationMode: 'full', verificationMode: 'full', }, @@ -316,38 +316,38 @@ describe('getProxySettings', () => { proxyRejectUnauthorizedCertificates: true, }; let proxySettings = getActionsConfigurationUtilities(configTrue).getProxySettings(); - expect(proxySettings?.proxyTLSSettings.verificationMode).toBe('full'); + expect(proxySettings?.proxySSLSettings.verificationMode).toBe('full'); const configFalse: ActionsConfig = { ...defaultActionsConfig, proxyUrl: 'https://proxy.elastic.co', proxyRejectUnauthorizedCertificates: false, - tls: {}, + ssl: {}, }; proxySettings = getActionsConfigurationUtilities(configFalse).getProxySettings(); - expect(proxySettings?.proxyTLSSettings.verificationMode).toBe('none'); + expect(proxySettings?.proxySSLSettings.verificationMode).toBe('none'); }); - test('returns proper verificationMode value, based on the TLS proxy configuration', () => { + test('returns proper verificationMode value, based on the SSL proxy configuration', () => { const configTrue: ActionsConfig = { ...defaultActionsConfig, proxyUrl: 'https://proxy.elastic.co', - tls: { + ssl: { proxyVerificationMode: 'full', }, }; let proxySettings = getActionsConfigurationUtilities(configTrue).getProxySettings(); - expect(proxySettings?.proxyTLSSettings.verificationMode).toBe('full'); + expect(proxySettings?.proxySSLSettings.verificationMode).toBe('full'); const configFalse: ActionsConfig = { ...defaultActionsConfig, proxyUrl: 'https://proxy.elastic.co', - tls: { + ssl: { proxyVerificationMode: 'none', }, }; proxySettings = getActionsConfigurationUtilities(configFalse).getProxySettings(); - expect(proxySettings?.proxyTLSSettings.verificationMode).toBe('none'); + expect(proxySettings?.proxySSLSettings.verificationMode).toBe('none'); }); test('returns proxy headers', () => { @@ -432,13 +432,13 @@ describe('getProxySettings', () => { customHostSettings: [ { url: 'https://elastic.co', - tls: { + ssl: { verificationMode: 'full', }, }, { url: 'smtp://elastic.co:123', - tls: { + ssl: { verificationMode: 'none', }, smtp: { @@ -465,24 +465,24 @@ describe('getProxySettings', () => { }); }); -describe('getTLSSettings', () => { - test('returns proper verificationMode value, based on the TLS proxy configuration', () => { +describe('getSSLSettings', () => { + test('returns proper verificationMode value, based on the SSL proxy configuration', () => { const configTrue: ActionsConfig = { ...defaultActionsConfig, - tls: { + ssl: { verificationMode: 'full', }, }; - let tlsSettings = getActionsConfigurationUtilities(configTrue).getTLSSettings(); - expect(tlsSettings.verificationMode).toBe('full'); + let sslSettings = getActionsConfigurationUtilities(configTrue).getSSLSettings(); + expect(sslSettings.verificationMode).toBe('full'); const configFalse: ActionsConfig = { ...defaultActionsConfig, - tls: { + ssl: { verificationMode: 'none', }, }; - tlsSettings = getActionsConfigurationUtilities(configFalse).getTLSSettings(); - expect(tlsSettings.verificationMode).toBe('none'); + sslSettings = getActionsConfigurationUtilities(configFalse).getSSLSettings(); + expect(sslSettings.verificationMode).toBe('none'); }); }); diff --git a/x-pack/plugins/actions/server/actions_config.ts b/x-pack/plugins/actions/server/actions_config.ts index d25101f8279f8..9ce9439b726d4 100644 --- a/x-pack/plugins/actions/server/actions_config.ts +++ b/x-pack/plugins/actions/server/actions_config.ts @@ -14,8 +14,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { ActionsConfig, AllowedHosts, EnabledActionTypes, CustomHostSettings } from './config'; import { getCanonicalCustomHostUrl } from './lib/custom_host_settings'; import { ActionTypeDisabledError } from './lib'; -import { ProxySettings, ResponseSettings, TLSSettings } from './types'; -import { getTLSSettingsFromConfig } from './builtin_action_types/lib/get_node_tls_options'; +import { ProxySettings, ResponseSettings, SSLSettings } from './types'; +import { getSSLSettingsFromConfig } from './builtin_action_types/lib/get_node_ssl_options'; export { AllowedHosts, EnabledActionTypes } from './config'; @@ -31,7 +31,7 @@ export interface ActionsConfigurationUtilities { ensureHostnameAllowed: (hostname: string) => void; ensureUriAllowed: (uri: string) => void; ensureActionTypeEnabled: (actionType: string) => void; - getTLSSettings: () => TLSSettings; + getSSLSettings: () => SSLSettings; getProxySettings: () => undefined | ProxySettings; getResponseSettings: () => ResponseSettings; getCustomHostSettings: (targetUrl: string) => CustomHostSettings | undefined; @@ -94,8 +94,8 @@ function getProxySettingsFromConfig(config: ActionsConfig): undefined | ProxySet proxyBypassHosts: arrayAsSet(config.proxyBypassHosts), proxyOnlyHosts: arrayAsSet(config.proxyOnlyHosts), proxyHeaders: config.proxyHeaders, - proxyTLSSettings: getTLSSettingsFromConfig( - config.tls?.proxyVerificationMode, + proxySSLSettings: getSSLSettingsFromConfig( + config.ssl?.proxyVerificationMode, config.proxyRejectUnauthorizedCertificates ), }; @@ -146,8 +146,8 @@ export function getActionsConfigurationUtilities( isActionTypeEnabled, getProxySettings: () => getProxySettingsFromConfig(config), getResponseSettings: () => getResponseSettingsFromConfig(config), - getTLSSettings: () => - getTLSSettingsFromConfig(config.tls?.verificationMode, config.rejectUnauthorized), + getSSLSettings: () => + getSSLSettingsFromConfig(config.ssl?.verificationMode, config.rejectUnauthorized), ensureUriAllowed(uri: string) { if (!isUriAllowed(uri)) { throw new Error(allowListErrorMessage(AllowListingField.URL, uri)); diff --git a/x-pack/plugins/actions/server/builtin_action_types/email.test.ts b/x-pack/plugins/actions/server/builtin_action_types/email.test.ts index 98ea436b17f3e..8e9ea1c5e4aa9 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/email.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/email.test.ts @@ -285,7 +285,7 @@ describe('execute()', () => { "getCustomHostSettings": [MockFunction], "getProxySettings": [MockFunction], "getResponseSettings": [MockFunction], - "getTLSSettings": [MockFunction], + "getSSLSettings": [MockFunction], "isActionTypeEnabled": [MockFunction], "isHostnameAllowed": [MockFunction], "isUriAllowed": [MockFunction], @@ -346,7 +346,7 @@ describe('execute()', () => { "getCustomHostSettings": [MockFunction], "getProxySettings": [MockFunction], "getResponseSettings": [MockFunction], - "getTLSSettings": [MockFunction], + "getSSLSettings": [MockFunction], "isActionTypeEnabled": [MockFunction], "isHostnameAllowed": [MockFunction], "isUriAllowed": [MockFunction], diff --git a/x-pack/plugins/actions/server/builtin_action_types/index.test.ts b/x-pack/plugins/actions/server/builtin_action_types/index.test.ts index 10955af2f3b13..5feb47ea6c962 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/index.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/index.test.ts @@ -21,6 +21,7 @@ const ACTION_TYPE_IDS = [ '.pagerduty', '.server-log', '.slack', + '.swimlane', '.teams', '.webhook', ]; diff --git a/x-pack/plugins/actions/server/builtin_action_types/index.ts b/x-pack/plugins/actions/server/builtin_action_types/index.ts index 551d3d02ff05d..07859cba4c371 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/index.ts @@ -12,6 +12,7 @@ import { Logger } from '../../../../../src/core/server'; import { getActionType as getEmailActionType } from './email'; import { getActionType as getIndexActionType } from './es_index'; import { getActionType as getPagerDutyActionType } from './pagerduty'; +import { getActionType as getSwimlaneActionType } from './swimlane'; import { getActionType as getServerLogActionType } from './server_log'; import { getActionType as getSlackActionType } from './slack'; import { getActionType as getWebhookActionType } from './webhook'; @@ -65,6 +66,7 @@ export function registerBuiltInActionTypes({ ); actionTypeRegistry.register(getIndexActionType({ logger })); actionTypeRegistry.register(getPagerDutyActionType({ logger, configurationUtilities })); + actionTypeRegistry.register(getSwimlaneActionType({ logger, configurationUtilities })); actionTypeRegistry.register(getServerLogActionType({ logger })); actionTypeRegistry.register(getSlackActionType({ logger, configurationUtilities })); actionTypeRegistry.register(getWebhookActionType({ logger, configurationUtilities })); diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts index 3161e97583b72..aa439787ad96f 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts @@ -25,7 +25,7 @@ import { JiraSecretConfigurationType, JiraExecutorResultData, ExecutorSubActionGetFieldsByIssueTypeParams, - ExecutorSubActionGetIssueTypesParams, + ExecutorSubActionCommonFieldsParams, ExecutorSubActionGetIssuesParams, ExecutorSubActionGetIssueParams, ExecutorSubActionGetIncidentParams, @@ -137,7 +137,7 @@ async function executor( } if (subAction === 'issueTypes') { - const getIssueTypesParams = subActionParams as ExecutorSubActionGetIssueTypesParams; + const getIssueTypesParams = subActionParams as ExecutorSubActionCommonFieldsParams; data = await api.issueTypes({ externalService, params: getIssueTypesParams, diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts index a81dfaeef8175..eb2f540deaa9a 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts @@ -25,14 +25,6 @@ export const ExternalIncidentServiceSecretConfigurationSchema = schema.object( ExternalIncidentServiceSecretConfiguration ); -export const ExecutorSubActionSchema = schema.oneOf([ - schema.literal('getIncident'), - schema.literal('pushToService'), - schema.literal('handshake'), - schema.literal('issueTypes'), - schema.literal('fieldsByIssueType'), -]); - export const ExecutorSubActionPushParamsSchema = schema.object({ incident: schema.object({ summary: schema.string(), diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts index f6462bac9d83e..9430d734287d3 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts @@ -155,12 +155,12 @@ describe('Jira service', () => { ).toThrow(); }); - test('throws without username', () => { + test('throws without email/username', () => { expect(() => createExternalService( { - config: { apiUrl: 'test.com' }, - secrets: { apiToken: '', email: 'elastic@elastic.com' }, + config: { apiUrl: 'test.com', projectKey: 'CK' }, + secrets: { apiToken: 'token' }, }, logger, configurationUtilities @@ -168,12 +168,12 @@ describe('Jira service', () => { ).toThrow(); }); - test('throws without password', () => { + test('throws without apiToken/password', () => { expect(() => createExternalService( { - config: { apiUrl: 'test.com' }, - secrets: { apiToken: '', email: undefined }, + config: { apiUrl: 'test.com', projectKey: 'CK' }, + secrets: { email: 'elastic@elastic.com' }, }, logger, configurationUtilities diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts index 89a5551554c4a..74d53901d55d9 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts @@ -16,10 +16,10 @@ import { ExecutorSubActionGetIncidentParamsSchema, ExecutorSubActionHandshakeParamsSchema, ExecutorSubActionGetCapabilitiesParamsSchema, - ExecutorSubActionGetIssueTypesParamsSchema, ExecutorSubActionGetFieldsByIssueTypeParamsSchema, ExecutorSubActionGetIssuesParamsSchema, ExecutorSubActionGetIssueParamsSchema, + ExecutorSubActionCommonFieldsParamsSchema, } from './schema'; import { ActionsConfigurationUtilities } from '../../actions_config'; import { Logger } from '../../../../../../src/core/server'; @@ -124,8 +124,8 @@ export type ExecutorSubActionGetCapabilitiesParams = TypeOf< typeof ExecutorSubActionGetCapabilitiesParamsSchema >; -export type ExecutorSubActionGetIssueTypesParams = TypeOf< - typeof ExecutorSubActionGetIssueTypesParamsSchema +export type ExecutorSubActionCommonFieldsParams = TypeOf< + typeof ExecutorSubActionCommonFieldsParamsSchema >; export type ExecutorSubActionGetFieldsByIssueTypeParams = TypeOf< @@ -157,12 +157,12 @@ export interface HandshakeApiHandlerArgs extends ExternalServiceApiHandlerArgs { export interface GetIssueTypesHandlerArgs { externalService: ExternalService; - params: ExecutorSubActionGetIssueTypesParams; + params: ExecutorSubActionCommonFieldsParams; } export interface GetCommonFieldsHandlerArgs { externalService: ExternalService; - params: ExecutorSubActionGetIssueTypesParams; + params: ExecutorSubActionCommonFieldsParams; } export interface GetFieldsByIssueTypeHandlerArgs { diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.test.ts index ccd5a044971df..292471aaf9b6d 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.test.ts @@ -75,7 +75,7 @@ describe('request', () => { test('it have been called with proper proxy agent for a valid url', async () => { configurationUtilities.getProxySettings.mockReturnValue({ - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'full', }, proxyUrl: 'https://localhost:1212', @@ -110,7 +110,7 @@ describe('request', () => { test('it have been called with proper proxy agent for an invalid url', async () => { configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: ':nope:', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -141,7 +141,7 @@ describe('request', () => { test('it bypasses with proxyBypassHosts when expected', async () => { configurationUtilities.getProxySettings.mockReturnValue({ - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'full', }, proxyUrl: 'https://elastic.proxy.co', @@ -164,7 +164,7 @@ describe('request', () => { test('it does not bypass with proxyBypassHosts when expected', async () => { configurationUtilities.getProxySettings.mockReturnValue({ - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'full', }, proxyUrl: 'https://elastic.proxy.co', @@ -187,7 +187,7 @@ describe('request', () => { test('it proxies with proxyOnlyHosts when expected', async () => { configurationUtilities.getProxySettings.mockReturnValue({ - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'full', }, proxyUrl: 'https://elastic.proxy.co', @@ -210,7 +210,7 @@ describe('request', () => { test('it does not proxy with proxyOnlyHosts when expected', async () => { configurationUtilities.getProxySettings.mockReturnValue({ - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'full', }, proxyUrl: 'https://elastic.proxy.co', diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils_connection.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils_connection.test.ts index 235fca005e225..4ed9485e923a7 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils_connection.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils_connection.test.ts @@ -86,7 +86,7 @@ describe('axios connections', () => { testServer = server; const configurationUtilities = getACUfromConfig({ - tls: { + ssl: { verificationMode: 'none', }, }); @@ -99,7 +99,7 @@ describe('axios connections', () => { testServer = server; const configurationUtilities = getACUfromConfig({ - customHostSettings: [{ url, tls: { verificationMode: 'none' } }], + customHostSettings: [{ url, ssl: { verificationMode: 'none' } }], }); const res = await request({ axios, url, logger, configurationUtilities }); expect(res.status).toBe(200); @@ -110,7 +110,7 @@ describe('axios connections', () => { testServer = server; const configurationUtilities = getACUfromConfig({ - customHostSettings: [{ url, tls: { certificateAuthoritiesData: CA } }], + customHostSettings: [{ url, ssl: { certificateAuthoritiesData: CA } }], }); const res = await request({ axios, url, logger, configurationUtilities }); expect(res.status).toBe(200); @@ -121,7 +121,7 @@ describe('axios connections', () => { testServer = server; const configurationUtilities = getACUfromConfig({ - customHostSettings: [{ url, tls: { certificateAuthoritiesData: KIBANA_CRT } }], + customHostSettings: [{ url, ssl: { certificateAuthoritiesData: KIBANA_CRT } }], }); const fn = async () => await request({ axios, url, logger, configurationUtilities }); await expect(fn()).rejects.toThrow('certificate'); @@ -135,7 +135,7 @@ describe('axios connections', () => { customHostSettings: [ { url, - tls: { + ssl: { certificateAuthoritiesData: CA, verificationMode: 'none', }, @@ -151,13 +151,13 @@ describe('axios connections', () => { testServer = server; const configurationUtilities = getACUfromConfig({ - tls: { + ssl: { verificationMode: 'none', }, customHostSettings: [ { url, - tls: { + ssl: { certificateAuthoritiesData: CA, }, }, @@ -173,7 +173,7 @@ describe('axios connections', () => { testServer = server; const configurationUtilities = getACUfromConfig({ - customHostSettings: [{ url: otherUrl, tls: { verificationMode: 'none' } }], + customHostSettings: [{ url: otherUrl, ssl: { verificationMode: 'none' } }], }); const fn = async () => await request({ axios, url, logger, configurationUtilities }); await expect(fn()).rejects.toThrow('certificate'); @@ -184,7 +184,7 @@ describe('axios connections', () => { testServer = server; const configurationUtilities = getACUfromConfig({ - customHostSettings: [{ url, tls: { certificateAuthoritiesData: 'garbage' } }], + customHostSettings: [{ url, ssl: { certificateAuthoritiesData: 'garbage' } }], }); const fn = async () => await request({ axios, url, logger, configurationUtilities }); await expect(fn()).rejects.toThrow('certificate'); @@ -196,7 +196,7 @@ describe('axios connections', () => { const ca = '-----BEGIN CERTIFICATE-----\ngarbage\n-----END CERTIFICATE-----\n'; const configurationUtilities = getACUfromConfig({ - customHostSettings: [{ url, tls: { certificateAuthoritiesData: ca } }], + customHostSettings: [{ url, ssl: { certificateAuthoritiesData: ca } }], }); const fn = async () => await request({ axios, url, logger, configurationUtilities }); await expect(fn()).rejects.toThrow('certificate'); @@ -255,7 +255,7 @@ const BaseActionsConfig: ActionsConfig = { proxyUrl: undefined, proxyHeaders: undefined, proxyRejectUnauthorizedCertificates: true, - tls: { + ssl: { proxyVerificationMode: 'full', verificationMode: 'full', }, diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/get_custom_agents.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/get_custom_agents.test.ts index 8b4abe86e271a..0c1112da5909f 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/get_custom_agents.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/get_custom_agents.test.ts @@ -30,7 +30,7 @@ describe('getCustomAgents', () => { test('get agents for valid proxy URL', () => { configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -44,7 +44,7 @@ describe('getCustomAgents', () => { test('return default agents for invalid proxy URL', () => { configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: ':nope: not a valid URL', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -64,7 +64,7 @@ describe('getCustomAgents', () => { test('returns non-proxy agents for matching proxyBypassHosts', () => { configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: new Set([targetHost]), @@ -78,7 +78,7 @@ describe('getCustomAgents', () => { test('returns proxy agents for non-matching proxyBypassHosts', () => { configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: new Set([targetHost]), @@ -96,7 +96,7 @@ describe('getCustomAgents', () => { test('returns proxy agents for matching proxyOnlyHosts', () => { configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -110,7 +110,7 @@ describe('getCustomAgents', () => { test('returns non-proxy agents for non-matching proxyOnlyHosts', () => { configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -128,7 +128,7 @@ describe('getCustomAgents', () => { test('handles custom host settings', () => { configurationUtilities.getCustomHostSettings.mockReturnValue({ url: targetUrlCanonical, - tls: { + ssl: { verificationMode: 'none', certificateAuthoritiesData: 'ca data here', }, @@ -141,7 +141,7 @@ describe('getCustomAgents', () => { test('handles custom host settings with proxy', () => { configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -149,7 +149,7 @@ describe('getCustomAgents', () => { }); configurationUtilities.getCustomHostSettings.mockReturnValue({ url: targetUrlCanonical, - tls: { + ssl: { verificationMode: 'none', certificateAuthoritiesData: 'ca data here', }, @@ -163,12 +163,12 @@ describe('getCustomAgents', () => { }); test('handles overriding global verificationMode "none"', () => { - configurationUtilities.getTLSSettings.mockReturnValue({ + configurationUtilities.getSSLSettings.mockReturnValue({ verificationMode: 'none', }); configurationUtilities.getCustomHostSettings.mockReturnValue({ url: targetUrlCanonical, - tls: { + ssl: { verificationMode: 'certificate', }, }); @@ -181,12 +181,12 @@ describe('getCustomAgents', () => { }); test('handles overriding global verificationMode "full"', () => { - configurationUtilities.getTLSSettings.mockReturnValue({ + configurationUtilities.getSSLSettings.mockReturnValue({ verificationMode: 'full', }); configurationUtilities.getCustomHostSettings.mockReturnValue({ url: targetUrlCanonical, - tls: { + ssl: { verificationMode: 'none', }, }); @@ -199,12 +199,12 @@ describe('getCustomAgents', () => { }); test('handles overriding global verificationMode "none" with a proxy', () => { - configurationUtilities.getTLSSettings.mockReturnValue({ + configurationUtilities.getSSLSettings.mockReturnValue({ verificationMode: 'none', }); configurationUtilities.getCustomHostSettings.mockReturnValue({ url: targetUrlCanonical, - tls: { + ssl: { verificationMode: 'full', }, }); @@ -212,7 +212,7 @@ describe('getCustomAgents', () => { proxyUrl: 'https://someproxyhost', // note: this setting doesn't come into play, it's for the connection to // the proxy, not the target url - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -226,12 +226,12 @@ describe('getCustomAgents', () => { }); test('handles overriding global verificationMode "full" with a proxy', () => { - configurationUtilities.getTLSSettings.mockReturnValue({ + configurationUtilities.getSSLSettings.mockReturnValue({ verificationMode: 'full', }); configurationUtilities.getCustomHostSettings.mockReturnValue({ url: targetUrlCanonical, - tls: { + ssl: { verificationMode: 'none', }, }); @@ -239,7 +239,7 @@ describe('getCustomAgents', () => { proxyUrl: 'https://someproxyhost', // note: this setting doesn't come into play, it's for the connection to // the proxy, not the target url - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/get_custom_agents.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/get_custom_agents.ts index a327ee3ffe931..83d31ae1355d3 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/get_custom_agents.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/get_custom_agents.ts @@ -11,7 +11,7 @@ import HttpProxyAgent from 'http-proxy-agent'; import { HttpsProxyAgent } from 'https-proxy-agent'; import { Logger } from '../../../../../../src/core/server'; import { ActionsConfigurationUtilities } from '../../actions_config'; -import { getNodeTLSOptions, getTLSSettingsFromConfig } from './get_node_tls_options'; +import { getNodeSSLOptions, getSSLSettingsFromConfig } from './get_node_ssl_options'; interface GetCustomAgentsResponse { httpAgent: HttpAgent | undefined; @@ -23,14 +23,14 @@ export function getCustomAgents( logger: Logger, url: string ): GetCustomAgentsResponse { - const generalTLSSettings = configurationUtilities.getTLSSettings(); - const agentTLSOptions = getNodeTLSOptions(logger, generalTLSSettings.verificationMode); + const generalSSLSettings = configurationUtilities.getSSLSettings(); + const agentSSLOptions = getNodeSSLOptions(logger, generalSSLSettings.verificationMode); // the default for rejectUnauthorized is the global setting, which can // be overridden (below) with a custom host setting const defaultAgents = { httpAgent: undefined, httpsAgent: new HttpsAgent({ - ...agentTLSOptions, + ...agentSSLOptions, }), }; @@ -43,28 +43,28 @@ export function getCustomAgents( } // update the defaultAgents.httpsAgent if configured - const tlsSettings = customHostSettings?.tls; + const sslSettings = customHostSettings?.ssl; let agentOptions: AgentOptions | undefined; - if (tlsSettings) { + if (sslSettings) { logger.debug(`Creating customized connection settings for: ${url}`); agentOptions = defaultAgents.httpsAgent.options; - if (tlsSettings.certificateAuthoritiesData) { - agentOptions.ca = tlsSettings.certificateAuthoritiesData; + if (sslSettings.certificateAuthoritiesData) { + agentOptions.ca = sslSettings.certificateAuthoritiesData; } - const tlsSettingsFromConfig = getTLSSettingsFromConfig( - tlsSettings.verificationMode, - tlsSettings.rejectUnauthorized + const sslSettingsFromConfig = getSSLSettingsFromConfig( + sslSettings.verificationMode, + sslSettings.rejectUnauthorized ); // see: src/core/server/elasticsearch/legacy/elasticsearch_client_config.ts // This is where the global rejectUnauthorized is overridden by a custom host - const customHostNodeTLSOptions = getNodeTLSOptions( + const customHostNodeSSLOptions = getNodeSSLOptions( logger, - tlsSettingsFromConfig.verificationMode + sslSettingsFromConfig.verificationMode ); - if (customHostNodeTLSOptions.rejectUnauthorized !== undefined) { - agentOptions.rejectUnauthorized = customHostNodeTLSOptions.rejectUnauthorized; + if (customHostNodeSSLOptions.rejectUnauthorized !== undefined) { + agentOptions.rejectUnauthorized = customHostNodeSSLOptions.rejectUnauthorized; } } @@ -107,12 +107,12 @@ export function getCustomAgents( return defaultAgents; } - const proxyNodeTLSOptions = getNodeTLSOptions( + const proxyNodeSSLOptions = getNodeSSLOptions( logger, - proxySettings.proxyTLSSettings.verificationMode + proxySettings.proxySSLSettings.verificationMode ); // At this point, we are going to use a proxy, so we need new agents. - // We will though, copy over the calculated tls options from above, into + // We will though, copy over the calculated ssl options from above, into // the https agent. const httpAgent = new HttpProxyAgent(proxySettings.proxyUrl); const httpsAgent = (new HttpsProxyAgent({ @@ -121,7 +121,7 @@ export function getCustomAgents( protocol: proxyUrl.protocol, headers: proxySettings.proxyHeaders, // do not fail on invalid certs if value is false - ...proxyNodeTLSOptions, + ...proxyNodeSSLOptions, }) as unknown) as HttpsAgent; // vsCode wasn't convinced HttpsProxyAgent is an https.Agent, so we convinced it diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/get_node_tls_options.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/get_node_ssl_options.test.ts similarity index 67% rename from x-pack/plugins/actions/server/builtin_action_types/lib/get_node_tls_options.test.ts rename to x-pack/plugins/actions/server/builtin_action_types/lib/get_node_ssl_options.test.ts index 7d131985053f1..893191b2ca2b4 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/get_node_tls_options.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/get_node_ssl_options.test.ts @@ -4,35 +4,35 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { getNodeTLSOptions, getTLSSettingsFromConfig } from './get_node_tls_options'; +import { getNodeSSLOptions, getSSLSettingsFromConfig } from './get_node_ssl_options'; import { Logger } from '../../../../../../src/core/server'; import { loggingSystemMock } from '../../../../../../src/core/server/mocks'; const logger = loggingSystemMock.create().get() as jest.Mocked; -describe('getNodeTLSOptions', () => { - test('get node.js TLS options: rejectUnauthorized eql true for the verification mode "full"', () => { - const nodeOption = getNodeTLSOptions(logger, 'full'); +describe('getNodeSSLOptions', () => { + test('get node.js SSL options: rejectUnauthorized eql true for the verification mode "full"', () => { + const nodeOption = getNodeSSLOptions(logger, 'full'); expect(nodeOption).toMatchObject({ rejectUnauthorized: true, }); }); - test('get node.js TLS options: rejectUnauthorized eql true for the verification mode "certificate"', () => { - const nodeOption = getNodeTLSOptions(logger, 'certificate'); + test('get node.js SSL options: rejectUnauthorized eql true for the verification mode "certificate"', () => { + const nodeOption = getNodeSSLOptions(logger, 'certificate'); expect(nodeOption.checkServerIdentity).not.toBeNull(); expect(nodeOption.rejectUnauthorized).toBeTruthy(); }); - test('get node.js TLS options: rejectUnauthorized eql false for the verification mode "none"', () => { - const nodeOption = getNodeTLSOptions(logger, 'none'); + test('get node.js SSL options: rejectUnauthorized eql false for the verification mode "none"', () => { + const nodeOption = getNodeSSLOptions(logger, 'none'); expect(nodeOption).toMatchObject({ rejectUnauthorized: false, }); }); - test('get node.js TLS options: rejectUnauthorized eql true for the verification mode value which does not exist, the logger called with the proper warning message', () => { - const nodeOption = getNodeTLSOptions(logger, 'notexist'); + test('get node.js SSL options: rejectUnauthorized eql true for the verification mode value which does not exist, the logger called with the proper warning message', () => { + const nodeOption = getNodeSSLOptions(logger, 'notexist'); expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(` Array [ Array [ @@ -46,23 +46,23 @@ describe('getNodeTLSOptions', () => { }); }); -describe('getTLSSettingsFromConfig', () => { +describe('getSSLSettingsFromConfig', () => { test('get verificationMode eql "none" if legacy rejectUnauthorized eql false', () => { - const nodeOption = getTLSSettingsFromConfig(undefined, false); + const nodeOption = getSSLSettingsFromConfig(undefined, false); expect(nodeOption).toMatchObject({ verificationMode: 'none', }); }); test('get verificationMode eql "none" if legacy rejectUnauthorized eql true', () => { - const nodeOption = getTLSSettingsFromConfig(undefined, true); + const nodeOption = getSSLSettingsFromConfig(undefined, true); expect(nodeOption).toMatchObject({ verificationMode: 'full', }); }); test('get verificationMode eql "certificate", ignore rejectUnauthorized', () => { - const nodeOption = getTLSSettingsFromConfig('certificate', false); + const nodeOption = getSSLSettingsFromConfig('certificate', false); expect(nodeOption).toMatchObject({ verificationMode: 'certificate', }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/get_node_tls_options.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/get_node_ssl_options.ts similarity index 92% rename from x-pack/plugins/actions/server/builtin_action_types/lib/get_node_tls_options.ts rename to x-pack/plugins/actions/server/builtin_action_types/lib/get_node_ssl_options.ts index 423e9756b13f8..46e90ec3be697 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/get_node_tls_options.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/get_node_ssl_options.ts @@ -6,10 +6,10 @@ */ import { PeerCertificate } from 'tls'; -import { TLSSettings } from '../../types'; +import { SSLSettings } from '../../types'; import { Logger } from '../../../../../../src/core/server'; -export function getNodeTLSOptions( +export function getNodeSSLOptions( logger: Logger, verificationMode?: string ): { @@ -44,10 +44,10 @@ export function getNodeTLSOptions( return agentOptions; } -export function getTLSSettingsFromConfig( +export function getSSLSettingsFromConfig( verificationMode?: 'none' | 'certificate' | 'full', rejectUnauthorized?: boolean -): TLSSettings { +): SSLSettings { if (verificationMode) { return { verificationMode }; } else if (rejectUnauthorized !== undefined) { diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts index 9bdb2d9481142..3719dd8cd737c 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts @@ -76,7 +76,7 @@ describe('send_email module', () => { }, { proxyUrl: 'https://example.com', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -238,7 +238,7 @@ describe('send_email module', () => { }, { proxyUrl: 'https://proxy.com', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: new Set(['example.com']), @@ -272,7 +272,7 @@ describe('send_email module', () => { }, { proxyUrl: 'https://proxy.com', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: new Set(['not-example.com']), @@ -308,7 +308,7 @@ describe('send_email module', () => { }, { proxyUrl: 'https://proxy.com', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -344,7 +344,7 @@ describe('send_email module', () => { }, { proxyUrl: 'https://proxy.com', - proxyTLSSettings: {}, + proxySSLSettings: {}, proxyBypassHosts: undefined, proxyOnlyHosts: new Set(['not-example.com']), } @@ -377,7 +377,7 @@ describe('send_email module', () => { undefined, { url: 'smtp://example.com:1025', - tls: { + ssl: { certificateAuthoritiesData: 'ca cert data goes here', }, smtp: { @@ -419,7 +419,7 @@ describe('send_email module', () => { undefined, { url: 'smtp://example.com:1025', - tls: { + ssl: { certificateAuthoritiesData: 'ca cert data goes here', rejectUnauthorized: true, }, @@ -461,13 +461,13 @@ describe('send_email module', () => { }, { proxyUrl: 'https://proxy.com', - proxyTLSSettings: {}, + proxySSLSettings: {}, proxyBypassHosts: undefined, proxyOnlyHosts: undefined, }, { url: 'smtp://example.com:1025', - tls: { + ssl: { certificateAuthoritiesData: 'ca cert data goes here', rejectUnauthorized: true, }, diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts index 9f601840bc982..b32ea7d74f025 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts @@ -12,7 +12,7 @@ import { default as MarkdownIt } from 'markdown-it'; import { Logger } from '../../../../../../src/core/server'; import { ActionsConfigurationUtilities } from '../../actions_config'; import { CustomHostSettings } from '../../config'; -import { getNodeTLSOptions, getTLSSettingsFromConfig } from './get_node_tls_options'; +import { getNodeSSLOptions, getSSLSettingsFromConfig } from './get_node_ssl_options'; // an email "service" which doesn't actually send, just returns what it would send export const JSON_TRANSPORT_SERVICE = '__json'; @@ -59,7 +59,7 @@ export async function sendEmail(logger: Logger, options: SendEmailOptions): Prom // eslint-disable-next-line @typescript-eslint/no-explicit-any const transportConfig: Record = {}; const proxySettings = configurationUtilities.getProxySettings(); - const generalTLSSettings = configurationUtilities.getTLSSettings(); + const generalSSLSettings = configurationUtilities.getSSLSettings(); if (hasAuth && user != null && password != null) { transportConfig.auth = { @@ -92,9 +92,9 @@ export async function sendEmail(logger: Logger, options: SendEmailOptions): Prom customHostSettings = configurationUtilities.getCustomHostSettings(`smtp://${host}:${port}`); if (proxySettings && useProxy) { - transportConfig.tls = getNodeTLSOptions( + transportConfig.tls = getNodeSSLOptions( logger, - proxySettings?.proxyTLSSettings.verificationMode + proxySettings?.proxySSLSettings.verificationMode ); transportConfig.proxy = proxySettings.proxyUrl; transportConfig.headers = proxySettings.proxyHeaders; @@ -104,25 +104,25 @@ export async function sendEmail(logger: Logger, options: SendEmailOptions): Prom // authenticate rarely have valid certs; eg cloud proxy, and npm maildev transportConfig.tls = { rejectUnauthorized: false }; } else { - transportConfig.tls = getNodeTLSOptions(logger, generalTLSSettings.verificationMode); + transportConfig.tls = getNodeSSLOptions(logger, generalSSLSettings.verificationMode); } // finally, allow customHostSettings to override some of the settings // see: https://nodemailer.com/smtp/ if (customHostSettings) { const tlsConfig: Record = {}; - const tlsSettings = customHostSettings.tls; + const sslSettings = customHostSettings.ssl; const smtpSettings = customHostSettings.smtp; - if (tlsSettings?.certificateAuthoritiesData) { - tlsConfig.ca = tlsSettings?.certificateAuthoritiesData; + if (sslSettings?.certificateAuthoritiesData) { + tlsConfig.ca = sslSettings?.certificateAuthoritiesData; } - const tlsSettingsFromConfig = getTLSSettingsFromConfig( - tlsSettings?.verificationMode, - tlsSettings?.rejectUnauthorized + const sslSettingsFromConfig = getSSLSettingsFromConfig( + sslSettings?.verificationMode, + sslSettings?.rejectUnauthorized ); - const nodeTLSOptions = getNodeTLSOptions(logger, tlsSettingsFromConfig.verificationMode); + const nodeTLSOptions = getNodeSSLOptions(logger, sslSettingsFromConfig.verificationMode); if (!transportConfig.tls) { transportConfig.tls = { ...tlsConfig, ...nodeTLSOptions }; } else { diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/schema.ts index 9095780fea17c..9f76a236cacd5 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/schema.ts @@ -25,14 +25,6 @@ export const ExternalIncidentServiceSecretConfigurationSchema = schema.object( ExternalIncidentServiceSecretConfiguration ); -export const ExecutorSubActionSchema = schema.oneOf([ - schema.literal('getIncident'), - schema.literal('pushToService'), - schema.literal('handshake'), - schema.literal('incidentTypes'), - schema.literal('severity'), -]); - export const ExecutorSubActionPushParamsSchema = schema.object({ incident: schema.object({ name: schema.string(), diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts index 59b0803d189cd..6fec30803d6d7 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts @@ -24,14 +24,6 @@ export const ExternalIncidentServiceSecretConfigurationSchema = schema.object( ExternalIncidentServiceSecretConfiguration ); -export const ExecutorSubActionSchema = schema.oneOf([ - schema.literal('getFields'), - schema.literal('getIncident'), - schema.literal('pushToService'), - schema.literal('handshake'), - schema.literal('getChoices'), -]); - const CommentsSchema = schema.nullable( schema.arrayOf( schema.object({ diff --git a/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts b/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts index 4108424e26ac4..7953f0ab365e8 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts @@ -194,7 +194,7 @@ describe('execute()', () => { const configurationUtilities = actionsConfigMock.create(); configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -221,7 +221,7 @@ describe('execute()', () => { const configurationUtilities = actionsConfigMock.create(); configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: new Set(['example.com']), @@ -248,7 +248,7 @@ describe('execute()', () => { const configurationUtilities = actionsConfigMock.create(); configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: new Set(['not-example.com']), @@ -275,7 +275,7 @@ describe('execute()', () => { const configurationUtilities = actionsConfigMock.create(); configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, @@ -302,7 +302,7 @@ describe('execute()', () => { const configurationUtilities = actionsConfigMock.create(); configurationUtilities.getProxySettings.mockReturnValue({ proxyUrl: 'https://someproxyhost', - proxyTLSSettings: { + proxySSLSettings: { verificationMode: 'none', }, proxyBypassHosts: undefined, diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/api.test.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/api.test.ts new file mode 100644 index 0000000000000..1e633e2175808 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/api.test.ts @@ -0,0 +1,142 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { api } from './api'; +import { ExternalService } from './types'; +import { + apiParams, + externalServiceMock, + recordResponseCreate, + recordResponseUpdate, +} from './mocks'; +import { Logger } from '@kbn/logging'; + +let mockedLogger: jest.Mocked; + +describe('api', () => { + let externalService: jest.Mocked; + + beforeEach(() => { + externalService = externalServiceMock.create(); + }); + + describe('pushToService', () => { + test('it pushes a new record', async () => { + const params = { ...apiParams, incident: { ...apiParams.incident, externalId: null } }; + const res = await api.pushToService({ + externalService, + logger: mockedLogger, + params, + }); + + expect(externalService.createComment).toHaveBeenCalled(); + expect(externalService.createRecord).toHaveBeenCalled(); + expect(externalService.updateRecord).not.toHaveBeenCalled(); + + expect(res).toEqual({ + ...recordResponseCreate, + comments: [ + { + commentId: '123456', + pushedDate: '2021-06-01T17:29:51.092Z', + }, + { + commentId: '123456', + pushedDate: '2021-06-01T17:29:51.092Z', + }, + ], + }); + }); + + test('it pushes a new record without comment', async () => { + const params = { + ...apiParams, + incident: { ...apiParams.incident, externalId: null }, + comments: [], + }; + const res = await api.pushToService({ + externalService, + logger: mockedLogger, + params, + }); + + expect(externalService.createComment).not.toHaveBeenCalled(); + expect(externalService.createRecord).toHaveBeenCalled(); + expect(res).toEqual(recordResponseCreate); + }); + + test('updates existing record', async () => { + const res = await api.pushToService({ + externalService, + logger: mockedLogger, + params: apiParams, + }); + + expect(externalService.createComment).toHaveBeenCalled(); + expect(externalService.createRecord).not.toHaveBeenCalled(); + expect(externalService.updateRecord).toHaveBeenCalled(); + expect(res).toEqual({ + ...recordResponseUpdate, + comments: [ + { + commentId: '123456', + pushedDate: '2021-06-01T17:29:51.092Z', + }, + { + commentId: '123456', + pushedDate: '2021-06-01T17:29:51.092Z', + }, + ], + }); + }); + + test('it calls createRecord correctly', async () => { + const params = { ...apiParams, incident: { ...apiParams.incident, externalId: null } }; + await api.pushToService({ externalService, params, logger: mockedLogger }); + + expect(externalService.createRecord).toHaveBeenCalledWith({ + incident: { + alertId: '123456', + caseId: '123456', + caseName: 'case name', + description: 'case desc', + ruleName: 'rule name', + severity: 'critical', + }, + }); + }); + + test('it calls createComment correctly', async () => { + const mockedToISOString = jest + .spyOn(Date.prototype, 'toISOString') + .mockReturnValue('2021-06-15T18:02:29.404Z'); + + const params = { ...apiParams, incident: { ...apiParams.incident, externalId: null } }; + await api.pushToService({ externalService, params, logger: mockedLogger }); + + expect(externalService.createComment).toHaveBeenNthCalledWith(1, { + createdDate: '2021-06-15T18:02:29.404Z', + incidentId: '123456', + comment: { + commentId: 'case-comment-1', + comment: 'A comment', + }, + }); + + expect(externalService.createComment).toHaveBeenNthCalledWith(2, { + createdDate: '2021-06-15T18:02:29.404Z', + incidentId: '123456', + comment: { + commentId: 'case-comment-2', + comment: 'Another comment', + }, + }); + + mockedToISOString.mockRestore(); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/api.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/api.ts new file mode 100644 index 0000000000000..343a94e52711f --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/api.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ExternalServiceIncidentResponse, + ExternalServiceApi, + Incident, + PushToServiceApiHandlerArgs, + PushToServiceResponse, +} from './types'; + +const pushToServiceHandler = async ({ + externalService, + params, +}: PushToServiceApiHandlerArgs): Promise => { + const { comments } = params; + let res: PushToServiceResponse; + const { externalId, ...rest } = params.incident; + const incident: Incident = rest; + + if (externalId != null) { + res = await externalService.updateRecord({ + incidentId: externalId, + incident, + }); + } else { + res = await externalService.createRecord({ incident }); + } + + const createdDate = new Date().toISOString(); + + if (comments && Array.isArray(comments) && comments.length > 0) { + res.comments = []; + for (const currentComment of comments) { + const comment = await externalService.createComment({ + incidentId: res.id, + comment: currentComment, + createdDate, + }); + + res.comments = [ + ...(res.comments ?? []), + { + commentId: comment.commentId, + pushedDate: comment.pushedDate, + }, + ]; + } + } + + return res; +}; + +export const api: ExternalServiceApi = { + pushToService: pushToServiceHandler, +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/helpers.test.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/helpers.test.ts new file mode 100644 index 0000000000000..c2974ec28486c --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/helpers.test.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getBodyForEventAction } from './helpers'; +import { mappings } from './mocks'; + +describe('Create Record Mapping', () => { + const appId = '45678'; + + test('it maps successfully', () => { + const params = { + alertId: 'al123', + ruleName: 'Rule Name', + severity: 'Critical', + caseName: 'Case Name', + caseId: 'es3456789', + description: 'case desc', + externalId: null, + }; + + const data = getBodyForEventAction(appId, mappings, params); + expect(data.applicationId).toEqual(appId); + expect(data.id).not.toBeDefined(); + expect(data.values?.[mappings.alertIdConfig?.id ?? 0]).toEqual(params.alertId); + expect(data.values?.[mappings.ruleNameConfig.id]).toEqual(params.ruleName); + expect(data.values?.[mappings.caseNameConfig?.id ?? 0]).toEqual(params.caseName); + expect(data.values?.[mappings.caseIdConfig?.id ?? 0]).toEqual(params.caseId); + expect(data.values?.[mappings?.severityConfig?.id ?? 0]).toEqual(params.severity); + expect(data.values?.[mappings?.descriptionConfig?.id ?? 0]).toEqual(params.description); + }); + + test('it contains the id if defined', () => { + const params = { + alertId: 'al123', + ruleName: 'Rule Name', + severity: 'Critical', + caseName: 'Case Name', + caseId: 'es3456789', + description: 'case desc', + externalId: null, + }; + const data = getBodyForEventAction(appId, mappings, params, '123'); + expect(data.id).toEqual('123'); + }); + + test('it does not includes null mappings', () => { + const params = { + alertId: 'al123', + ruleName: 'Rule Name', + severity: 'Critical', + caseName: 'Case Name', + caseId: 'es3456789', + description: 'case desc', + externalId: null, + }; + + // @ts-expect-error + const data = getBodyForEventAction(appId, { ...mappings, test: null }, params); + expect(data.values?.test).not.toBeDefined(); + }); + + test('it converts a numeric values correctly', () => { + const params = { + alertId: 'thisIsNotANumber', + ruleName: 'Rule Name', + severity: 'Critical', + caseName: 'Case Name', + caseId: '123', + description: 'case desc', + externalId: null, + }; + + const data = getBodyForEventAction( + appId, + { + ...mappings, + caseIdConfig: { ...mappings.caseIdConfig, fieldType: 'numeric' }, + alertIdConfig: { ...mappings.alertIdConfig, fieldType: 'numeric' }, + }, + params + ); + + expect(data.values?.[mappings.alertIdConfig?.id ?? 0]).toBe(0); + expect(data.values?.[mappings.caseIdConfig?.id ?? 0]).toBe(123); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/helpers.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/helpers.ts new file mode 100644 index 0000000000000..13b2df1c97f16 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/helpers.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CreateRecordParams, Incident, SwimlaneRecordPayload, MappingConfigType } from './types'; + +type ConfigMapping = Omit; + +const mappingKeysToIncidentKeys: Record = { + ruleNameConfig: 'ruleName', + alertIdConfig: 'alertId', + caseIdConfig: 'caseId', + caseNameConfig: 'caseName', + severityConfig: 'severity', + descriptionConfig: 'description', +}; + +export const getBodyForEventAction = ( + applicationId: string, + mappingConfig: MappingConfigType, + params: CreateRecordParams['incident'], + incidentId?: string +): SwimlaneRecordPayload => { + const data: SwimlaneRecordPayload = { + applicationId, + ...(incidentId ? { id: incidentId } : {}), + values: {}, + }; + + return (Object.keys(mappingConfig) as Array).reduce((acc, key) => { + const fieldMap = mappingConfig[key]; + + if (!fieldMap) { + return acc; + } + + const { id, fieldType } = fieldMap; + const paramName = mappingKeysToIncidentKeys[key]; + const value = params[paramName]; + + if (value) { + switch (fieldType) { + case 'numeric': { + const number = Number(value); + return { ...acc, values: { ...acc.values, [id]: isNaN(number) ? 0 : number } }; + } + default: { + return { ...acc, values: { ...acc.values, [id]: value } }; + } + } + } + + return acc; + }, data); +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/index.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/index.ts new file mode 100644 index 0000000000000..de5010436b6b3 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/index.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { curry } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; +import { Logger } from '@kbn/logging'; +import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../../types'; +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { + SwimlaneExecutorResultData, + SwimlanePublicConfigurationType, + SwimlaneSecretConfigurationType, + ExecutorParams, + ExecutorSubActionPushParams, +} from './types'; +import { validate } from './validators'; +import { + ExecutorParamsSchema, + SwimlaneSecretsConfiguration, + SwimlaneServiceConfiguration, +} from './schema'; +import { createExternalService } from './service'; +import { api } from './api'; + +interface GetActionTypeParams { + logger: Logger; + configurationUtilities: ActionsConfigurationUtilities; +} + +const supportedSubActions: string[] = ['pushToService']; + +// action type definition +export function getActionType( + params: GetActionTypeParams +): ActionType< + SwimlanePublicConfigurationType, + SwimlaneSecretConfigurationType, + ExecutorParams, + SwimlaneExecutorResultData | {} +> { + const { logger, configurationUtilities } = params; + + return { + id: '.swimlane', + minimumLicenseRequired: 'gold', + name: i18n.translate('xpack.actions.builtin.swimlaneTitle', { + defaultMessage: 'Swimlane', + }), + validate: { + config: schema.object(SwimlaneServiceConfiguration, { + validate: curry(validate.config)(configurationUtilities), + }), + secrets: schema.object(SwimlaneSecretsConfiguration, { + validate: curry(validate.secrets)(configurationUtilities), + }), + params: ExecutorParamsSchema, + }, + executor: curry(executor)({ logger, configurationUtilities }), + }; +} + +async function executor( + { + logger, + configurationUtilities, + }: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities }, + execOptions: ActionTypeExecutorOptions< + SwimlanePublicConfigurationType, + SwimlaneSecretConfigurationType, + ExecutorParams + > +): Promise> { + const { actionId, config, params, secrets } = execOptions; + const { subAction, subActionParams } = params as ExecutorParams; + let data: SwimlaneExecutorResultData | null = null; + + const externalService = createExternalService( + { + config, + secrets, + }, + logger, + configurationUtilities + ); + + if (!api[subAction]) { + const errorMessage = `[Action][ExternalService] -> [Swimlane] Unsupported subAction type ${subAction}.`; + logger.error(errorMessage); + throw new Error(errorMessage); + } + + if (!supportedSubActions.includes(subAction)) { + const errorMessage = `[Action][ExternalService] -> [Swimlane] subAction ${subAction} not implemented.`; + logger.error(errorMessage); + throw new Error(errorMessage); + } + + if (subAction === 'pushToService') { + const pushToServiceParams = subActionParams as ExecutorSubActionPushParams; + + data = await api.pushToService({ + externalService, + params: pushToServiceParams, + logger, + }); + + logger.debug(`response push to service for incident id: ${data.id}`); + } + + return { status: 'ok', data: data ?? {}, actionId }; +} diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/mocks.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/mocks.ts new file mode 100644 index 0000000000000..f9931049d81c2 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/mocks.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ExecutorSubActionPushParams, ExternalService, PushToServiceApiParams } from './types'; + +export const applicationFields = [ + { + id: 'adnlas', + name: 'Severity', + key: 'severity', + fieldType: 'text', + }, + { + id: 'adnfls', + name: 'Rule Name', + key: 'rule-name', + fieldType: 'text', + }, + { + id: 'a6sst', + name: 'Case Id', + key: 'case-id-name', + fieldType: 'text', + }, + { + id: 'a6fst', + name: 'Case Name', + key: 'case-name', + fieldType: 'text', + }, + { + id: 'a6fdf', + name: 'Comments', + key: 'comments', + fieldType: 'notes', + }, + { + id: 'a6fde', + name: 'Description', + key: 'description', + fieldType: 'text', + }, + { + id: 'dfnkls', + name: 'Alert ID', + key: 'alert-id', + fieldType: 'text', + }, +]; + +export const mappings = { + severityConfig: applicationFields[0], + ruleNameConfig: applicationFields[1], + caseIdConfig: applicationFields[2], + caseNameConfig: applicationFields[3], + commentsConfig: applicationFields[4], + descriptionConfig: applicationFields[5], + alertIdConfig: applicationFields[6], +}; + +export const getApplicationResponse = { fields: applicationFields }; + +export const recordResponseCreate = { + id: '123456', + title: 'neato', + url: 'swimlane.com', + pushedDate: '2021-06-01T17:29:51.092Z', +}; + +export const recordResponseUpdate = { + id: '98765', + title: 'not neato', + url: 'laneswim.com', + pushedDate: '2021-06-01T17:29:51.092Z', +}; + +export const commentResponse = { + commentId: '123456', + pushedDate: '2021-06-01T17:29:51.092Z', +}; + +const createMock = (): jest.Mocked => { + return { + createComment: jest.fn().mockImplementation(() => Promise.resolve(commentResponse)), + createRecord: jest.fn().mockImplementation(() => Promise.resolve(recordResponseCreate)), + updateRecord: jest.fn().mockImplementation(() => Promise.resolve(recordResponseUpdate)), + }; +}; + +const externalServiceMock = { + create: createMock, +}; + +const executorParams: ExecutorSubActionPushParams = { + incident: { + ruleName: 'rule name', + alertId: '123456', + caseName: 'case name', + severity: 'critical', + caseId: '123456', + description: 'case desc', + externalId: 'incident-3', + }, + comments: [ + { + commentId: 'case-comment-1', + comment: 'A comment', + }, + { + commentId: 'case-comment-2', + comment: 'Another comment', + }, + ], +}; + +const apiParams: PushToServiceApiParams = { + ...executorParams, +}; + +export { externalServiceMock, executorParams, apiParams }; diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/schema.ts new file mode 100644 index 0000000000000..7f4bdc8ca6c0d --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/schema.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; + +export const ConfigMap = { + id: schema.string(), + key: schema.string(), + name: schema.string(), + fieldType: schema.string(), +}; + +export const ConfigMapSchema = schema.object(ConfigMap); + +export const ConfigMapping = { + ruleNameConfig: schema.nullable(ConfigMapSchema), + alertIdConfig: schema.nullable(ConfigMapSchema), + caseIdConfig: schema.nullable(ConfigMapSchema), + caseNameConfig: schema.nullable(ConfigMapSchema), + commentsConfig: schema.nullable(ConfigMapSchema), + severityConfig: schema.nullable(ConfigMapSchema), + descriptionConfig: schema.nullable(ConfigMapSchema), +}; + +export const ConfigMappingSchema = schema.object(ConfigMapping); + +export const SwimlaneServiceConfiguration = { + apiUrl: schema.string(), + appId: schema.string(), + connectorType: schema.string(), + mappings: ConfigMappingSchema, +}; + +export const SwimlaneServiceConfigurationSchema = schema.object(SwimlaneServiceConfiguration); + +export const SwimlaneSecretsConfiguration = { + apiToken: schema.string(), +}; + +export const SwimlaneSecretsConfigurationSchema = schema.object(SwimlaneSecretsConfiguration); + +const SwimlaneFields = { + alertId: schema.nullable(schema.string()), + ruleName: schema.nullable(schema.string()), + caseId: schema.nullable(schema.string()), + caseName: schema.nullable(schema.string()), + severity: schema.nullable(schema.string()), + description: schema.nullable(schema.string()), +}; + +export const ExecutorSubActionPushParamsSchema = schema.object({ + incident: schema.object({ + ...SwimlaneFields, + externalId: schema.nullable(schema.string()), + }), + comments: schema.nullable( + schema.arrayOf( + schema.object({ + comment: schema.string(), + commentId: schema.string(), + }) + ) + ), +}); + +export const ExecutorParamsSchema = schema.oneOf([ + schema.object({ + subAction: schema.literal('pushToService'), + subActionParams: ExecutorSubActionPushParamsSchema, + }), +]); diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/service.test.ts new file mode 100644 index 0000000000000..77f4686f8acd0 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/service.test.ts @@ -0,0 +1,434 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import axios from 'axios'; + +import { loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { Logger } from '../../../../../../src/core/server'; +import { actionsConfigMock } from '../../actions_config.mock'; +import * as utils from '../lib/axios_utils'; +import { createExternalService } from './service'; +import { mappings } from './mocks'; +import { ExternalService } from './types'; + +const logger = loggingSystemMock.create().get() as jest.Mocked; + +jest.mock('axios'); +jest.mock('../lib/axios_utils', () => { + const originalUtils = jest.requireActual('../lib/axios_utils'); + return { + ...originalUtils, + request: jest.fn(), + }; +}); + +axios.create = jest.fn(() => axios); +const requestMock = utils.request as jest.Mock; +const configurationUtilities = actionsConfigMock.create(); + +describe('Swimlane Service', () => { + let service: ExternalService; + const config = { + apiUrl: 'https://test.swimlane.com/', + appId: 'bcq16kdTbz5jlwM6h', + connectorType: 'all', + mappings, + }; + const apiToken = 'token'; + + const headers = { + 'Content-Type': 'application/json', + 'Private-Token': apiToken, + }; + + const incident = { + ruleName: 'Rule Name', + caseId: 'Case Id', + caseName: 'Case Name', + severity: 'Severity', + externalId: null, + description: 'Description', + alertId: 'Alert Id', + }; + + const url = config.apiUrl.slice(0, -1); + + beforeAll(() => { + service = createExternalService( + { + // The trailing slash at the end of the url is intended. + // All API calls need to have the trailing slash removed. + config, + secrets: { apiToken }, + }, + logger, + configurationUtilities + ); + }); + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('createExternalService', () => { + test('throws without url', () => { + expect(() => + createExternalService( + { + config: { + // @ts-ignore + apiUrl: null, + appId: '99999', + mappings, + }, + secrets: { apiToken }, + }, + logger, + configurationUtilities + ) + ).toThrow(); + }); + + test('throws without app id', () => { + expect(() => + createExternalService( + { + config: { + apiUrl: 'test.com', + // @ts-ignore + appId: null, + }, + secrets: { apiToken }, + }, + logger, + configurationUtilities + ) + ).toThrow(); + }); + + test('throws without mappings', () => { + expect(() => + createExternalService( + { + config: { + apiUrl: 'test.com', + appId: '987987', + // @ts-ignore + mappings: null, + }, + secrets: { apiToken }, + }, + logger, + configurationUtilities + ) + ).toThrow(); + }); + + test('throws without api token', () => { + expect(() => { + return createExternalService( + { + config: { apiUrl: 'test.com', appId: '78978', mappings, connectorType: 'all' }, + secrets: { + // @ts-ignore + apiToken: null, + }, + }, + logger, + configurationUtilities + ); + }).toThrow(); + }); + }); + + describe('createRecord', () => { + const data = { + id: '123', + name: 'title', + createdDate: '2021-06-01T17:29:51.092Z', + }; + + test('it creates a record correctly', async () => { + requestMock.mockImplementation(() => ({ + data, + })); + + const res = await service.createRecord({ + incident, + }); + + expect(res).toEqual({ + id: '123', + title: 'title', + pushedDate: '2021-06-01T17:29:51.092Z', + url: `${url}/record/${config.appId}/123`, + }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data, + })); + + await service.createRecord({ + incident, + }); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + headers, + data: { + applicationId: config.appId, + values: { + [mappings.ruleNameConfig.id]: 'Rule Name', + [mappings.caseNameConfig.id]: 'Case Name', + [mappings.caseIdConfig.id]: 'Case Id', + [mappings.severityConfig.id]: 'Severity', + [mappings.descriptionConfig.id]: 'Description', + [mappings.alertIdConfig.id]: 'Alert Id', + }, + }, + url: `${url}/api/app/${config.appId}/record`, + method: 'post', + configurationUtilities, + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + await expect(service.createRecord({ incident })).rejects.toThrow( + `[Action][Swimlane]: Unable to create record in application with id ${config.appId}. Status: 500. Error: An error has occurred. Reason: unknown` + ); + }); + }); + + describe('updateRecord', () => { + const data = { + id: '123', + name: 'title', + modifiedDate: '2021-06-01T17:29:51.092Z', + }; + const incidentId = '123'; + + test('it updates a record correctly', async () => { + requestMock.mockImplementation(() => ({ + data, + })); + + const res = await service.updateRecord({ + incident, + incidentId, + }); + + expect(res).toEqual({ + id: '123', + title: 'title', + pushedDate: '2021-06-01T17:29:51.092Z', + url: `${url}/record/${config.appId}/123`, + }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data, + })); + + await service.updateRecord({ + incident, + incidentId, + }); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + headers, + data: { + applicationId: config.appId, + id: incidentId, + values: { + [mappings.ruleNameConfig.id]: 'Rule Name', + [mappings.caseNameConfig.id]: 'Case Name', + [mappings.caseIdConfig.id]: 'Case Id', + [mappings.severityConfig.id]: 'Severity', + [mappings.descriptionConfig.id]: 'Description', + [mappings.alertIdConfig.id]: 'Alert Id', + }, + }, + url: `${url}/api/app/${config.appId}/record/${incidentId}`, + method: 'patch', + configurationUtilities, + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + await expect(service.updateRecord({ incident, incidentId })).rejects.toThrow( + `[Action][Swimlane]: Unable to update record in application with id ${config.appId}. Status: 500. Error: An error has occurred. Reason: unknown` + ); + }); + }); + + describe('createComment', () => { + const data = { + id: '123', + name: 'title', + modifiedDate: '2021-06-01T17:29:51.092Z', + }; + const incidentId = '123'; + const comment = { commentId: '456', comment: 'A comment' }; + const createdDate = '2021-06-01T17:29:51.092Z'; + + test('it updates a record correctly', async () => { + requestMock.mockImplementation(() => ({ + data, + })); + + const res = await service.createComment({ + comment, + incidentId, + createdDate, + }); + + expect(res).toEqual({ + commentId: '456', + pushedDate: '2021-06-01T17:29:51.092Z', + }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data, + })); + + await service.createComment({ + comment, + incidentId, + createdDate, + }); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + headers, + data: { + createdDate, + fieldId: mappings.commentsConfig.id, + isRichText: true, + message: comment.comment, + }, + url: `${url}/api/app/${config.appId}/record/${incidentId}/${mappings.commentsConfig.id}/comment`, + method: 'post', + configurationUtilities, + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + await expect(service.createComment({ comment, incidentId, createdDate })).rejects.toThrow( + `[Action][Swimlane]: Unable to create comment in application with id ${config.appId}. Status: 500. Error: An error has occurred. Reason: unknown` + ); + }); + }); + + describe('error messages', () => { + const errorResponse = { ErrorCode: '1', Argument: 'Invalid field' }; + + test('it contains the response error', async () => { + requestMock.mockImplementation(() => { + const error = new Error('An error has occurred'); + // @ts-ignore + error.response = { data: errorResponse }; + throw error; + }); + + await expect( + service.createRecord({ + incident, + }) + ).rejects.toThrow( + `[Action][Swimlane]: Unable to create record in application with id ${config.appId}. Status: 500. Error: An error has occurred. Reason: Invalid field (1)` + ); + }); + + test('it shows an empty string for reason if the ErrorCode is undefined', async () => { + requestMock.mockImplementation(() => { + const error = new Error('An error has occurred'); + // @ts-ignore + error.response = { data: { ErrorCode: '1' } }; + throw error; + }); + + await expect( + service.createRecord({ + incident, + }) + ).rejects.toThrow( + `[Action][Swimlane]: Unable to create record in application with id ${config.appId}. Status: 500. Error: An error has occurred. Reason: unknown` + ); + }); + + test('it shows an empty string for reason if the Argument is undefined', async () => { + requestMock.mockImplementation(() => { + const error = new Error('An error has occurred'); + // @ts-ignore + error.response = { data: { Argument: 'Invalid field' } }; + throw error; + }); + + await expect( + service.createRecord({ + incident, + }) + ).rejects.toThrow( + `[Action][Swimlane]: Unable to create record in application with id ${config.appId}. Status: 500. Error: An error has occurred. Reason: unknown` + ); + }); + + test('it shows an empty string for reason if data is undefined', async () => { + requestMock.mockImplementation(() => { + const error = new Error('An error has occurred'); + // @ts-ignore + error.response = {}; + throw error; + }); + + await expect( + service.createRecord({ + incident, + }) + ).rejects.toThrow( + `[Action][Swimlane]: Unable to create record in application with id ${config.appId}. Status: 500. Error: An error has occurred. Reason: unknown` + ); + }); + + test('it shows the status code', async () => { + requestMock.mockImplementation(() => { + const error = new Error('An error has occurred'); + // @ts-ignore + error.response = { data: errorResponse, status: 400 }; + throw error; + }); + + await expect( + service.createRecord({ + incident, + }) + ).rejects.toThrow( + `[Action][Swimlane]: Unable to create record in application with id ${config.appId}. Status: 400. Error: An error has occurred. Reason: Invalid field (1)` + ); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/service.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/service.ts new file mode 100644 index 0000000000000..f68d22121dbcc --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/service.ts @@ -0,0 +1,196 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '@kbn/logging'; +import axios from 'axios'; + +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { getErrorMessage, request } from '../lib/axios_utils'; +import { getBodyForEventAction } from './helpers'; +import { + CreateCommentParams, + CreateRecordParams, + ExternalService, + ExternalServiceCredentials, + ExternalServiceIncidentResponse, + MappingConfigType, + ResponseError, + SwimlanePublicConfigurationType, + SwimlaneRecordPayload, + SwimlaneSecretConfigurationType, + UpdateRecordParams, +} from './types'; +import * as i18n from './translations'; + +const createErrorMessage = (errorResponse: ResponseError | null | undefined): string => { + if (errorResponse == null) { + return 'unknown'; + } + + const { ErrorCode, Argument } = errorResponse; + return Argument != null && ErrorCode != null ? `${Argument} (${ErrorCode})` : 'unknown'; +}; + +export const createExternalService = ( + { config, secrets }: ExternalServiceCredentials, + logger: Logger, + configurationUtilities: ActionsConfigurationUtilities +): ExternalService => { + const { apiUrl: url, appId, mappings } = config as SwimlanePublicConfigurationType; + const { apiToken } = secrets as SwimlaneSecretConfigurationType; + + const axiosInstance = axios.create(); + + if (!url || !appId || !apiToken || !mappings) { + throw Error(`[Action]${i18n.NAME}: Wrong configuration.`); + } + + const headers: Record = { + 'Content-Type': 'application/json', + 'Private-Token': `${secrets.apiToken}`, + }; + + const urlWithoutTrailingSlash = url.endsWith('/') ? url.slice(0, -1) : url; + const apiUrl = urlWithoutTrailingSlash.endsWith('api') + ? urlWithoutTrailingSlash + : urlWithoutTrailingSlash + '/api'; + + const getPostRecordUrl = (id: string) => `${apiUrl}/app/${id}/record`; + + const getPostRecordIdUrl = (id: string, recordId: string) => + `${getPostRecordUrl(id)}/${recordId}`; + + const getRecordIdUrl = (id: string, recordId: string) => + `${urlWithoutTrailingSlash}/record/${id}/${recordId}`; + + const getPostCommentUrl = (id: string, recordId: string, commentFieldId: string) => + `${getPostRecordIdUrl(id, recordId)}/${commentFieldId}/comment`; + + const getCommentFieldId = (fieldMappings: MappingConfigType): string | null => + fieldMappings.commentsConfig?.id || null; + + const createRecord = async ( + params: CreateRecordParams + ): Promise => { + try { + const mappingConfig = mappings as MappingConfigType; + const data = getBodyForEventAction(appId, mappingConfig, params.incident); + + const res = await request({ + axios: axiosInstance, + configurationUtilities, + data, + headers, + logger, + method: 'post', + url: getPostRecordUrl(appId), + }); + return { + id: res.data.id, + title: res.data.name, + url: getRecordIdUrl(appId, res.data.id), + pushedDate: new Date(res.data.createdDate).toISOString(), + }; + } catch (error) { + throw new Error( + getErrorMessage( + i18n.NAME, + `Unable to create record in application with id ${appId}. Status: ${ + error.response?.status ?? 500 + }. Error: ${error.message}. Reason: ${createErrorMessage(error.response?.data)}` + ) + ); + } + }; + + const updateRecord = async ( + params: UpdateRecordParams + ): Promise => { + try { + const mappingConfig = mappings as MappingConfigType; + const data = getBodyForEventAction(appId, mappingConfig, params.incident, params.incidentId); + + const res = await request({ + axios: axiosInstance, + configurationUtilities, + data, + headers, + logger, + method: 'patch', + url: getPostRecordIdUrl(appId, params.incidentId), + }); + + return { + id: res.data.id, + title: res.data.name, + url: getRecordIdUrl(appId, params.incidentId), + pushedDate: new Date(res.data.modifiedDate).toISOString(), + }; + } catch (error) { + throw new Error( + getErrorMessage( + i18n.NAME, + `Unable to update record in application with id ${appId}. Status: ${ + error.response?.status ?? 500 + }. Error: ${error.message}. Reason: ${createErrorMessage(error.response?.data)}` + ) + ); + } + }; + + const createComment = async ({ incidentId, comment, createdDate }: CreateCommentParams) => { + try { + const mappingConfig = mappings as MappingConfigType; + const fieldId = getCommentFieldId(mappingConfig); + + if (fieldId == null) { + throw new Error(`No comment field mapped in ${i18n.NAME} connector`); + } + + const data = { + createdDate, + fieldId, + isRichText: true, + message: comment.comment, + }; + + await request({ + axios: axiosInstance, + configurationUtilities, + data, + headers, + logger, + method: 'post', + url: getPostCommentUrl(appId, incidentId, fieldId), + }); + + /** + * Swimlane response does not contain any data. + * We cannot get an externalCommentId + */ + return { + commentId: comment.commentId, + pushedDate: createdDate, + }; + } catch (error) { + throw new Error( + getErrorMessage( + i18n.NAME, + `Unable to create comment in application with id ${appId}. Status: ${ + error.response?.status ?? 500 + }. Error: ${error.message}. Reason: ${createErrorMessage(error.response?.data)}` + ) + ); + } + }; + + return { + createComment, + createRecord, + updateRecord, + }; +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/translations.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/translations.ts new file mode 100644 index 0000000000000..671cf224448f6 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/translations.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const NAME = i18n.translate('xpack.actions.builtin.case.swimlaneTitle', { + defaultMessage: 'Swimlane', +}); + +export const ALLOWED_HOSTS_ERROR = (message: string) => + i18n.translate('xpack.actions.builtin.swimlane.configuration.apiAllowedHostsError', { + defaultMessage: 'error configuring connector action: {message}', + values: { + message, + }, + }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/types.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/types.ts new file mode 100644 index 0000000000000..5cb3b10989621 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/types.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { TypeOf } from '@kbn/config-schema'; +import { Logger } from '@kbn/logging'; +import { + ConfigMappingSchema, + ExecutorParamsSchema, + ExecutorSubActionPushParamsSchema, + SwimlaneSecretsConfigurationSchema, + SwimlaneServiceConfigurationSchema, +} from './schema'; +import { ActionsConfigurationUtilities } from '../../actions_config'; + +export type SwimlanePublicConfigurationType = TypeOf; +export type SwimlaneSecretConfigurationType = TypeOf; + +export type MappingConfigType = TypeOf; +export type ExecutorParams = TypeOf; +export type ExecutorSubActionPushParams = TypeOf; + +export interface ExternalServiceCredentials { + config: SwimlanePublicConfigurationType; + secrets: SwimlaneSecretConfigurationType; +} + +export interface ExternalServiceValidation { + config: (configurationUtilities: ActionsConfigurationUtilities, configObject: any) => void; + secrets: (configurationUtilities: ActionsConfigurationUtilities, secrets: any) => void; +} + +export interface CreateRecordParams { + incident: Incident; +} +export interface UpdateRecordParams extends CreateRecordParams { + incidentId: string; +} + +export type PushToServiceApiParams = ExecutorSubActionPushParams; +export interface PushToServiceApiHandlerArgs extends ExternalServiceApiHandlerArgs { + params: PushToServiceApiParams; + logger: Logger; +} + +export interface ExternalServiceIncidentResponse { + id: string; + title: string; + url: string; + pushedDate: string; +} +export interface ExternalServiceCommentResponse { + commentId: string; + pushedDate: string; + externalCommentId?: string; +} + +export interface FieldConfig { + id: string; + name: string; + key: string; + fieldType: string; +} + +export interface SwimlaneRecordPayload { + applicationId: string; + values: SwimlaneDataValues; + id?: string; +} + +export interface ExternalService { + createComment: (params: CreateCommentParams) => Promise; + createRecord: (params: CreateRecordParams) => Promise; + updateRecord: (params: UpdateRecordParams) => Promise; +} + +export type Incident = Omit; + +export interface ExternalServiceApiHandlerArgs { + externalService: ExternalService; +} + +export interface GetApplicationHandlerArgs { + externalService: ExternalService; +} + +export interface PushToServiceResponse extends ExternalServiceIncidentResponse { + comments?: ExternalServiceCommentResponse[]; +} + +export interface ExternalServiceApi { + pushToService: (args: PushToServiceApiHandlerArgs) => Promise; +} + +export type SwimlaneExecutorResultData = ExternalServiceIncidentResponse; +export type SwimlaneDataValues = Record; +export interface SwimlaneComment { + fieldId: string; + message: string | number; + createdDate: string; + isRichText: boolean; +} +export type SwimlaneDataComments = Record; + +export interface SimpleComment { + comment: SwimlaneComment['message']; + commentId: string; +} + +export interface CreateCommentParams { + incidentId: string; + comment: SimpleComment; + createdDate: string; +} + +export interface ResponseError { + ErrorCode: number; + Argument: string; +} diff --git a/x-pack/plugins/actions/server/builtin_action_types/swimlane/validators.ts b/x-pack/plugins/actions/server/builtin_action_types/swimlane/validators.ts new file mode 100644 index 0000000000000..1972cd7e6af0b --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/swimlane/validators.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { ExternalServiceValidation, SwimlanePublicConfigurationType } from './types'; +import * as i18n from './translations'; + +export const validateCommonConfig = ( + configurationUtilities: ActionsConfigurationUtilities, + configObject: SwimlanePublicConfigurationType +) => { + try { + configurationUtilities.ensureUriAllowed(configObject.apiUrl); + } catch (allowedListError) { + return i18n.ALLOWED_HOSTS_ERROR(allowedListError.message); + } +}; + +export const validateCommonSecrets = () => {}; + +export const validate: ExternalServiceValidation = { + config: validateCommonConfig, + secrets: validateCommonSecrets, +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/teams.test.ts b/x-pack/plugins/actions/server/builtin_action_types/teams.test.ts index bf34789e03fae..497300b86bdea 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/teams.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/teams.test.ts @@ -170,7 +170,7 @@ describe('execute()', () => { "getCustomHostSettings": [MockFunction], "getProxySettings": [MockFunction], "getResponseSettings": [MockFunction], - "getTLSSettings": [MockFunction], + "getSSLSettings": [MockFunction], "isActionTypeEnabled": [MockFunction], "isHostnameAllowed": [MockFunction], "isUriAllowed": [MockFunction], @@ -234,7 +234,7 @@ describe('execute()', () => { "getCustomHostSettings": [MockFunction], "getProxySettings": [MockFunction], "getResponseSettings": [MockFunction], - "getTLSSettings": [MockFunction], + "getSSLSettings": [MockFunction], "isActionTypeEnabled": [MockFunction], "isHostnameAllowed": [MockFunction], "isUriAllowed": [MockFunction], diff --git a/x-pack/plugins/actions/server/builtin_action_types/webhook.test.ts b/x-pack/plugins/actions/server/builtin_action_types/webhook.test.ts index b2c865c2f5374..c04c79075abdc 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/webhook.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/webhook.test.ts @@ -293,7 +293,7 @@ describe('execute()', () => { "getCustomHostSettings": [MockFunction], "getProxySettings": [MockFunction], "getResponseSettings": [MockFunction], - "getTLSSettings": [MockFunction], + "getSSLSettings": [MockFunction], "isActionTypeEnabled": [MockFunction], "isHostnameAllowed": [MockFunction], "isUriAllowed": [MockFunction], @@ -386,7 +386,7 @@ describe('execute()', () => { "getCustomHostSettings": [MockFunction], "getProxySettings": [MockFunction], "getResponseSettings": [MockFunction], - "getTLSSettings": [MockFunction], + "getSSLSettings": [MockFunction], "isActionTypeEnabled": [MockFunction], "isHostnameAllowed": [MockFunction], "isUriAllowed": [MockFunction], diff --git a/x-pack/plugins/actions/server/config.test.ts b/x-pack/plugins/actions/server/config.test.ts index 9774bfb05d4ff..d99b9349e977b 100644 --- a/x-pack/plugins/actions/server/config.test.ts +++ b/x-pack/plugins/actions/server/config.test.ts @@ -178,9 +178,9 @@ describe('config validation', () => { ); }); - test('action with tls configuration', () => { + test('action with ssl configuration', () => { const config: Record = { - tls: { + ssl: { verificationMode: 'none', proxyVerificationMode: 'none', }, @@ -208,7 +208,7 @@ describe('config validation', () => { "proxyRejectUnauthorizedCertificates": true, "rejectUnauthorized": true, "responseTimeout": "PT1M", - "tls": Object { + "ssl": Object { "proxyVerificationMode": "none", "verificationMode": "none", }, diff --git a/x-pack/plugins/actions/server/config.ts b/x-pack/plugins/actions/server/config.ts index 8859a2d8881a2..1ae196c25a756 100644 --- a/x-pack/plugins/actions/server/config.ts +++ b/x-pack/plugins/actions/server/config.ts @@ -31,7 +31,7 @@ const customHostSettingsSchema = schema.object({ requireTLS: schema.maybe(schema.boolean()), }) ), - tls: schema.maybe( + ssl: schema.maybe( schema.object({ /** * @deprecated in favor of `verificationMode` @@ -78,16 +78,16 @@ export const configSchema = schema.object({ proxyUrl: schema.maybe(schema.string()), proxyHeaders: schema.maybe(schema.recordOf(schema.string(), schema.string())), /** - * @deprecated in favor of `tls.proxyVerificationMode` + * @deprecated in favor of `ssl.proxyVerificationMode` **/ proxyRejectUnauthorizedCertificates: schema.boolean({ defaultValue: true }), proxyBypassHosts: schema.maybe(schema.arrayOf(schema.string({ hostname: true }))), proxyOnlyHosts: schema.maybe(schema.arrayOf(schema.string({ hostname: true }))), /** - * @deprecated in favor of `tls.verificationMode` + * @deprecated in favor of `ssl.verificationMode` **/ rejectUnauthorized: schema.boolean({ defaultValue: true }), - tls: schema.maybe( + ssl: schema.maybe( schema.object({ verificationMode: schema.maybe( schema.oneOf( diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index 692ff6fa0a508..230ed826cb108 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -47,7 +47,6 @@ export type { TeamsActionTypeId, TeamsActionParams, } from './builtin_action_types'; - export type { PluginSetupContract, PluginStartContract } from './plugin'; export { asSavedObjectExecutionSource, asHttpRequestExecutionSource } from './lib'; @@ -64,19 +63,19 @@ export const config: PluginConfigDescriptor = { if ( customHostSettings.find( (customHostSchema: CustomHostSettings) => - !!customHostSchema.tls && !!customHostSchema.tls.rejectUnauthorized + !!customHostSchema.ssl && !!customHostSchema.ssl.rejectUnauthorized ) ) { addDeprecation({ message: - `"xpack.actions.customHostSettings[].tls.rejectUnauthorized" is deprecated.` + - `Use "xpack.actions.customHostSettings[].tls.verificationMode" instead, ` + + `"xpack.actions.customHostSettings[].ssl.rejectUnauthorized" is deprecated.` + + `Use "xpack.actions.customHostSettings[].ssl.verificationMode" instead, ` + `with the setting "verificationMode:full" eql to "rejectUnauthorized:true", ` + `and "verificationMode:none" eql to "rejectUnauthorized:false".`, correctiveActions: { manualSteps: [ - `Remove "xpack.actions.customHostSettings[].tls.rejectUnauthorized" from your kibana configs.`, - `Use "xpack.actions.customHostSettings[].tls.verificationMode" ` + + `Remove "xpack.actions.customHostSettings[].ssl.rejectUnauthorized" from your kibana configs.`, + `Use "xpack.actions.customHostSettings[].ssl.verificationMode" ` + `with the setting "verificationMode:full" eql to "rejectUnauthorized:true", ` + `and "verificationMode:none" eql to "rejectUnauthorized:false".`, ], diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 37d461d6b2a50..440de161490aa 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -109,6 +109,96 @@ test('successfully executes', async () => { }); expect(loggerMock.debug).toBeCalledWith('executing action test:1: 1'); + expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "event": Object { + "action": "execute-start", + }, + "kibana": Object { + "saved_objects": Array [ + Object { + "id": "1", + "namespace": "some-namespace", + "rel": "primary", + "type": "action", + "type_id": "test", + }, + ], + }, + "message": "action started: test:1: 1", + }, + ], + Array [ + Object { + "event": Object { + "action": "execute", + "outcome": "success", + }, + "kibana": Object { + "saved_objects": Array [ + Object { + "id": "1", + "namespace": "some-namespace", + "rel": "primary", + "type": "action", + "type_id": "test", + }, + ], + }, + "message": "action executed: test:1: 1", + }, + ], + ] + `); +}); + +test('successfully executes as a task', async () => { + const actionType: jest.Mocked = { + id: 'test', + name: 'Test', + minimumLicenseRequired: 'basic', + executor: jest.fn(), + }; + const actionSavedObject = { + id: '1', + type: 'action', + attributes: { + actionTypeId: 'test', + config: { + bar: true, + }, + secrets: { + baz: true, + }, + }, + references: [], + }; + const actionResult = { + id: actionSavedObject.id, + name: actionSavedObject.id, + ...pick(actionSavedObject.attributes, 'actionTypeId', 'config'), + isPreconfigured: false, + }; + actionsClient.get.mockResolvedValueOnce(actionResult); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); + actionTypeRegistry.get.mockReturnValueOnce(actionType); + + const scheduleDelay = 10000; // milliseconds + const scheduled = new Date(Date.now() - scheduleDelay); + await actionExecutor.execute({ + ...executeParams, + taskInfo: { + scheduled, + }, + }); + + const eventTask = eventLogger.logEvent.mock.calls[0][0]?.kibana?.task; + expect(eventTask).toBeDefined(); + expect(eventTask?.scheduled).toBe(scheduled.toISOString()); + expect(eventTask?.schedule_delay).toBeGreaterThanOrEqual(scheduleDelay * 1000 * 1000); + expect(eventTask?.schedule_delay).toBeLessThanOrEqual(2 * scheduleDelay * 1000 * 1000); }); test('provides empty config when config and / or secrets is empty', async () => { diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index e9e7b17288611..9e62b123951df 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -25,6 +25,9 @@ import { ActionsClient } from '../actions_client'; import { ActionExecutionSource } from './action_execution_source'; import { RelatedSavedObjects } from './related_saved_objects'; +// 1,000,000 nanoseconds in 1 millisecond +const Millis2Nanos = 1000 * 1000; + export interface ActionExecutorContext { logger: Logger; spaces?: SpacesServiceStart; @@ -39,11 +42,16 @@ export interface ActionExecutorContext { preconfiguredActions: PreConfiguredAction[]; } +export interface TaskInfo { + scheduled: Date; +} + export interface ExecuteOptions { actionId: string; request: KibanaRequest; params: Record; source?: ActionExecutionSource; + taskInfo?: TaskInfo; relatedSavedObjects?: RelatedSavedObjects; } @@ -71,6 +79,7 @@ export class ActionExecutor { params, request, source, + taskInfo, relatedSavedObjects, }: ExecuteOptions): Promise> { if (!this.isInitialized) { @@ -143,9 +152,19 @@ export class ActionExecutor { const actionLabel = `${actionTypeId}:${actionId}: ${name}`; logger.debug(`executing action ${actionLabel}`); + const task = taskInfo + ? { + task: { + scheduled: taskInfo.scheduled.toISOString(), + schedule_delay: Millis2Nanos * (Date.now() - taskInfo.scheduled.getTime()), + }, + } + : {}; + const event: IEvent = { event: { action: EVENT_LOG_ACTIONS.execute }, kibana: { + ...task, saved_objects: [ { rel: SAVED_OBJECT_REL_PRIMARY, diff --git a/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts b/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts index ad07ea21d7917..ec7b46e545112 100644 --- a/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts +++ b/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts @@ -112,14 +112,14 @@ describe('custom_host_settings', () => { customHostSettings: [ { url: 'https://elastic.co:443', - tls: { + ssl: { certificateAuthoritiesData: 'xyz', rejectUnauthorized: false, }, }, { url: 'smtp://mail.elastic.com:25', - tls: { + ssl: { certificateAuthoritiesData: 'abc', rejectUnauthorized: true, }, @@ -338,7 +338,7 @@ describe('custom_host_settings', () => { customHostSettings: [ { url: 'https://almost.purrfect.com/', - tls: { + ssl: { certificateAuthoritiesFiles: 'this-file-does-not-exist', }, }, @@ -350,7 +350,7 @@ describe('custom_host_settings', () => { customHostSettings: [ { url: 'https://almost.purrfect.com:443', - tls: { + ssl: { certificateAuthoritiesFiles: 'this-file-does-not-exist', }, }, @@ -371,7 +371,7 @@ describe('custom_host_settings', () => { customHostSettings: [ { url: 'https://almost.purrfect.com/', - tls: { + ssl: { certificateAuthoritiesFiles: CA_FILE1, }, }, @@ -380,7 +380,7 @@ describe('custom_host_settings', () => { const resConfig = resolveCustomHosts(mockLogger, config); // not checking the full structure anymore, just ca bits - expect(resConfig?.customHostSettings?.[0].tls?.certificateAuthoritiesData).toBe(CA_CONTENTS1); + expect(resConfig?.customHostSettings?.[0].ssl?.certificateAuthoritiesData).toBe(CA_CONTENTS1); expect(warningLogs()).toEqual([]); }); @@ -390,7 +390,7 @@ describe('custom_host_settings', () => { customHostSettings: [ { url: 'https://almost.purrfect.com/', - tls: { + ssl: { certificateAuthoritiesFiles: [CA_FILE1, CA_FILE2], }, }, @@ -399,7 +399,7 @@ describe('custom_host_settings', () => { const resConfig = resolveCustomHosts(mockLogger, config); // not checking the full structure anymore, just ca bits - expect(resConfig?.customHostSettings?.[0].tls?.certificateAuthoritiesData).toBe( + expect(resConfig?.customHostSettings?.[0].ssl?.certificateAuthoritiesData).toBe( `${CA_CONTENTS1}\n${CA_CONTENTS2}` ); expect(warningLogs()).toEqual([]); @@ -411,7 +411,7 @@ describe('custom_host_settings', () => { customHostSettings: [ { url: 'https://almost.purrfect.com/', - tls: { + ssl: { certificateAuthoritiesFiles: [CA_FILE2], certificateAuthoritiesData: CA_CONTENTS1, }, @@ -421,7 +421,7 @@ describe('custom_host_settings', () => { const resConfig = resolveCustomHosts(mockLogger, config); // not checking the full structure anymore, just ca bits - expect(resConfig?.customHostSettings?.[0].tls?.certificateAuthoritiesData).toBe( + expect(resConfig?.customHostSettings?.[0].ssl?.certificateAuthoritiesData).toBe( `${CA_CONTENTS1}\n${CA_CONTENTS2}` ); expect(warningLogs()).toEqual([]); @@ -468,13 +468,13 @@ describe('custom_host_settings', () => { customHostSettings: [ { url: 'https://almost.purrfect.com/', - tls: { + ssl: { rejectUnauthorized: true, }, }, { url: 'https://almost.purrfect.com:443', - tls: { + ssl: { rejectUnauthorized: false, }, }, @@ -486,7 +486,7 @@ describe('custom_host_settings', () => { customHostSettings: [ { url: 'https://almost.purrfect.com:443', - tls: { + ssl: { rejectUnauthorized: true, }, }, diff --git a/x-pack/plugins/actions/server/lib/custom_host_settings.ts b/x-pack/plugins/actions/server/lib/custom_host_settings.ts index bfc8dad48aab6..0ff8624d42cfe 100644 --- a/x-pack/plugins/actions/server/lib/custom_host_settings.ts +++ b/x-pack/plugins/actions/server/lib/custom_host_settings.ts @@ -86,8 +86,8 @@ export function resolveCustomHosts(logger: Logger, config: ActionsConfig): Actio } // read the specified ca files, add their content to certificateAuthoritiesData - if (customHostSetting.tls) { - let files = customHostSetting.tls?.certificateAuthoritiesFiles || []; + if (customHostSetting.ssl) { + let files = customHostSetting.ssl?.certificateAuthoritiesFiles || []; if (typeof files === 'string') { files = [files]; } @@ -134,12 +134,12 @@ export function resolveCustomHosts(logger: Logger, config: ActionsConfig): Actio } function appendToCertificateAuthoritiesData(customHost: CustomHostSettingsWriteable, cert: string) { - const tls = customHost.tls; - if (tls) { - if (!tls.certificateAuthoritiesData) { - tls.certificateAuthoritiesData = cert; + const ssl = customHost.ssl; + if (ssl) { + if (!ssl.certificateAuthoritiesData) { + ssl.certificateAuthoritiesData = cert; } else { - tls.certificateAuthoritiesData += '\n' + cert; + ssl.certificateAuthoritiesData += '\n' + cert; } } } diff --git a/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts b/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts index 2292994e3ccfd..495d638951b56 100644 --- a/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts +++ b/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts @@ -133,6 +133,9 @@ test('executes the task by calling the executor with proper parameters', async ( authorization: 'ApiKey MTIzOmFiYw==', }, }), + taskInfo: { + scheduled: new Date(), + }, }); const [executeParams] = mockedActionExecutor.execute.mock.calls[0]; @@ -255,6 +258,9 @@ test('uses API key when provided', async () => { authorization: 'ApiKey MTIzOmFiYw==', }, }), + taskInfo: { + scheduled: new Date(), + }, }); const [executeParams] = mockedActionExecutor.execute.mock.calls[0]; @@ -300,6 +306,9 @@ test('uses relatedSavedObjects when provided', async () => { authorization: 'ApiKey MTIzOmFiYw==', }, }), + taskInfo: { + scheduled: new Date(), + }, }); }); @@ -323,7 +332,6 @@ test('sanitizes invalid relatedSavedObjects when provided', async () => { }); await taskRunner.run(); - expect(mockedActionExecutor.execute).toHaveBeenCalledWith({ actionId: '2', params: { baz: true }, @@ -334,6 +342,9 @@ test('sanitizes invalid relatedSavedObjects when provided', async () => { authorization: 'ApiKey MTIzOmFiYw==', }, }), + taskInfo: { + scheduled: new Date(), + }, }); }); @@ -363,6 +374,9 @@ test(`doesn't use API key when not provided`, async () => { request: expect.objectContaining({ headers: {}, }), + taskInfo: { + scheduled: new Date(), + }, }); const [executeParams] = mockedActionExecutor.execute.mock.calls[0]; diff --git a/x-pack/plugins/actions/server/lib/task_runner_factory.ts b/x-pack/plugins/actions/server/lib/task_runner_factory.ts index 0515963ab82f4..64169de728f75 100644 --- a/x-pack/plugins/actions/server/lib/task_runner_factory.ts +++ b/x-pack/plugins/actions/server/lib/task_runner_factory.ts @@ -72,6 +72,10 @@ export class TaskRunnerFactory { getUnsecuredSavedObjectsClient, } = this.taskRunnerContext!; + const taskInfo = { + scheduled: taskInstance.runAt, + }; + return { async run() { const { spaceId, actionTaskParamsId } = taskInstance.params as Record; @@ -118,6 +122,7 @@ export class TaskRunnerFactory { actionId, request: fakeRequest, ...getSourceFromReferences(references), + taskInfo, relatedSavedObjects: validatedRelatedSavedObjects(logger, relatedSavedObjects), }); } catch (e) { diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index c8c9967afca1a..7c05d16923b9d 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -22,7 +22,7 @@ export { ActionTypeExecutorResult } from '../common'; export { GetFieldsByIssueTypeResponse as JiraGetFieldsResponse } from './builtin_action_types/jira/types'; export { GetCommonFieldsResponse as ServiceNowGetFieldsResponse } from './builtin_action_types/servicenow/types'; export { GetCommonFieldsResponse as ResilientGetFieldsResponse } from './builtin_action_types/resilient/types'; - +export { SwimlanePublicConfigurationType } from './builtin_action_types/swimlane/types'; export type WithoutQueryAndParams = Pick>; export type GetServicesFunction = (request: KibanaRequest) => Services; export type ActionTypeRegistryContract = PublicMethodsOf; @@ -142,7 +142,7 @@ export interface ProxySettings { proxyBypassHosts: Set | undefined; proxyOnlyHosts: Set | undefined; proxyHeaders?: Record; - proxyTLSSettings: TLSSettings; + proxySSLSettings: SSLSettings; } export interface ResponseSettings { @@ -150,6 +150,6 @@ export interface ResponseSettings { timeout: number; } -export interface TLSSettings { +export interface SSLSettings { verificationMode?: 'none' | 'certificate' | 'full'; } diff --git a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts index 06248e1fa95a8..80e0c19092c78 100644 --- a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts +++ b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts @@ -18,6 +18,7 @@ const byTypeSchema: MakeSchemaFrom['count_by_type'] = { __email: { type: 'long' }, __index: { type: 'long' }, __pagerduty: { type: 'long' }, + __swimlane: { type: 'long' }, '__server-log': { type: 'long' }, __slack: { type: 'long' }, __webhook: { type: 'long' }, diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts index 033ffcceb6a0a..1dcd19119b6fd 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts @@ -195,7 +195,6 @@ test('enqueues execution per selected action', async () => { "id": "1", "license": "basic", "name": "name-of-alert", - "namespace": "test1", "ruleset": "alerts", }, }, diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index 968fff540dc03..3004ed599128e 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -209,7 +209,6 @@ export function createExecutionHandler< license: alertType.minimumLicenseRequired, category: alertType.id, ruleset: alertType.producer, - ...namespace, name: alertName, }, }; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 8ab267a5610d3..88d1b1b24a4ec 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -282,13 +282,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, } @@ -394,6 +397,10 @@ describe('Task Runner', () => { kind: 'alert', }, kibana: { + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, saved_objects: [ { id: '1', @@ -409,7 +416,6 @@ describe('Task Runner', () => { category: 'test', id: '1', license: 'basic', - namespace: undefined, ruleset: 'alerts', }, }); @@ -518,6 +524,10 @@ describe('Task Runner', () => { alerting: { status: 'active', }, + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, saved_objects: [ { id: '1', @@ -534,7 +544,6 @@ describe('Task Runner', () => { id: '1', license: 'basic', name: 'alert-name', - namespace: undefined, ruleset: 'alerts', }, }); @@ -603,6 +612,10 @@ describe('Task Runner', () => { kind: 'alert', }, kibana: { + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, saved_objects: [ { id: '1', @@ -618,7 +631,6 @@ describe('Task Runner', () => { category: 'test', id: '1', license: 'basic', - namespace: undefined, ruleset: 'alerts', }, }); @@ -700,6 +712,10 @@ describe('Task Runner', () => { alerting: { status: 'active', }, + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, saved_objects: [ { id: '1', @@ -716,7 +732,6 @@ describe('Task Runner', () => { id: '1', license: 'basic', name: 'alert-name', - namespace: undefined, ruleset: 'alerts', }, }); @@ -854,13 +869,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -897,7 +915,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -926,6 +943,10 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert executed: test:1: 'alert-name'", "rule": Object { @@ -933,7 +954,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1151,13 +1171,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1194,7 +1217,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1231,7 +1253,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1273,7 +1294,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1302,6 +1322,10 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert executed: test:1: 'alert-name'", "rule": Object { @@ -1309,7 +1333,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1433,13 +1456,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1476,7 +1502,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1513,7 +1538,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1555,7 +1579,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1597,7 +1620,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1626,6 +1648,10 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert executed: test:1: 'alert-name'", "rule": Object { @@ -1633,7 +1659,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -1968,13 +1993,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2012,7 +2040,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2049,7 +2076,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2078,6 +2104,10 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert executed: test:1: 'alert-name'", "rule": Object { @@ -2085,7 +2115,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2294,13 +2323,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2333,13 +2365,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution failure: test:1: 'alert-name'", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2397,13 +2432,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2436,13 +2474,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "test:1: execution failed", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2508,13 +2549,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2547,13 +2591,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "test:1: execution failed", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2619,13 +2666,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2658,13 +2708,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "test:1: execution failed", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2729,13 +2782,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -2768,13 +2824,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "test:1: execution failed", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3007,13 +3066,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3050,7 +3112,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3087,7 +3148,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3124,7 +3184,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3161,7 +3220,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3190,6 +3248,10 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert executed: test:1: 'alert-name'", "rule": Object { @@ -3197,7 +3259,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3291,13 +3352,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3334,7 +3398,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3371,7 +3434,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3400,6 +3462,10 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert executed: test:1: 'alert-name'", "rule": Object { @@ -3407,7 +3473,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3493,13 +3558,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3534,7 +3602,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3569,7 +3636,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3598,6 +3664,10 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert executed: test:1: 'alert-name'", "rule": Object { @@ -3605,7 +3675,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3686,13 +3755,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3729,7 +3801,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3766,7 +3837,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3795,6 +3865,10 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert executed: test:1: 'alert-name'", "rule": Object { @@ -3802,7 +3876,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3885,13 +3958,16 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert execution start: \\"1\\"", "rule": Object { "category": "test", "id": "1", "license": "basic", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3925,7 +4001,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3959,7 +4034,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, @@ -3988,6 +4062,10 @@ describe('Task Runner', () => { "type_id": "test", }, ], + "task": Object { + "schedule_delay": 0, + "scheduled": "1970-01-01T00:00:00.000Z", + }, }, "message": "alert executed: test:1: 'alert-name'", "rule": Object { @@ -3995,7 +4073,6 @@ describe('Task Runner', () => { "id": "1", "license": "basic", "name": "alert-name", - "namespace": undefined, "ruleset": "alerts", }, }, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index b712b6237c8a7..c66c054bc8ac3 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -54,6 +54,9 @@ import { getEsErrorMessage } from '../lib/errors'; const FALLBACK_RETRY_INTERVAL = '5m'; +// 1,000,000 nanoseconds in 1 millisecond +const Millis2Nanos = 1000 * 1000; + type Event = Exclude; interface AlertTaskRunResult { @@ -489,15 +492,17 @@ export class TaskRunner< schedule: taskSchedule, } = this.taskInstance; - const runDate = new Date().toISOString(); - this.logger.debug(`executing alert ${this.alertType.id}:${alertId} at ${runDate}`); + const runDate = new Date(); + const runDateString = runDate.toISOString(); + this.logger.debug(`executing alert ${this.alertType.id}:${alertId} at ${runDateString}`); const namespace = this.context.spaceIdToNamespace(spaceId); const eventLogger = this.context.eventLogger; + const scheduleDelay = runDate.getTime() - this.taskInstance.runAt.getTime(); const event: IEvent = { // explicitly set execute timestamp so it will be before other events // generated here (new-instance, schedule-action, etc) - '@timestamp': runDate, + '@timestamp': runDateString, event: { action: EVENT_LOG_ACTIONS.execute, kind: 'alert', @@ -513,13 +518,16 @@ export class TaskRunner< namespace, }, ], + task: { + scheduled: this.taskInstance.runAt.toISOString(), + schedule_delay: Millis2Nanos * scheduleDelay, + }, }, rule: { id: alertId, license: this.alertType.minimumLicenseRequired, category: this.alertType.id, ruleset: this.alertType.producer, - namespace, }, }; @@ -814,7 +822,6 @@ function generateNewAndRecoveredInstanceEvents< license: ruleType.minimumLicenseRequired, category: ruleType.id, ruleset: ruleType.producer, - namespace, name: rule.name, }, }; diff --git a/x-pack/plugins/apm/public/assets/illustration_integrations_darkmode.svg b/x-pack/plugins/apm/public/assets/illustration_integrations_darkmode.svg new file mode 100644 index 0000000000000..b1f86be19a080 --- /dev/null +++ b/x-pack/plugins/apm/public/assets/illustration_integrations_darkmode.svg @@ -0,0 +1 @@ +Kibana-integrations-darkmode \ No newline at end of file diff --git a/x-pack/plugins/apm/public/assets/illustration_integrations_lightmode.svg b/x-pack/plugins/apm/public/assets/illustration_integrations_lightmode.svg new file mode 100644 index 0000000000000..0cddcb0af6909 --- /dev/null +++ b/x-pack/plugins/apm/public/assets/illustration_integrations_lightmode.svg @@ -0,0 +1 @@ +Kibana-integrations-lightmode \ No newline at end of file diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/select_anomaly_severity.test.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/select_anomaly_severity.test.tsx index 7b56eaa4721de..8c8f0aa8b9b24 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/select_anomaly_severity.test.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/select_anomaly_severity.test.tsx @@ -7,7 +7,7 @@ import { render } from '@testing-library/react'; import React, { ReactNode } from 'react'; -import { IntlProvider } from 'react-intl'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n/react'; import { ANOMALY_SEVERITY } from '../../../../common/ml_constants'; import { SelectAnomalySeverity } from './select_anomaly_severity'; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx index 20d930d28599f..63ba7047696ca 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx @@ -47,10 +47,11 @@ export function UXActionMenu({ const uxExploratoryViewLink = createExploratoryViewUrl( { - 'ux-series': { + 'ux-series': ({ dataType: 'ux', + isNew: true, time: { from: rangeFrom, to: rangeTo }, - } as SeriesUrl, + } as unknown) as SeriesUrl, }, http?.basePath.get() ); diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx index e0486af6cd6ef..5c63cc24b6fdf 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/PageLoadDistribution/index.tsx @@ -89,7 +89,7 @@ export function PageLoadDistribution() { { [`${serviceName}-page-views`]: { dataType: 'ux', - reportType: 'dist', + reportType: 'data-distribution', time: { from: rangeFrom!, to: rangeTo! }, reportDefinitions: { 'service.name': serviceName as string[], diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx index c45637e5d3c82..667d0b5e4b4db 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/PageViewsTrend/index.tsx @@ -64,7 +64,7 @@ export function PageViewsTrend() { { [`${serviceName}-page-views`]: { dataType: 'ux', - reportType: 'kpi', + reportType: 'kpi-over-time', time: { from: rangeFrom!, to: rangeTo! }, reportDefinitions: { 'service.name': serviceName as string[], diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/ResponsiveFlyout.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/ResponsiveFlyout.tsx index 8549f09bba248..09fbf07b8ecbd 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/ResponsiveFlyout.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/ResponsiveFlyout.tsx @@ -5,10 +5,21 @@ * 2.0. */ +import { ReactNode } from 'react'; +import { StyledComponent } from 'styled-components'; import { EuiFlyout } from '@elastic/eui'; -import { euiStyled } from '../../../../../../../../../../src/plugins/kibana_react/common'; +import { + euiStyled, + EuiTheme, +} from '../../../../../../../../../../src/plugins/kibana_react/common'; -export const ResponsiveFlyout = euiStyled(EuiFlyout)` +// TODO: EUI team follow up on complex types and styled-components `styled` +// https://github.com/elastic/eui/issues/4855 +export const ResponsiveFlyout: StyledComponent< + typeof EuiFlyout, + EuiTheme, + { children?: ReactNode } +> = euiStyled(EuiFlyout)` width: 100%; @media (min-width: 800px) { diff --git a/x-pack/plugins/apm/public/components/routing/app_root.tsx b/x-pack/plugins/apm/public/components/routing/app_root.tsx index 2bb387ae315ff..8fc59a01eeca0 100644 --- a/x-pack/plugins/apm/public/components/routing/app_root.tsx +++ b/x-pack/plugins/apm/public/components/routing/app_root.tsx @@ -24,7 +24,7 @@ import { } from '../../context/apm_plugin/apm_plugin_context'; import { LicenseProvider } from '../../context/license/license_context'; import { UrlParamsProvider } from '../../context/url_params_context/url_params_context'; -import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { useApmBreadcrumbs } from '../../hooks/use_apm_breadcrumbs'; import { ApmPluginStartDeps } from '../../plugin'; import { HeaderMenuPortal } from '../../../../observability/public'; import { ApmHeaderActionMenu } from '../shared/apm_header_action_menu'; @@ -79,7 +79,7 @@ export function ApmAppRoot({ } function MountApmHeaderActionMenu() { - useBreadcrumbs(apmRouteConfig); + useApmBreadcrumbs(apmRouteConfig); const { setHeaderActionMenu } = useApmPluginContext().appMountParameters; return ( diff --git a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx index a16f81826636b..bcc1932dde7cb 100644 --- a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx +++ b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx @@ -44,6 +44,7 @@ const mockCore = { ml: {}, }, currentAppId$: new Observable(), + getUrlForApp: (appId: string) => '', navigateToUrl: (url: string) => {}, }, chrome: { diff --git a/x-pack/plugins/apm/public/hooks/use_breadcrumbs.test.tsx b/x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.test.tsx similarity index 79% rename from x-pack/plugins/apm/public/hooks/use_breadcrumbs.test.tsx rename to x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.test.tsx index 64990651b52bb..1cdb84c324750 100644 --- a/x-pack/plugins/apm/public/hooks/use_breadcrumbs.test.tsx +++ b/x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.test.tsx @@ -15,14 +15,15 @@ import { mockApmPluginContextValue, MockApmPluginContextWrapper, } from '../context/apm_plugin/mock_apm_plugin_context'; -import { useBreadcrumbs } from './use_breadcrumbs'; +import { useApmBreadcrumbs } from './use_apm_breadcrumbs'; +import { useBreadcrumbs } from '../../../observability/public'; + +jest.mock('../../../observability/public'); function createWrapper(path: string) { return ({ children }: { children?: ReactNode }) => { const value = (produce(mockApmPluginContextValue, (draft) => { draft.core.application.navigateToUrl = (url: string) => Promise.resolve(); - draft.core.chrome.docTitle.change = changeTitle; - draft.core.chrome.setBreadcrumbs = setBreadcrumbs; }) as unknown) as ApmPluginContextValue; return ( @@ -36,27 +37,18 @@ function createWrapper(path: string) { } function mountBreadcrumb(path: string) { - renderHook(() => useBreadcrumbs(apmRouteConfig), { + renderHook(() => useApmBreadcrumbs(apmRouteConfig), { wrapper: createWrapper(path), }); } -const changeTitle = jest.fn(); -const setBreadcrumbs = jest.fn(); - -describe('useBreadcrumbs', () => { - it('changes the page title', () => { - mountBreadcrumb('/'); - - expect(changeTitle).toHaveBeenCalledWith(['APM']); - }); - +describe('useApmBreadcrumbs', () => { test('/services/:serviceName/errors/:groupId', () => { mountBreadcrumb( '/services/opbeans-node/errors/myGroupId?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0' ); - expect(setBreadcrumbs).toHaveBeenCalledWith( + expect(useBreadcrumbs).toHaveBeenCalledWith( expect.arrayContaining([ expect.objectContaining({ text: 'APM', @@ -81,20 +73,12 @@ describe('useBreadcrumbs', () => { expect.objectContaining({ text: 'myGroupId', href: undefined }), ]) ); - - expect(changeTitle).toHaveBeenCalledWith([ - 'myGroupId', - 'Errors', - 'opbeans-node', - 'Services', - 'APM', - ]); }); test('/services/:serviceName/errors', () => { mountBreadcrumb('/services/opbeans-node/errors?kuery=myKuery'); - expect(setBreadcrumbs).toHaveBeenCalledWith( + expect(useBreadcrumbs).toHaveBeenCalledWith( expect.arrayContaining([ expect.objectContaining({ text: 'APM', @@ -111,19 +95,12 @@ describe('useBreadcrumbs', () => { expect.objectContaining({ text: 'Errors', href: undefined }), ]) ); - - expect(changeTitle).toHaveBeenCalledWith([ - 'Errors', - 'opbeans-node', - 'Services', - 'APM', - ]); }); test('/services/:serviceName/transactions', () => { mountBreadcrumb('/services/opbeans-node/transactions?kuery=myKuery'); - expect(setBreadcrumbs).toHaveBeenCalledWith( + expect(useBreadcrumbs).toHaveBeenCalledWith( expect.arrayContaining([ expect.objectContaining({ text: 'APM', @@ -140,13 +117,6 @@ describe('useBreadcrumbs', () => { expect.objectContaining({ text: 'Transactions', href: undefined }), ]) ); - - expect(changeTitle).toHaveBeenCalledWith([ - 'Transactions', - 'opbeans-node', - 'Services', - 'APM', - ]); }); test('/services/:serviceName/transactions/view?transactionName=my-transaction-name', () => { @@ -154,7 +124,7 @@ describe('useBreadcrumbs', () => { '/services/opbeans-node/transactions/view?kuery=myKuery&transactionName=my-transaction-name' ); - expect(setBreadcrumbs).toHaveBeenCalledWith( + expect(useBreadcrumbs).toHaveBeenCalledWith( expect.arrayContaining([ expect.objectContaining({ text: 'APM', @@ -179,13 +149,5 @@ describe('useBreadcrumbs', () => { }), ]) ); - - expect(changeTitle).toHaveBeenCalledWith([ - 'my-transaction-name', - 'Transactions', - 'opbeans-node', - 'Services', - 'APM', - ]); }); }); diff --git a/x-pack/plugins/apm/public/hooks/use_breadcrumbs.ts b/x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.ts similarity index 85% rename from x-pack/plugins/apm/public/hooks/use_breadcrumbs.ts rename to x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.ts index d907c27319d26..d64bcadf79577 100644 --- a/x-pack/plugins/apm/public/hooks/use_breadcrumbs.ts +++ b/x-pack/plugins/apm/public/hooks/use_apm_breadcrumbs.ts @@ -7,14 +7,15 @@ import { History, Location } from 'history'; import { ChromeBreadcrumb } from 'kibana/public'; -import { MouseEvent, ReactNode, useEffect } from 'react'; +import { MouseEvent } from 'react'; import { + match as Match, matchPath, RouteComponentProps, useHistory, - match as Match, useLocation, } from 'react-router-dom'; +import { useBreadcrumbs } from '../../../observability/public'; import { APMRouteDefinition, BreadcrumbTitle } from '../application/routes'; import { getAPMHref } from '../components/shared/Links/apm/APMLink'; import { useApmPluginContext } from '../context/apm_plugin/use_apm_plugin_context'; @@ -164,33 +165,17 @@ function routeDefinitionsToBreadcrumbs({ return breadcrumbs; } -/** - * Get an array for a page title from a list of breadcrumbs - */ -function getTitleFromBreadcrumbs(breadcrumbs: ChromeBreadcrumb[]): string[] { - function removeNonStrings(item: ReactNode): item is string { - return typeof item === 'string'; - } - - return breadcrumbs - .map(({ text }) => text) - .reverse() - .filter(removeNonStrings); -} - /** * Determine the breadcrumbs from the routes, set them, and update the page * title when the route changes. */ -export function useBreadcrumbs(routes: APMRouteDefinition[]) { +export function useApmBreadcrumbs(routes: APMRouteDefinition[]) { const history = useHistory(); const location = useLocation(); const { search } = location; const { core } = useApmPluginContext(); const { basePath } = core.http; const { navigateToUrl } = core.application; - const { docTitle, setBreadcrumbs } = core.chrome; - const changeTitle = docTitle.change; function wrappedGetAPMHref(path: string) { return getAPMHref({ basePath, path, search }); @@ -206,10 +191,6 @@ export function useBreadcrumbs(routes: APMRouteDefinition[]) { wrappedGetAPMHref, navigateToUrl, }); - const title = getTitleFromBreadcrumbs(breadcrumbs); - useEffect(() => { - changeTitle(title); - setBreadcrumbs(breadcrumbs); - }, [breadcrumbs, changeTitle, location, title, setBreadcrumbs]); + useBreadcrumbs(breadcrumbs); } diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index 77e7f2834b080..012856ca9213c 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { i18n } from '@kbn/i18n'; import { from } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -140,16 +139,42 @@ export class ApmPlugin implements Plugin { ); const getApmDataHelper = async () => { - const { - fetchObservabilityOverviewPageData, - getHasData, - createCallApmApi, - } = await import('./services/rest/apm_observability_overview_fetchers'); + const { fetchObservabilityOverviewPageData, getHasData } = await import( + './services/rest/apm_observability_overview_fetchers' + ); + const { hasFleetApmIntegrations } = await import( + './tutorial/tutorial_apm_fleet_check' + ); + + const { createCallApmApi } = await import( + './services/rest/createCallApmApi' + ); + // have to do this here as well in case app isn't mounted yet createCallApmApi(core); - return { fetchObservabilityOverviewPageData, getHasData }; + return { + fetchObservabilityOverviewPageData, + getHasData, + hasFleetApmIntegrations, + }; }; + + // Registers a status check callback for the tutorial to call and verify if the APM integration is installed on fleet. + pluginSetupDeps.home?.tutorials.registerCustomStatusCheck( + 'apm_fleet_server_status_check', + async () => { + const { hasFleetApmIntegrations } = await getApmDataHelper(); + return hasFleetApmIntegrations(); + } + ); + + // Registers custom component that is going to be render on fleet section + pluginSetupDeps.home?.tutorials.registerCustomComponent( + 'TutorialFleetInstructions', + () => import('./tutorial/tutorial_fleet_instructions') + ); + plugins.observability.dashboard.register({ appName: 'apm', hasData: async () => { @@ -163,11 +188,12 @@ export class ApmPlugin implements Plugin { }); const getUxDataHelper = async () => { - const { - fetchUxOverviewDate, - hasRumData, - createCallApmApi, - } = await import('./components/app/RumDashboard/ux_overview_fetchers'); + const { fetchUxOverviewDate, hasRumData } = await import( + './components/app/RumDashboard/ux_overview_fetchers' + ); + const { createCallApmApi } = await import( + './services/rest/createCallApmApi' + ); // have to do this here as well in case app isn't mounted yet createCallApmApi(core); diff --git a/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts b/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts index ef61e25af4fc2..1b95c88a5fdc5 100644 --- a/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts +++ b/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts @@ -11,8 +11,6 @@ import { } from '../../../../observability/public'; import { callApmApi } from './createCallApmApi'; -export { createCallApmApi } from './createCallApmApi'; - export const fetchObservabilityOverviewPageData = async ({ absoluteTime, relativeTime, diff --git a/x-pack/plugins/apm/public/tutorial/tutorial_apm_fleet_check.ts b/x-pack/plugins/apm/public/tutorial/tutorial_apm_fleet_check.ts new file mode 100644 index 0000000000000..8db8614d606a9 --- /dev/null +++ b/x-pack/plugins/apm/public/tutorial/tutorial_apm_fleet_check.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { callApmApi } from '../services/rest/createCallApmApi'; + +export async function hasFleetApmIntegrations() { + try { + const { hasData = false } = await callApmApi({ + endpoint: 'GET /api/apm/fleet/has_data', + signal: null, + }); + return hasData; + } catch (e) { + console.error('Something went wrong while fetching apm fleet data', e); + return false; + } +} diff --git a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx new file mode 100644 index 0000000000000..8a81b7a994e76 --- /dev/null +++ b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiButton } from '@elastic/eui'; +import { EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup } from '@elastic/eui'; +import { EuiPanel } from '@elastic/eui'; +import { EuiCard } from '@elastic/eui'; +import { EuiImage } from '@elastic/eui'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { HttpStart } from 'kibana/public'; +import React, { useEffect, useState } from 'react'; +import styled from 'styled-components'; +import { APIReturnType } from '../../services/rest/createCallApmApi'; + +interface Props { + http: HttpStart; + basePath: string; + isDarkTheme: boolean; +} + +const CentralizedContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; +`; + +type APIResponseType = APIReturnType<'GET /api/apm/fleet/has_data'>; + +function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) { + const [data, setData] = useState(); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + async function fetchData() { + setIsLoading(true); + try { + const response = await http.get('/api/apm/fleet/has_data'); + setData(response as APIResponseType); + } catch (e) { + console.error('Error while fetching fleet details.', e); + } + setIsLoading(false); + } + fetchData(); + }, [http]); + + if (isLoading) { + return ( + + + + ); + } + + // When APM integration is enable in Fleet + if (data?.hasData) { + return ( + + {i18n.translate( + 'xpack.apm.tutorial.apmServer.fleet.manageApmIntegration.button', + { + defaultMessage: 'Manage APM integration in Fleet', + } + )} + + ); + } + // When APM integration is not installed in Fleet or for some reason the API didn't work out + return ( + + + + + {i18n.translate( + 'xpack.apm.tutorial.apmServer.fleet.apmIntegration.button', + { + defaultMessage: 'APM integration', + } + )} + + } + /> + + + + + + + ); +} +// eslint-disable-next-line import/no-default-export +export default TutorialFleetInstructions; diff --git a/x-pack/plugins/apm/server/lib/fleet/source_maps.test.ts b/x-pack/plugins/apm/server/lib/fleet/source_maps.test.ts index 61a4fa4436e69..d6a1770a91591 100644 --- a/x-pack/plugins/apm/server/lib/fleet/source_maps.test.ts +++ b/x-pack/plugins/apm/server/lib/fleet/source_maps.test.ts @@ -103,12 +103,51 @@ const artifacts = [ describe('Source maps', () => { describe('getPackagePolicyWithSourceMap', () => { - it('returns unchanged package policy when artifacts is empty', () => { + it('removes source map from package policy', () => { + const packagePolicyWithSourceMaps = { + ...packagePolicy, + inputs: [ + { + ...packagePolicy.inputs[0], + compiled_input: { + 'apm-server': { + ...packagePolicy.inputs[0].compiled_input['apm-server'], + value: { + rum: { + source_mapping: { + metadata: [ + { + 'service.name': 'service_name', + 'service.version': '1.0.0', + 'bundle.filepath': + 'http://localhost:3000/static/js/main.chunk.js', + 'sourcemap.url': + '/api/fleet/artifacts/service_name-1.0.0/my-id-1', + }, + { + 'service.name': 'service_name', + 'service.version': '2.0.0', + 'bundle.filepath': + 'http://localhost:3000/static/js/main.chunk.js', + 'sourcemap.url': + '/api/fleet/artifacts/service_name-2.0.0/my-id-2', + }, + ], + }, + }, + }, + }, + }, + }, + ], + }; const updatedPackagePolicy = getPackagePolicyWithSourceMap({ - packagePolicy, + packagePolicy: packagePolicyWithSourceMaps, artifacts: [], }); - expect(updatedPackagePolicy).toEqual(packagePolicy); + expect(updatedPackagePolicy.inputs[0].config).toEqual({ + 'apm-server': { value: { rum: { source_mapping: { metadata: [] } } } }, + }); }); it('adds source maps into the package policy', () => { const updatedPackagePolicy = getPackagePolicyWithSourceMap({ diff --git a/x-pack/plugins/apm/server/lib/fleet/source_maps.ts b/x-pack/plugins/apm/server/lib/fleet/source_maps.ts index b313fbad2806f..6d608f7751f3b 100644 --- a/x-pack/plugins/apm/server/lib/fleet/source_maps.ts +++ b/x-pack/plugins/apm/server/lib/fleet/source_maps.ts @@ -97,9 +97,6 @@ export function getPackagePolicyWithSourceMap({ packagePolicy: PackagePolicy; artifacts: ArtifactSourceMap[]; }) { - if (!artifacts.length) { - return packagePolicy; - } const [firstInput, ...restInputs] = packagePolicy.inputs; return { ...packagePolicy, diff --git a/x-pack/plugins/apm/server/routes/fleet.ts b/x-pack/plugins/apm/server/routes/fleet.ts new file mode 100644 index 0000000000000..74ca8dc368dad --- /dev/null +++ b/x-pack/plugins/apm/server/routes/fleet.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import Boom from '@hapi/boom'; +import { i18n } from '@kbn/i18n'; +import { getApmPackgePolicies } from '../lib/fleet/get_apm_package_policies'; +import { createApmServerRoute } from './create_apm_server_route'; +import { createApmServerRouteRepository } from './create_apm_server_route_repository'; + +const hasFleetDataRoute = createApmServerRoute({ + endpoint: 'GET /api/apm/fleet/has_data', + options: { tags: [] }, + handler: async ({ core, plugins }) => { + const fleetPluginStart = await plugins.fleet?.start(); + if (!fleetPluginStart) { + throw Boom.internal( + i18n.translate('xpack.apm.fleet_has_data.fleetRequired', { + defaultMessage: `Fleet plugin is required`, + }) + ); + } + const packagePolicies = await getApmPackgePolicies({ + core, + fleetPluginStart, + }); + return { hasData: packagePolicies.total > 0 }; + }, +}); + +export const ApmFleetRouteRepository = createApmServerRouteRepository().add( + hasFleetDataRoute +); diff --git a/x-pack/plugins/apm/server/routes/get_global_apm_server_route_repository.ts b/x-pack/plugins/apm/server/routes/get_global_apm_server_route_repository.ts index f1c08444d2e1e..fa2f80f073958 100644 --- a/x-pack/plugins/apm/server/routes/get_global_apm_server_route_repository.ts +++ b/x-pack/plugins/apm/server/routes/get_global_apm_server_route_repository.ts @@ -30,6 +30,7 @@ import { sourceMapsRouteRepository } from './source_maps'; import { traceRouteRepository } from './traces'; import { transactionRouteRepository } from './transactions'; import { APMRouteHandlerResources } from './typings'; +import { ApmFleetRouteRepository } from './fleet'; const getTypedGlobalApmServerRouteRepository = () => { const repository = createApmServerRouteRepository() @@ -50,7 +51,8 @@ const getTypedGlobalApmServerRouteRepository = () => { .merge(anomalyDetectionRouteRepository) .merge(apmIndicesRouteRepository) .merge(customLinkRouteRepository) - .merge(sourceMapsRouteRepository); + .merge(sourceMapsRouteRepository) + .merge(ApmFleetRouteRepository); return repository; }; diff --git a/x-pack/plugins/apm/server/routes/source_maps.ts b/x-pack/plugins/apm/server/routes/source_maps.ts index 24ea825774b0a..f6d160e68a76a 100644 --- a/x-pack/plugins/apm/server/routes/source_maps.ts +++ b/x-pack/plugins/apm/server/routes/source_maps.ts @@ -5,6 +5,7 @@ * 2.0. */ import Boom from '@hapi/boom'; +import { jsonRt } from '@kbn/io-ts-utils'; import * as t from 'io-ts'; import { SavedObjectsClientContract } from 'kibana/server'; import { @@ -34,7 +35,7 @@ export const sourceMapRt = t.intersection([ const listSourceMapRoute = createApmServerRoute({ endpoint: 'GET /api/apm/sourcemaps', options: { tags: ['access:apm'] }, - handler: async ({ plugins, logger }) => { + handler: async ({ plugins }) => { try { const fleetPluginStart = await plugins.fleet?.start(); if (fleetPluginStart) { @@ -51,21 +52,26 @@ const listSourceMapRoute = createApmServerRoute({ }); const uploadSourceMapRoute = createApmServerRoute({ - endpoint: 'POST /api/apm/sourcemaps/{serviceName}/{serviceVersion}', - options: { tags: ['access:apm', 'access:apm_write'] }, + endpoint: 'POST /api/apm/sourcemaps', + options: { + tags: ['access:apm', 'access:apm_write'], + body: { accepts: ['multipart/form-data'] }, + }, params: t.type({ - path: t.type({ - serviceName: t.string, - serviceVersion: t.string, - }), body: t.type({ - bundleFilepath: t.string, - sourceMap: sourceMapRt, + service_name: t.string, + service_version: t.string, + bundle_filepath: t.string, + sourcemap: jsonRt.pipe(sourceMapRt), }), }), handler: async ({ params, plugins, core }) => { - const { serviceName, serviceVersion } = params.path; - const { bundleFilepath, sourceMap } = params.body; + const { + service_name: serviceName, + service_version: serviceVersion, + bundle_filepath: bundleFilepath, + sourcemap: sourceMap, + } = params.body; const fleetPluginStart = await plugins.fleet?.start(); const coreStart = await core.start(); const esClient = coreStart.elasticsearch.client.asInternalUser; @@ -107,7 +113,7 @@ const deleteSourceMapRoute = createApmServerRoute({ id: t.string, }), }), - handler: async ({ context, params, plugins, core }) => { + handler: async ({ params, plugins, core }) => { const fleetPluginStart = await plugins.fleet?.start(); const { id } = params.path; const coreStart = await core.start(); diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index 13bd631085aac..474464dec1f99 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -39,6 +39,7 @@ export interface APMRouteCreateOptions { | 'access:ml:canGetJobs' | 'access:ml:canCreateJob' >; + body?: { accepts: Array<'application/json' | 'multipart/form-data'> }; }; } diff --git a/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts b/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts index c6afd6a592fff..55adc756f31af 100644 --- a/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts +++ b/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts @@ -6,7 +6,11 @@ */ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from '../../../../../../src/plugins/home/server'; +import { + INSTRUCTION_VARIANT, + TutorialSchema, + InstructionSetSchema, +} from '../../../../../../src/plugins/home/server'; import { createNodeAgentInstructions, @@ -22,7 +26,9 @@ import { } from '../instructions/apm_agent_instructions'; import { CloudSetup } from '../../../../cloud/server'; -export function createElasticCloudInstructions(cloudSetup?: CloudSetup) { +export function createElasticCloudInstructions( + cloudSetup?: CloudSetup +): TutorialSchema['elasticCloud'] { const apmServerUrl = cloudSetup?.apm.url; const instructionSets = []; @@ -37,7 +43,9 @@ export function createElasticCloudInstructions(cloudSetup?: CloudSetup) { }; } -function getApmServerInstructionSet(cloudSetup?: CloudSetup) { +function getApmServerInstructionSet( + cloudSetup?: CloudSetup +): InstructionSetSchema { const cloudId = cloudSetup?.cloudId; return { title: i18n.translate('xpack.apm.tutorial.apmServer.title', { @@ -61,7 +69,9 @@ function getApmServerInstructionSet(cloudSetup?: CloudSetup) { }; } -function getApmAgentInstructionSet(cloudSetup?: CloudSetup) { +function getApmAgentInstructionSet( + cloudSetup?: CloudSetup +): InstructionSetSchema { const apmServerUrl = cloudSetup?.apm.url; const secretToken = cloudSetup?.apm.secretToken; diff --git a/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts b/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts index a0e96f563381c..882d45c4c21db 100644 --- a/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts +++ b/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts @@ -6,28 +6,31 @@ */ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from '../../../../../../src/plugins/home/server'; import { - createWindowsServerInstructions, - createEditConfig, - createStartServerUnixSysv, - createStartServerUnix, - createDownloadServerRpm, - createDownloadServerDeb, - createDownloadServerOsx, -} from '../instructions/apm_server_instructions'; + INSTRUCTION_VARIANT, + InstructionsSchema, +} from '../../../../../../src/plugins/home/server'; import { - createNodeAgentInstructions, createDjangoAgentInstructions, + createDotNetAgentInstructions, createFlaskAgentInstructions, - createRailsAgentInstructions, - createRackAgentInstructions, - createJsAgentInstructions, createGoAgentInstructions, createJavaAgentInstructions, - createDotNetAgentInstructions, + createJsAgentInstructions, + createNodeAgentInstructions, createPhpAgentInstructions, + createRackAgentInstructions, + createRailsAgentInstructions, } from '../instructions/apm_agent_instructions'; +import { + createDownloadServerDeb, + createDownloadServerOsx, + createDownloadServerRpm, + createEditConfig, + createStartServerUnix, + createStartServerUnixSysv, + createWindowsServerInstructions, +} from '../instructions/apm_server_instructions'; export function onPremInstructions({ errorIndices, @@ -41,7 +44,7 @@ export function onPremInstructions({ metricsIndices: string; sourcemapIndices: string; onboardingIndices: string; -}) { +}): InstructionsSchema { const EDIT_CONFIG = createEditConfig(); const START_SERVER_UNIX = createStartServerUnix(); const START_SERVER_UNIX_SYSV = createStartServerUnixSysv(); @@ -66,6 +69,12 @@ export function onPremInstructions({ iconType: 'alert', }, instructionVariants: [ + { + id: INSTRUCTION_VARIANT.FLEET, + instructions: [ + { customComponentName: 'TutorialFleetInstructions' }, + ], + }, { id: INSTRUCTION_VARIANT.OSX, instructions: [ diff --git a/x-pack/plugins/apm/server/tutorial/index.ts b/x-pack/plugins/apm/server/tutorial/index.ts index d678677a4b751..9118c30b845d0 100644 --- a/x-pack/plugins/apm/server/tutorial/index.ts +++ b/x-pack/plugins/apm/server/tutorial/index.ts @@ -6,15 +6,16 @@ */ import { i18n } from '@kbn/i18n'; -import { onPremInstructions } from './envs/on_prem'; -import { createElasticCloudInstructions } from './envs/elastic_cloud'; -import apmIndexPattern from './index_pattern.json'; -import { CloudSetup } from '../../../cloud/server'; import { ArtifactsSchema, TutorialsCategory, + TutorialSchema, } from '../../../../../src/plugins/home/server'; +import { CloudSetup } from '../../../cloud/server'; import { APM_STATIC_INDEX_PATTERN_ID } from '../../common/index_pattern_constants'; +import { createElasticCloudInstructions } from './envs/elastic_cloud'; +import { onPremInstructions } from './envs/on_prem'; +import apmIndexPattern from './index_pattern.json'; const apmIntro = i18n.translate('xpack.apm.tutorial.introduction', { defaultMessage: @@ -102,6 +103,7 @@ It allows you to monitor the performance of thousands of applications in real ti ), euiIconType: 'apmApp', artifacts, + customStatusCheckName: 'apm_fleet_server_status_check', onPrem: onPremInstructions(indices), elasticCloud: createElasticCloudInstructions(cloud), previewImagePath: '/plugins/apm/assets/apm.png', @@ -113,5 +115,5 @@ It allows you to monitor the performance of thousands of applications in real ti 'An APM index pattern is required for some features in the APM UI.', } ), - }; + } as TutorialSchema; }; diff --git a/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts b/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts index a25021fac5d00..ba11a996f00df 100644 --- a/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts +++ b/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts @@ -913,7 +913,10 @@ export const createPhpAgentInstructions = ( 'APM is automatically started when your app boots. Configure the agent either via `php.ini` file:', } ), - commands: `elastic_apm.server_url=http://localhost:8200 + commands: `elastic_apm.server_url="${ + apmServerUrl || 'http://localhost:8200' + }" +elastic.apm.secret_token="${secretToken}" elastic_apm.service_name="My service" `.split('\n'), textPost: i18n.translate( diff --git a/x-pack/plugins/canvas/CONTRIBUTING.md b/x-pack/plugins/canvas/CONTRIBUTING.md index d3bff67771244..d8a657ea73c40 100644 --- a/x-pack/plugins/canvas/CONTRIBUTING.md +++ b/x-pack/plugins/canvas/CONTRIBUTING.md @@ -36,8 +36,8 @@ To keep the code terse, Canvas uses i18n "dictionaries": abstracted, static sing ```js -// i18n/components.ts -export const ComponentStrings = { +// asset_manager.tsx +const strings = { // ... AssetManager: { getCopyAssetMessage: (id: string) => @@ -52,10 +52,6 @@ export const ComponentStrings = { // ... }; -// asset_manager.tsx -import { ComponentStrings } from '../../../i18n'; -const { AssetManager: strings } = ComponentStrings; - const text = ( {strings.getSpaceUsedText(percentageUsed)} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/advanced_filter.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/advanced_filter.tsx index 4dfb4c3f09273..b5c009abc2768 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/advanced_filter.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/advanced_filter/component/advanced_filter.tsx @@ -5,12 +5,22 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import PropTypes from 'prop-types'; import React, { FunctionComponent } from 'react'; -import { ComponentStrings } from '../../../../../i18n'; +import PropTypes from 'prop-types'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; -const { AdvancedFilter: strings } = ComponentStrings; +const strings = { + getApplyButtonLabel: () => + i18n.translate('xpack.canvas.renderer.advancedFilter.applyButtonLabel', { + defaultMessage: 'Apply', + description: 'This refers to applying the filter to the Canvas workpad', + }), + getInputPlaceholder: () => + i18n.translate('xpack.canvas.renderer.advancedFilter.inputPlaceholder', { + defaultMessage: 'Enter filter expression', + }), +}; export interface Props { /** Optional value for the component */ diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/dropdown_filter.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/dropdown_filter.tsx index 86517c897f02d..43f2e1ecc84f3 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/dropdown_filter.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/dropdown_filter/component/dropdown_filter.tsx @@ -5,12 +5,18 @@ * 2.0. */ -import { EuiIcon } from '@elastic/eui'; -import PropTypes from 'prop-types'; import React, { ChangeEvent, FocusEvent, FunctionComponent } from 'react'; -import { ComponentStrings } from '../../../../../i18n'; +import PropTypes from 'prop-types'; +import { EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; -const { DropdownFilter: strings } = ComponentStrings; +const strings = { + getMatchAllOptionLabel: () => + i18n.translate('xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel', { + defaultMessage: 'ANY', + description: 'The dropdown filter option to match any value in the field.', + }), +}; export interface Props { /** diff --git a/x-pack/plugins/canvas/i18n/components.ts b/x-pack/plugins/canvas/i18n/components.ts deleted file mode 100644 index 6f011bb73e3b0..0000000000000 --- a/x-pack/plugins/canvas/i18n/components.ts +++ /dev/null @@ -1,1543 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { BOLD_MD_TOKEN, CANVAS, HTML, JSON, PDF, URL, ZIP } from './constants'; - -export const ComponentStrings = { - AddEmbeddableFlyout: { - getNoItemsText: () => - i18n.translate('xpack.canvas.embedObject.noMatchingObjectsMessage', { - defaultMessage: 'No matching objects found.', - }), - getTitleText: () => - i18n.translate('xpack.canvas.embedObject.titleText', { - defaultMessage: 'Add from Kibana', - }), - }, - AdvancedFilter: { - getApplyButtonLabel: () => - i18n.translate('xpack.canvas.renderer.advancedFilter.applyButtonLabel', { - defaultMessage: 'Apply', - description: 'This refers to applying the filter to the Canvas workpad', - }), - getInputPlaceholder: () => - i18n.translate('xpack.canvas.renderer.advancedFilter.inputPlaceholder', { - defaultMessage: 'Enter filter expression', - }), - }, - App: { - getLoadErrorMessage: (error: string) => - i18n.translate('xpack.canvas.app.loadErrorMessage', { - defaultMessage: 'Message: {error}', - values: { - error, - }, - }), - getLoadErrorTitle: () => - i18n.translate('xpack.canvas.app.loadErrorTitle', { - defaultMessage: 'Canvas failed to load', - }), - getLoadingMessage: () => - i18n.translate('xpack.canvas.app.loadingMessage', { - defaultMessage: 'Canvas is loading', - }), - }, - ArgAddPopover: { - getAddAriaLabel: () => - i18n.translate('xpack.canvas.argAddPopover.addAriaLabel', { - defaultMessage: 'Add argument', - }), - }, - ArgFormAdvancedFailure: { - getApplyButtonLabel: () => - i18n.translate('xpack.canvas.argFormAdvancedFailure.applyButtonLabel', { - defaultMessage: 'Apply', - }), - getResetButtonLabel: () => - i18n.translate('xpack.canvas.argFormAdvancedFailure.resetButtonLabel', { - defaultMessage: 'Reset', - }), - getRowErrorMessage: () => - i18n.translate('xpack.canvas.argFormAdvancedFailure.rowErrorMessage', { - defaultMessage: 'Invalid Expression', - }), - }, - ArgFormArgSimpleForm: { - getRemoveAriaLabel: () => - i18n.translate('xpack.canvas.argFormArgSimpleForm.removeAriaLabel', { - defaultMessage: 'Remove', - }), - getRequiredTooltip: () => - i18n.translate('xpack.canvas.argFormArgSimpleForm.requiredTooltip', { - defaultMessage: 'This argument is required, you should specify a value.', - }), - }, - ArgFormPendingArgValue: { - getLoadingMessage: () => - i18n.translate('xpack.canvas.argFormPendingArgValue.loadingMessage', { - defaultMessage: 'Loading', - }), - }, - ArgFormSimpleFailure: { - getFailureTooltip: () => - i18n.translate('xpack.canvas.argFormSimpleFailure.failureTooltip', { - defaultMessage: - 'The interface for this argument could not parse the value, so a fallback input is being used', - }), - }, - Asset: { - getCopyAssetTooltip: () => - i18n.translate('xpack.canvas.asset.copyAssetTooltip', { - defaultMessage: 'Copy id to clipboard', - }), - getCreateImageTooltip: () => - i18n.translate('xpack.canvas.asset.createImageTooltip', { - defaultMessage: 'Create image element', - }), - getDeleteAssetTooltip: () => - i18n.translate('xpack.canvas.asset.deleteAssetTooltip', { - defaultMessage: 'Delete', - }), - getDownloadAssetTooltip: () => - i18n.translate('xpack.canvas.asset.downloadAssetTooltip', { - defaultMessage: 'Download', - }), - getThumbnailAltText: () => - i18n.translate('xpack.canvas.asset.thumbnailAltText', { - defaultMessage: 'Asset thumbnail', - }), - getConfirmModalButtonLabel: () => - i18n.translate('xpack.canvas.asset.confirmModalButtonLabel', { - defaultMessage: 'Remove', - }), - getConfirmModalMessageText: () => - i18n.translate('xpack.canvas.asset.confirmModalDetail', { - defaultMessage: 'Are you sure you want to remove this asset?', - }), - getConfirmModalTitle: () => - i18n.translate('xpack.canvas.asset.confirmModalTitle', { - defaultMessage: 'Remove Asset', - }), - }, - AssetManager: { - getButtonLabel: () => - i18n.translate('xpack.canvas.assetManager.manageButtonLabel', { - defaultMessage: 'Manage assets', - }), - getDescription: () => - i18n.translate('xpack.canvas.assetModal.modalDescription', { - defaultMessage: - 'Below are the image assets in this workpad. Any assets that are currently in use cannot be determined at this time. To reclaim space, delete assets.', - }), - getEmptyAssetsDescription: () => - i18n.translate('xpack.canvas.assetModal.emptyAssetsDescription', { - defaultMessage: 'Import your assets to get started', - }), - getFilePickerPromptText: () => - i18n.translate('xpack.canvas.assetModal.filePickerPromptText', { - defaultMessage: 'Select or drag and drop images', - }), - getLoadingText: () => - i18n.translate('xpack.canvas.assetModal.loadingText', { - defaultMessage: 'Uploading images', - }), - getModalCloseButtonLabel: () => - i18n.translate('xpack.canvas.assetModal.modalCloseButtonLabel', { - defaultMessage: 'Close', - }), - getModalTitle: () => - i18n.translate('xpack.canvas.assetModal.modalTitle', { - defaultMessage: 'Manage workpad assets', - }), - getSpaceUsedText: (percentageUsed: number) => - i18n.translate('xpack.canvas.assetModal.spacedUsedText', { - defaultMessage: '{percentageUsed}% space used', - values: { - percentageUsed, - }, - }), - getCopyAssetMessage: (id: string) => - i18n.translate('xpack.canvas.assetModal.copyAssetMessage', { - defaultMessage: `Copied '{id}' to clipboard`, - values: { - id, - }, - }), - }, - AssetPicker: { - getAssetAltText: () => - i18n.translate('xpack.canvas.assetpicker.assetAltText', { - defaultMessage: 'Asset thumbnail', - }), - }, - CanvasLoading: { - getLoadingLabel: () => - i18n.translate('xpack.canvas.canvasLoading.loadingMessage', { - defaultMessage: 'Loading', - }), - }, - ColorManager: { - getAddAriaLabel: () => - i18n.translate('xpack.canvas.colorManager.addAriaLabel', { - defaultMessage: 'Add Color', - }), - getCodePlaceholder: () => - i18n.translate('xpack.canvas.colorManager.codePlaceholder', { - defaultMessage: 'Color code', - }), - getRemoveAriaLabel: () => - i18n.translate('xpack.canvas.colorManager.removeAriaLabel', { - defaultMessage: 'Remove Color', - }), - }, - CustomElementModal: { - getCancelButtonLabel: () => - i18n.translate('xpack.canvas.customElementModal.cancelButtonLabel', { - defaultMessage: 'Cancel', - }), - getCharactersRemainingDescription: (numberOfRemainingCharacter: number) => - i18n.translate('xpack.canvas.customElementModal.remainingCharactersDescription', { - defaultMessage: '{numberOfRemainingCharacter} characters remaining', - values: { - numberOfRemainingCharacter, - }, - }), - getDescriptionInputLabel: () => - i18n.translate('xpack.canvas.customElementModal.descriptionInputLabel', { - defaultMessage: 'Description', - }), - getElementPreviewTitle: () => - i18n.translate('xpack.canvas.customElementModal.elementPreviewTitle', { - defaultMessage: 'Element preview', - }), - getImageFilePickerPlaceholder: () => - i18n.translate('xpack.canvas.customElementModal.imageFilePickerPlaceholder', { - defaultMessage: 'Select or drag and drop an image', - }), - getImageInputDescription: () => - i18n.translate('xpack.canvas.customElementModal.imageInputDescription', { - defaultMessage: - 'Take a screenshot of your element and upload it here. This can also be done after saving.', - }), - getImageInputLabel: () => - i18n.translate('xpack.canvas.customElementModal.imageInputLabel', { - defaultMessage: 'Thumbnail image', - }), - getNameInputLabel: () => - i18n.translate('xpack.canvas.customElementModal.nameInputLabel', { - defaultMessage: 'Name', - }), - getSaveButtonLabel: () => - i18n.translate('xpack.canvas.customElementModal.saveButtonLabel', { - defaultMessage: 'Save', - }), - }, - DatasourceDatasourceComponent: { - getChangeButtonLabel: () => - i18n.translate('xpack.canvas.datasourceDatasourceComponent.changeButtonLabel', { - defaultMessage: 'Change element data source', - }), - getExpressionArgDescription: () => - i18n.translate('xpack.canvas.datasourceDatasourceComponent.expressionArgDescription', { - defaultMessage: - 'The datasource has an argument controlled by an expression. Use the expression editor to modify the datasource.', - }), - getPreviewButtonLabel: () => - i18n.translate('xpack.canvas.datasourceDatasourceComponent.previewButtonLabel', { - defaultMessage: 'Preview data', - }), - getSaveButtonLabel: () => - i18n.translate('xpack.canvas.datasourceDatasourceComponent.saveButtonLabel', { - defaultMessage: 'Save', - }), - }, - DatasourceDatasourcePreview: { - getEmptyFirstLineDescription: () => - i18n.translate('xpack.canvas.datasourceDatasourcePreview.emptyFirstLineDescription', { - defaultMessage: "We couldn't find any documents matching your search criteria.", - }), - getEmptySecondLineDescription: () => - i18n.translate('xpack.canvas.datasourceDatasourcePreview.emptySecondLineDescription', { - defaultMessage: 'Check your datasource settings and try again.', - }), - getEmptyTitle: () => - i18n.translate('xpack.canvas.datasourceDatasourcePreview.emptyTitle', { - defaultMessage: 'No documents found', - }), - getModalTitle: () => - i18n.translate('xpack.canvas.datasourceDatasourcePreview.modalTitle', { - defaultMessage: 'Datasource preview', - }), - }, - DatasourceNoDatasource: { - getPanelDescription: () => - i18n.translate('xpack.canvas.datasourceNoDatasource.panelDescription', { - defaultMessage: - "This element does not have an attached data source. This is usually because the element is an image or other static asset. If that's not the case you might want to check your expression to make sure it is not malformed.", - }), - getPanelTitle: () => - i18n.translate('xpack.canvas.datasourceNoDatasource.panelTitle', { - defaultMessage: 'No data source present', - }), - }, - DropdownFilter: { - getMatchAllOptionLabel: () => - i18n.translate('xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel', { - defaultMessage: 'ANY', - description: 'The dropdown filter option to match any value in the field.', - }), - }, - ElementConfig: { - getFailedLabel: () => - i18n.translate('xpack.canvas.elementConfig.failedLabel', { - defaultMessage: 'Failed', - description: - 'The label for the total number of elements in a workpad that have thrown an error or failed to load', - }), - getLoadedLabel: () => - i18n.translate('xpack.canvas.elementConfig.loadedLabel', { - defaultMessage: 'Loaded', - description: 'The label for the number of elements in a workpad that have loaded', - }), - getProgressLabel: () => - i18n.translate('xpack.canvas.elementConfig.progressLabel', { - defaultMessage: 'Progress', - description: 'The label for the percentage of elements that have finished loading', - }), - getTitle: () => - i18n.translate('xpack.canvas.elementConfig.title', { - defaultMessage: 'Element status', - description: - '"Elements" refers to the individual text, images, or visualizations that you can add to a Canvas workpad', - }), - getTotalLabel: () => - i18n.translate('xpack.canvas.elementConfig.totalLabel', { - defaultMessage: 'Total', - description: 'The label for the total number of elements in a workpad', - }), - }, - ElementControls: { - getDeleteAriaLabel: () => - i18n.translate('xpack.canvas.elementControls.deleteAriaLabel', { - defaultMessage: 'Delete element', - }), - getDeleteTooltip: () => - i18n.translate('xpack.canvas.elementControls.deleteToolTip', { - defaultMessage: 'Delete', - }), - getEditAriaLabel: () => - i18n.translate('xpack.canvas.elementControls.editAriaLabel', { - defaultMessage: 'Edit element', - }), - getEditTooltip: () => - i18n.translate('xpack.canvas.elementControls.editToolTip', { - defaultMessage: 'Edit', - }), - }, - ElementSettings: { - getDataTabLabel: () => - i18n.translate('xpack.canvas.elementSettings.dataTabLabel', { - defaultMessage: 'Data', - description: - 'This tab contains the settings for the data (i.e. Elasticsearch query) used as ' + - 'the source for a Canvas element', - }), - getDisplayTabLabel: () => - i18n.translate('xpack.canvas.elementSettings.displayTabLabel', { - defaultMessage: 'Display', - description: 'This tab contains the settings for how data is displayed in a Canvas element', - }), - }, - Error: { - getDescription: () => - i18n.translate('xpack.canvas.errorComponent.description', { - defaultMessage: 'Expression failed with the message:', - }), - getTitle: () => - i18n.translate('xpack.canvas.errorComponent.title', { - defaultMessage: 'Whoops! Expression failed', - }), - }, - Expression: { - getCancelButtonLabel: () => - i18n.translate('xpack.canvas.expression.cancelButtonLabel', { - defaultMessage: 'Cancel', - }), - getCloseButtonLabel: () => - i18n.translate('xpack.canvas.expression.closeButtonLabel', { - defaultMessage: 'Close', - }), - getLearnLinkText: () => - i18n.translate('xpack.canvas.expression.learnLinkText', { - defaultMessage: 'Learn expression syntax', - }), - getMaximizeButtonLabel: () => - i18n.translate('xpack.canvas.expression.maximizeButtonLabel', { - defaultMessage: 'Maximize editor', - }), - getMinimizeButtonLabel: () => - i18n.translate('xpack.canvas.expression.minimizeButtonLabel', { - defaultMessage: 'Minimize Editor', - }), - getRunButtonLabel: () => - i18n.translate('xpack.canvas.expression.runButtonLabel', { - defaultMessage: 'Run', - }), - getRunTooltip: () => - i18n.translate('xpack.canvas.expression.runTooltip', { - defaultMessage: 'Run the expression', - }), - }, - ExpressionElementNotSelected: { - getCloseButtonLabel: () => - i18n.translate('xpack.canvas.expressionElementNotSelected.closeButtonLabel', { - defaultMessage: 'Close', - }), - getSelectDescription: () => - i18n.translate('xpack.canvas.expressionElementNotSelected.selectDescription', { - defaultMessage: 'Select an element to show expression input', - }), - }, - ExpressionInput: { - getArgReferenceAliasesDetail: (aliases: string) => - i18n.translate('xpack.canvas.expressionInput.argReferenceAliasesDetail', { - defaultMessage: '{BOLD_MD_TOKEN}Aliases{BOLD_MD_TOKEN}: {aliases}', - values: { - BOLD_MD_TOKEN, - aliases, - }, - }), - getArgReferenceDefaultDetail: (defaultVal: string) => - i18n.translate('xpack.canvas.expressionInput.argReferenceDefaultDetail', { - defaultMessage: '{BOLD_MD_TOKEN}Default{BOLD_MD_TOKEN}: {defaultVal}', - values: { - BOLD_MD_TOKEN, - defaultVal, - }, - }), - getArgReferenceRequiredDetail: (required: string) => - i18n.translate('xpack.canvas.expressionInput.argReferenceRequiredDetail', { - defaultMessage: '{BOLD_MD_TOKEN}Required{BOLD_MD_TOKEN}: {required}', - values: { - BOLD_MD_TOKEN, - required, - }, - }), - getArgReferenceTypesDetail: (types: string) => - i18n.translate('xpack.canvas.expressionInput.argReferenceTypesDetail', { - defaultMessage: '{BOLD_MD_TOKEN}Types{BOLD_MD_TOKEN}: {types}', - values: { - BOLD_MD_TOKEN, - types, - }, - }), - getFunctionReferenceAcceptsDetail: (acceptTypes: string) => - i18n.translate('xpack.canvas.expressionInput.functionReferenceAccepts', { - defaultMessage: '{BOLD_MD_TOKEN}Accepts{BOLD_MD_TOKEN}: {acceptTypes}', - values: { - BOLD_MD_TOKEN, - acceptTypes, - }, - }), - getFunctionReferenceReturnsDetail: (returnType: string) => - i18n.translate('xpack.canvas.expressionInput.functionReferenceReturns', { - defaultMessage: '{BOLD_MD_TOKEN}Returns{BOLD_MD_TOKEN}: {returnType}', - values: { - BOLD_MD_TOKEN, - returnType, - }, - }), - }, - FunctionFormContextError: { - getContextErrorMessage: (errorMessage: string) => - i18n.translate('xpack.canvas.functionForm.contextError', { - defaultMessage: 'ERROR: {errorMessage}', - values: { - errorMessage, - }, - }), - }, - FunctionFormFunctionUnknown: { - getUnknownArgumentTypeErrorMessage: (expressionType: string) => - i18n.translate('xpack.canvas.functionForm.functionUnknown.unknownArgumentTypeError', { - defaultMessage: 'Unknown expression type "{expressionType}"', - values: { - expressionType, - }, - }), - }, - GroupSettings: { - getSaveGroupDescription: () => - i18n.translate('xpack.canvas.groupSettings.saveGroupDescription', { - defaultMessage: 'Save this group as a new element to re-use it throughout your workpad.', - }), - getUngroupDescription: () => - i18n.translate('xpack.canvas.groupSettings.ungroupDescription', { - defaultMessage: 'Ungroup ({uKey}) to edit individual element settings.', - values: { - uKey: 'U', - }, - }), - }, - HelpMenu: { - getDocumentationLinkLabel: () => - i18n.translate('xpack.canvas.helpMenu.documentationLinkLabel', { - defaultMessage: '{CANVAS} documentation', - values: { - CANVAS, - }, - }), - getHelpMenuDescription: () => - i18n.translate('xpack.canvas.helpMenu.description', { - defaultMessage: 'For {CANVAS} specific information', - values: { - CANVAS, - }, - }), - getKeyboardShortcutsLinkLabel: () => - i18n.translate('xpack.canvas.helpMenu.keyboardShortcutsLinkLabel', { - defaultMessage: 'Keyboard shortcuts', - }), - }, - KeyboardShortcutsDoc: { - getFlyoutCloseButtonAriaLabel: () => - i18n.translate('xpack.canvas.keyboardShortcutsDoc.flyout.closeButtonAriaLabel', { - defaultMessage: 'Closes keyboard shortcuts reference', - }), - getShortcutSeparator: () => - i18n.translate('xpack.canvas.keyboardShortcutsDoc.shortcutListSeparator', { - defaultMessage: 'or', - description: - 'Separates which keyboard shortcuts can be used for a single action. Example: "{shortcut1} or {shortcut2} or {shortcut3}"', - }), - getTitle: () => - i18n.translate('xpack.canvas.keyboardShortcutsDoc.flyoutHeaderTitle', { - defaultMessage: 'Keyboard shortcuts', - }), - }, - LabsControl: { - getLabsButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderLabsControlSettings.labsButtonLabel', { - defaultMessage: 'Labs', - }), - getAriaLabel: () => - i18n.translate('xpack.canvas.workpadHeaderLabsControlSettings.labsAriaLabel', { - defaultMessage: 'View labs projects', - }), - getTooltip: () => - i18n.translate('xpack.canvas.workpadHeaderLabsControlSettings.labsTooltip', { - defaultMessage: 'View labs projects', - }), - }, - Link: { - getErrorMessage: (message: string) => - i18n.translate('xpack.canvas.link.errorMessage', { - defaultMessage: 'LINK ERROR: {message}', - values: { - message, - }, - }), - }, - MultiElementSettings: { - getMultipleElementsActionsDescription: () => - i18n.translate('xpack.canvas.groupSettings.multipleElementsActionsDescription', { - defaultMessage: - 'Deselect these elements to edit their individual settings, press ({gKey}) to group them, or save this selection as a new ' + - 'element to re-use it throughout your workpad.', - values: { - gKey: 'G', - }, - }), - getMultipleElementsDescription: () => - i18n.translate('xpack.canvas.groupSettings.multipleElementsDescription', { - defaultMessage: 'Multiple elements are currently selected.', - }), - }, - PageConfig: { - getBackgroundColorDescription: () => - i18n.translate('xpack.canvas.pageConfig.backgroundColorDescription', { - defaultMessage: 'Accepts HEX, RGB or HTML color names', - }), - getBackgroundColorLabel: () => - i18n.translate('xpack.canvas.pageConfig.backgroundColorLabel', { - defaultMessage: 'Background', - }), - getNoTransitionDropDownOptionLabel: () => - i18n.translate('xpack.canvas.pageConfig.transitions.noneDropDownOptionLabel', { - defaultMessage: 'None', - description: - 'This is the option the user should choose if they do not want any page transition (i.e. fade in, fade out, etc) to ' + - 'be applied to the current page.', - }), - getTitle: () => - i18n.translate('xpack.canvas.pageConfig.title', { - defaultMessage: 'Page settings', - }), - getTransitionLabel: () => - i18n.translate('xpack.canvas.pageConfig.transitionLabel', { - defaultMessage: 'Transition', - description: - 'This refers to the transition effect, such as fade in or rotate, applied to a page in presentation mode.', - }), - getTransitionPreviewLabel: () => - i18n.translate('xpack.canvas.pageConfig.transitionPreviewLabel', { - defaultMessage: 'Preview', - description: 'This is the label for a preview of the transition effect selected.', - }), - }, - PageManager: { - getPageNumberAriaLabel: (pageNumber: number) => - i18n.translate('xpack.canvas.pageManager.pageNumberAriaLabel', { - defaultMessage: 'Load page number {pageNumber}', - values: { - pageNumber, - }, - }), - getAddPageTooltip: () => - i18n.translate('xpack.canvas.pageManager.addPageTooltip', { - defaultMessage: 'Add a new page to this workpad', - }), - getConfirmRemoveTitle: () => - i18n.translate('xpack.canvas.pageManager.confirmRemoveTitle', { - defaultMessage: 'Remove Page', - }), - getConfirmRemoveDescription: () => - i18n.translate('xpack.canvas.pageManager.confirmRemoveDescription', { - defaultMessage: 'Are you sure you want to remove this page?', - }), - getConfirmRemoveButtonLabel: () => - i18n.translate('xpack.canvas.pageManager.removeButtonLabel', { - defaultMessage: 'Remove', - }), - }, - PagePreviewPageControls: { - getClonePageAriaLabel: () => - i18n.translate('xpack.canvas.pagePreviewPageControls.clonePageAriaLabel', { - defaultMessage: 'Clone page', - }), - getClonePageTooltip: () => - i18n.translate('xpack.canvas.pagePreviewPageControls.clonePageTooltip', { - defaultMessage: 'Clone', - }), - getDeletePageAriaLabel: () => - i18n.translate('xpack.canvas.pagePreviewPageControls.deletePageAriaLabel', { - defaultMessage: 'Delete page', - }), - getDeletePageTooltip: () => - i18n.translate('xpack.canvas.pagePreviewPageControls.deletePageTooltip', { - defaultMessage: 'Delete', - }), - }, - PalettePicker: { - getEmptyPaletteLabel: () => - i18n.translate('xpack.canvas.palettePicker.emptyPaletteLabel', { - defaultMessage: 'None', - }), - getNoPaletteFoundErrorTitle: () => - i18n.translate('xpack.canvas.palettePicker.noPaletteFoundErrorTitle', { - defaultMessage: 'Color palette not found', - }), - }, - SavedElementsModal: { - getAddNewElementDescription: () => - i18n.translate('xpack.canvas.savedElementsModal.addNewElementDescription', { - defaultMessage: 'Group and save workpad elements to create new elements', - }), - getAddNewElementTitle: () => - i18n.translate('xpack.canvas.savedElementsModal.addNewElementTitle', { - defaultMessage: 'Add new elements', - }), - getCancelButtonLabel: () => - i18n.translate('xpack.canvas.savedElementsModal.cancelButtonLabel', { - defaultMessage: 'Cancel', - }), - getDeleteButtonLabel: () => - i18n.translate('xpack.canvas.savedElementsModal.deleteButtonLabel', { - defaultMessage: 'Delete', - }), - getDeleteElementDescription: () => - i18n.translate('xpack.canvas.savedElementsModal.deleteElementDescription', { - defaultMessage: 'Are you sure you want to delete this element?', - }), - getDeleteElementTitle: (elementName: string) => - i18n.translate('xpack.canvas.savedElementsModal.deleteElementTitle', { - defaultMessage: `Delete element '{elementName}'?`, - values: { - elementName, - }, - }), - getEditElementTitle: () => - i18n.translate('xpack.canvas.savedElementsModal.editElementTitle', { - defaultMessage: 'Edit element', - }), - getElementsTitle: () => - i18n.translate('xpack.canvas.savedElementsModal.elementsTitle', { - defaultMessage: 'Elements', - description: 'Title for the "Elements" tab when adding a new element', - }), - getFindElementPlaceholder: () => - i18n.translate('xpack.canvas.savedElementsModal.findElementPlaceholder', { - defaultMessage: 'Find element', - }), - getModalTitle: () => - i18n.translate('xpack.canvas.savedElementsModal.modalTitle', { - defaultMessage: 'My elements', - }), - getMyElementsTitle: () => - i18n.translate('xpack.canvas.savedElementsModal.myElementsTitle', { - defaultMessage: 'My elements', - description: 'Title for the "My elements" tab when adding a new element', - }), - getSavedElementsModalCloseButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeader.addElementModalCloseButtonLabel', { - defaultMessage: 'Close', - }), - }, - ShareWebsiteFlyout: { - getRuntimeStepTitle: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.downloadRuntimeTitle', { - defaultMessage: 'Download runtime', - }), - getSnippentsStepTitle: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.addSnippetsTitle', { - defaultMessage: 'Add snippets to website', - }), - getStepsDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.description', { - defaultMessage: - 'Follow these steps to share a static version of this workpad on an external website. It will be a visual snapshot of the current workpad, and will not have access to live data.', - }), - getTitle: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.flyoutTitle', { - defaultMessage: 'Share on a website', - }), - getUnsupportedRendererWarning: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.unsupportedRendererWarning', { - defaultMessage: - 'This workpad contains render functions that are not supported by the {CANVAS} Shareable Workpad Runtime. These elements will not be rendered:', - values: { - CANVAS, - }, - }), - getWorkpadStepTitle: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.downloadWorkpadTitle', { - defaultMessage: 'Download workpad', - }), - }, - ShareWebsiteRuntimeStep: { - getDownloadLabel: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.runtimeStep.downloadLabel', { - defaultMessage: 'Download runtime', - }), - getStepDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.runtimeStep.description', { - defaultMessage: - 'In order to render a Shareable Workpad, you also need to include the {CANVAS} Shareable Workpad Runtime. You can skip this step if the runtime is already included on your website.', - values: { - CANVAS, - }, - }), - }, - ShareWebsiteSnippetsStep: { - getAutoplayParameterDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.autoplayParameterDescription', { - defaultMessage: 'Should the runtime automatically move through the pages of the workpad?', - }), - getCallRuntimeLabel: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.callRuntimeLabel', { - defaultMessage: 'Call Runtime', - }), - getHeightParameterDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.heightParameterDescription', { - defaultMessage: 'The height of the Workpad. Defaults to the Workpad height.', - }), - getIncludeRuntimeLabel: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.includeRuntimeLabel', { - defaultMessage: 'Include Runtime', - }), - getIntervalParameterDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.intervalParameterDescription', { - defaultMessage: - 'The interval upon which the pages will advance in time format, (e.g. {twoSeconds}, {oneMinute})', - values: { - twoSeconds: '2s', - oneMinute: '1m', - }, - }), - getPageParameterDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.pageParameterDescription', { - defaultMessage: 'The page to display. Defaults to the page specified by the Workpad.', - }), - getParametersDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.parametersDescription', { - defaultMessage: - 'There are a number of inline parameters to configure the Shareable Workpad.', - }), - getParametersTitle: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.parametersLabel', { - defaultMessage: 'Parameters', - }), - getPlaceholderLabel: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.placeholderLabel', { - defaultMessage: 'Placeholder', - }), - getRequiredLabel: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.requiredLabel', { - defaultMessage: 'required', - }), - getShareableParameterDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.shareableParameterDescription', { - defaultMessage: 'The type of shareable. In this case, a {CANVAS} Workpad.', - values: { - CANVAS, - }, - }), - getSnippetsStepDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.description', { - defaultMessage: - 'The Workpad is placed within the {HTML} of the site by using an {HTML} placeholder. Parameters for the runtime are included inline. See the full list of parameters below. You can include more than one workpad on the page.', - values: { - HTML, - }, - }), - getToolbarParameterDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.toolbarParameterDescription', { - defaultMessage: 'Should the toolbar be hidden?', - }), - getUrlParameterDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.urlParameterDescription', { - defaultMessage: 'The {URL} of the Shareable Workpad {JSON} file.', - values: { - URL, - JSON, - }, - }), - getWidthParameterDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.snippetsStep.widthParameterDescription', { - defaultMessage: 'The width of the Workpad. Defaults to the Workpad width.', - }), - }, - ShareWebsiteWorkpadStep: { - getDownloadLabel: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.workpadStep.downloadLabel', { - defaultMessage: 'Download workpad', - }), - getStepDescription: () => - i18n.translate('xpack.canvas.shareWebsiteFlyout.workpadStep.description', { - defaultMessage: - 'The workpad will be exported as a single {JSON} file for sharing in another site.', - values: { - JSON, - }, - }), - }, - SidebarContent: { - getGroupedElementSidebarTitle: () => - i18n.translate('xpack.canvas.sidebarContent.groupedElementSidebarTitle', { - defaultMessage: 'Grouped element', - description: - 'The title displayed when a grouped element is selected. "elements" refer to the different visualizations, images, ' + - 'text, etc that can be added in a Canvas workpad. These elements can be grouped into a larger "grouped element" ' + - 'that contains multiple individual elements.', - }), - getMultiElementSidebarTitle: () => - i18n.translate('xpack.canvas.sidebarContent.multiElementSidebarTitle', { - defaultMessage: 'Multiple elements', - description: - 'The title displayed when multiple elements are selected. "elements" refer to the different visualizations, images, ' + - 'text, etc that can be added in a Canvas workpad.', - }), - getSingleElementSidebarTitle: () => - i18n.translate('xpack.canvas.sidebarContent.singleElementSidebarTitle', { - defaultMessage: 'Selected element', - description: - 'The title displayed when a single element are selected. "element" refer to the different visualizations, images, ' + - 'text, etc that can be added in a Canvas workpad.', - }), - }, - SidebarHeader: { - getBringForwardAriaLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.bringForwardArialLabel', { - defaultMessage: 'Move element up one layer', - }), - getBringToFrontAriaLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.bringToFrontArialLabel', { - defaultMessage: 'Move element to top layer', - }), - getSendBackwardAriaLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.sendBackwardArialLabel', { - defaultMessage: 'Move element down one layer', - }), - getSendToBackAriaLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.sendToBackArialLabel', { - defaultMessage: 'Move element to bottom layer', - }), - }, - TextStylePicker: { - getAlignCenterOption: () => - i18n.translate('xpack.canvas.textStylePicker.alignCenterOption', { - defaultMessage: 'Align center', - }), - getAlignLeftOption: () => - i18n.translate('xpack.canvas.textStylePicker.alignLeftOption', { - defaultMessage: 'Align left', - }), - getAlignRightOption: () => - i18n.translate('xpack.canvas.textStylePicker.alignRightOption', { - defaultMessage: 'Align right', - }), - getAlignmentOptionsControlLegend: () => - i18n.translate('xpack.canvas.textStylePicker.alignmentOptionsControl', { - defaultMessage: 'Alignment options', - }), - getFontColorLabel: () => - i18n.translate('xpack.canvas.textStylePicker.fontColorLabel', { - defaultMessage: 'Font Color', - }), - getStyleBoldOption: () => - i18n.translate('xpack.canvas.textStylePicker.styleBoldOption', { - defaultMessage: 'Bold', - }), - getStyleItalicOption: () => - i18n.translate('xpack.canvas.textStylePicker.styleItalicOption', { - defaultMessage: 'Italic', - }), - getStyleUnderlineOption: () => - i18n.translate('xpack.canvas.textStylePicker.styleUnderlineOption', { - defaultMessage: 'Underline', - }), - getStyleOptionsControlLegend: () => - i18n.translate('xpack.canvas.textStylePicker.styleOptionsControl', { - defaultMessage: 'Style options', - }), - }, - TimePicker: { - getApplyButtonLabel: () => - i18n.translate('xpack.canvas.timePicker.applyButtonLabel', { - defaultMessage: 'Apply', - }), - }, - Toolbar: { - getEditorButtonLabel: () => - i18n.translate('xpack.canvas.toolbar.editorButtonLabel', { - defaultMessage: 'Expression editor', - }), - getNextPageAriaLabel: () => - i18n.translate('xpack.canvas.toolbar.nextPageAriaLabel', { - defaultMessage: 'Next Page', - }), - getPageButtonLabel: (pageNum: number, totalPages: number) => - i18n.translate('xpack.canvas.toolbar.pageButtonLabel', { - defaultMessage: 'Page {pageNum}{rest}', - values: { - pageNum, - rest: totalPages > 1 ? ` of ${totalPages}` : '', - }, - }), - getPreviousPageAriaLabel: () => - i18n.translate('xpack.canvas.toolbar.previousPageAriaLabel', { - defaultMessage: 'Previous Page', - }), - getWorkpadManagerCloseButtonLabel: () => - i18n.translate('xpack.canvas.toolbar.workpadManagerCloseButtonLabel', { - defaultMessage: 'Close', - }), - getErrorMessage: (message: string) => - i18n.translate('xpack.canvas.toolbar.errorMessage', { - defaultMessage: 'TOOLBAR ERROR: {message}', - values: { - message, - }, - }), - }, - ToolbarTray: { - getCloseTrayAriaLabel: () => - i18n.translate('xpack.canvas.toolbarTray.closeTrayAriaLabel', { - defaultMessage: 'Close tray', - }), - }, - VarConfig: { - getAddButtonLabel: () => - i18n.translate('xpack.canvas.varConfig.addButtonLabel', { - defaultMessage: 'Add a variable', - }), - getAddTooltipLabel: () => - i18n.translate('xpack.canvas.varConfig.addTooltipLabel', { - defaultMessage: 'Add a variable', - }), - getCopyActionButtonLabel: () => - i18n.translate('xpack.canvas.varConfig.copyActionButtonLabel', { - defaultMessage: 'Copy snippet', - }), - getCopyActionTooltipLabel: () => - i18n.translate('xpack.canvas.varConfig.copyActionTooltipLabel', { - defaultMessage: 'Copy variable syntax to clipboard', - }), - getCopyNotificationDescription: () => - i18n.translate('xpack.canvas.varConfig.copyNotificationDescription', { - defaultMessage: 'Variable syntax copied to clipboard', - }), - getDeleteActionButtonLabel: () => - i18n.translate('xpack.canvas.varConfig.deleteActionButtonLabel', { - defaultMessage: 'Delete variable', - }), - getDeleteNotificationDescription: () => - i18n.translate('xpack.canvas.varConfig.deleteNotificationDescription', { - defaultMessage: 'Variable successfully deleted', - }), - getEditActionButtonLabel: () => - i18n.translate('xpack.canvas.varConfig.editActionButtonLabel', { - defaultMessage: 'Edit variable', - }), - getEmptyDescription: () => - i18n.translate('xpack.canvas.varConfig.emptyDescription', { - defaultMessage: - 'This workpad has no variables currently. You may add variables to store and edit common values. These variables can then be used in elements or within the expression editor.', - }), - getTableNameLabel: () => - i18n.translate('xpack.canvas.varConfig.tableNameLabel', { - defaultMessage: 'Name', - }), - getTableTypeLabel: () => - i18n.translate('xpack.canvas.varConfig.tableTypeLabel', { - defaultMessage: 'Type', - }), - getTableValueLabel: () => - i18n.translate('xpack.canvas.varConfig.tableValueLabel', { - defaultMessage: 'Value', - }), - getTitle: () => - i18n.translate('xpack.canvas.varConfig.titleLabel', { - defaultMessage: 'Variables', - }), - getTitleTooltip: () => - i18n.translate('xpack.canvas.varConfig.titleTooltip', { - defaultMessage: 'Add variables to store and edit common values', - }), - }, - VarConfigDeleteVar: { - getCancelButtonLabel: () => - i18n.translate('xpack.canvas.varConfigDeleteVar.cancelButtonLabel', { - defaultMessage: 'Cancel', - }), - getDeleteButtonLabel: () => - i18n.translate('xpack.canvas.varConfigDeleteVar.deleteButtonLabel', { - defaultMessage: 'Delete variable', - }), - getTitle: () => - i18n.translate('xpack.canvas.varConfigDeleteVar.titleLabel', { - defaultMessage: 'Delete variable?', - }), - getWarningDescription: () => - i18n.translate('xpack.canvas.varConfigDeleteVar.warningDescription', { - defaultMessage: - 'Deleting this variable may adversely affect the workpad. Are you sure you wish to continue?', - }), - }, - VarConfigEditVar: { - getAddTitle: () => - i18n.translate('xpack.canvas.varConfigEditVar.addTitleLabel', { - defaultMessage: 'Add variable', - }), - getCancelButtonLabel: () => - i18n.translate('xpack.canvas.varConfigEditVar.cancelButtonLabel', { - defaultMessage: 'Cancel', - }), - getDuplicateNameError: () => - i18n.translate('xpack.canvas.varConfigEditVar.duplicateNameError', { - defaultMessage: 'Variable name already in use', - }), - getEditTitle: () => - i18n.translate('xpack.canvas.varConfigEditVar.editTitleLabel', { - defaultMessage: 'Edit variable', - }), - getEditWarning: () => - i18n.translate('xpack.canvas.varConfigEditVar.editWarning', { - defaultMessage: 'Editing a variable in use may adversely affect your workpad', - }), - getNameFieldLabel: () => - i18n.translate('xpack.canvas.varConfigEditVar.nameFieldLabel', { - defaultMessage: 'Name', - }), - getSaveButtonLabel: () => - i18n.translate('xpack.canvas.varConfigEditVar.saveButtonLabel', { - defaultMessage: 'Save changes', - }), - getTypeBooleanLabel: () => - i18n.translate('xpack.canvas.varConfigEditVar.typeBooleanLabel', { - defaultMessage: 'Boolean', - }), - getTypeFieldLabel: () => - i18n.translate('xpack.canvas.varConfigEditVar.typeFieldLabel', { - defaultMessage: 'Type', - }), - getTypeNumberLabel: () => - i18n.translate('xpack.canvas.varConfigEditVar.typeNumberLabel', { - defaultMessage: 'Number', - }), - getTypeStringLabel: () => - i18n.translate('xpack.canvas.varConfigEditVar.typeStringLabel', { - defaultMessage: 'String', - }), - getValueFieldLabel: () => - i18n.translate('xpack.canvas.varConfigEditVar.valueFieldLabel', { - defaultMessage: 'Value', - }), - }, - VarConfigVarValueField: { - getBooleanOptionsLegend: () => - i18n.translate('xpack.canvas.varConfigVarValueField.booleanOptionsLegend', { - defaultMessage: 'Boolean value', - }), - getFalseOption: () => - i18n.translate('xpack.canvas.varConfigVarValueField.falseOption', { - defaultMessage: 'False', - }), - getTrueOption: () => - i18n.translate('xpack.canvas.varConfigVarValueField.trueOption', { - defaultMessage: 'True', - }), - }, - WorkpadConfig: { - getApplyStylesheetButtonLabel: () => - i18n.translate('xpack.canvas.workpadConfig.applyStylesheetButtonLabel', { - defaultMessage: `Apply stylesheet`, - description: - '"stylesheet" refers to the collection of CSS style rules entered by the user.', - }), - getBackgroundColorLabel: () => - i18n.translate('xpack.canvas.workpadConfig.backgroundColorLabel', { - defaultMessage: 'Background color', - }), - getFlipDimensionAriaLabel: () => - i18n.translate('xpack.canvas.workpadConfig.swapDimensionsAriaLabel', { - defaultMessage: `Swap the page's width and height`, - }), - getFlipDimensionTooltip: () => - i18n.translate('xpack.canvas.workpadConfig.swapDimensionsTooltip', { - defaultMessage: 'Swap the width and height', - }), - getGlobalCSSLabel: () => - i18n.translate('xpack.canvas.workpadConfig.globalCSSLabel', { - defaultMessage: `Global CSS overrides`, - }), - getGlobalCSSTooltip: () => - i18n.translate('xpack.canvas.workpadConfig.globalCSSTooltip', { - defaultMessage: `Apply styles to all pages in this workpad`, - }), - getNameLabel: () => - i18n.translate('xpack.canvas.workpadConfig.nameLabel', { - defaultMessage: 'Name', - }), - getPageHeightLabel: () => - i18n.translate('xpack.canvas.workpadConfig.heightLabel', { - defaultMessage: 'Height', - }), - getPageSizeBadgeAriaLabel: (sizeName: string) => - i18n.translate('xpack.canvas.workpadConfig.pageSizeBadgeAriaLabel', { - defaultMessage: `Preset page size: {sizeName}`, - values: { - sizeName, - }, - }), - getPageSizeBadgeOnClickAriaLabel: (sizeName: string) => - i18n.translate('xpack.canvas.workpadConfig.pageSizeBadgeOnClickAriaLabel', { - defaultMessage: `Set page size to {sizeName}`, - values: { - sizeName, - }, - }), - getPageWidthLabel: () => - i18n.translate('xpack.canvas.workpadConfig.widthLabel', { - defaultMessage: 'Width', - }), - getTitle: () => - i18n.translate('xpack.canvas.workpadConfig.title', { - defaultMessage: 'Workpad settings', - }), - getUSLetterButtonLabel: () => - i18n.translate('xpack.canvas.workpadConfig.USLetterButtonLabel', { - defaultMessage: 'US Letter', - description: 'This is referring to the dimensions of U.S. standard letter paper.', - }), - }, - WorkpadHeader: { - getAddElementButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeader.addElementButtonLabel', { - defaultMessage: 'Add element', - }), - getFullScreenButtonAriaLabel: () => - i18n.translate('xpack.canvas.workpadHeader.fullscreenButtonAriaLabel', { - defaultMessage: 'View fullscreen', - }), - getFullScreenTooltip: () => - i18n.translate('xpack.canvas.workpadHeader.fullscreenTooltip', { - defaultMessage: 'Enter fullscreen mode', - }), - getHideEditControlTooltip: () => - i18n.translate('xpack.canvas.workpadHeader.hideEditControlTooltip', { - defaultMessage: 'Hide editing controls', - }), - getNoWritePermissionTooltipText: () => - i18n.translate('xpack.canvas.workpadHeader.noWritePermissionTooltip', { - defaultMessage: "You don't have permission to edit this workpad", - }), - getShowEditControlTooltip: () => - i18n.translate('xpack.canvas.workpadHeader.showEditControlTooltip', { - defaultMessage: 'Show editing controls', - }), - }, - WorkpadHeaderAutoRefreshControls: { - getDisableTooltip: () => - i18n.translate('xpack.canvas.workpadHeaderAutoRefreshControls.disableTooltip', { - defaultMessage: 'Disable auto-refresh', - }), - getIntervalFormLabelText: () => - i18n.translate('xpack.canvas.workpadHeaderAutoRefreshControls.intervalFormLabel', { - defaultMessage: 'Change auto-refresh interval', - }), - getRefreshListDurationManualText: () => - i18n.translate( - 'xpack.canvas.workpadHeaderAutoRefreshControls.refreshListDurationManualText', - { - defaultMessage: 'Manually', - } - ), - getRefreshListTitle: () => - i18n.translate('xpack.canvas.workpadHeaderAutoRefreshControls.refreshListTitle', { - defaultMessage: 'Refresh elements', - }), - }, - WorkpadHeaderCustomInterval: { - getButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderCustomInterval.confirmButtonLabel', { - defaultMessage: 'Set', - }), - getFormDescription: () => - i18n.translate('xpack.canvas.workpadHeaderCustomInterval.formDescription', { - defaultMessage: - 'Use shorthand notation, like {secondsExample}, {minutesExample}, or {hoursExample}', - values: { - secondsExample: '30s', - minutesExample: '10m', - hoursExample: '1h', - }, - }), - getFormLabel: () => - i18n.translate('xpack.canvas.workpadHeaderCustomInterval.formLabel', { - defaultMessage: 'Set a custom interval', - }), - }, - WorkpadHeaderEditMenu: { - getAlignmentMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.alignmentMenuItemLabel', { - defaultMessage: 'Alignment', - description: - 'This refers to the vertical (i.e. left, center, right) and horizontal (i.e. top, middle, bottom) ' + - 'alignment options of the selected elements', - }), - getBottomAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.bottomAlignMenuItemLabel', { - defaultMessage: 'Bottom', - }), - getCenterAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.centerAlignMenuItemLabel', { - defaultMessage: 'Center', - description: 'This refers to alignment centered horizontally.', - }), - getCreateElementModalTitle: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.createElementModalTitle', { - defaultMessage: 'Create new element', - }), - getDistributionMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.distributionMenutItemLabel', { - defaultMessage: 'Distribution', - description: - 'This refers to the options to evenly spacing the selected elements horizontall or vertically.', - }), - getEditMenuButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.editMenuButtonLabel', { - defaultMessage: 'Edit', - }), - getEditMenuLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.editMenuLabel', { - defaultMessage: 'Edit options', - }), - getGroupMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.groupMenuItemLabel', { - defaultMessage: 'Group', - description: 'This refers to grouping multiple selected elements.', - }), - getHorizontalDistributionMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.horizontalDistributionMenutItemLabel', { - defaultMessage: 'Horizontal', - }), - getLeftAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.leftAlignMenuItemLabel', { - defaultMessage: 'Left', - }), - getMiddleAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.middleAlignMenuItemLabel', { - defaultMessage: 'Middle', - description: 'This refers to alignment centered vertically.', - }), - getOrderMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.orderMenuItemLabel', { - defaultMessage: 'Order', - description: 'Refers to the order of the elements displayed on the page from front to back', - }), - getRedoMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.redoMenuItemLabel', { - defaultMessage: 'Redo', - }), - getRightAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.rightAlignMenuItemLabel', { - defaultMessage: 'Right', - }), - getSaveElementMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.savedElementMenuItemLabel', { - defaultMessage: 'Save as new element', - }), - getTopAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.topAlignMenuItemLabel', { - defaultMessage: 'Top', - }), - getUndoMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.undoMenuItemLabel', { - defaultMessage: 'Undo', - }), - getUngroupMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.ungroupMenuItemLabel', { - defaultMessage: 'Ungroup', - description: 'This refers to ungrouping a grouped element', - }), - getVerticalDistributionMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderEditMenu.verticalDistributionMenutItemLabel', { - defaultMessage: 'Vertical', - }), - }, - WorkpadHeaderElementMenu: { - getAssetsMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.manageAssetsMenuItemLabel', { - defaultMessage: 'Manage assets', - }), - getChartMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.chartMenuItemLabel', { - defaultMessage: 'Chart', - }), - getElementMenuButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.elementMenuButtonLabel', { - defaultMessage: 'Add element', - }), - getElementMenuLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.elementMenuLabel', { - defaultMessage: 'Add an element', - }), - getEmbedObjectMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.embedObjectMenuItemLabel', { - defaultMessage: 'Add from Kibana', - }), - getFilterMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.filterMenuItemLabel', { - defaultMessage: 'Filter', - }), - getImageMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.imageMenuItemLabel', { - defaultMessage: 'Image', - }), - getMyElementsMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.myElementsMenuItemLabel', { - defaultMessage: 'My elements', - }), - getOtherMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.otherMenuItemLabel', { - defaultMessage: 'Other', - }), - getProgressMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.progressMenuItemLabel', { - defaultMessage: 'Progress', - }), - getShapeMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.shapeMenuItemLabel', { - defaultMessage: 'Shape', - }), - getTextMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderElementMenu.textMenuItemLabel', { - defaultMessage: 'Text', - }), - }, - WorkpadHeaderKioskControls: { - getCycleFormLabel: () => - i18n.translate('xpack.canvas.workpadHeaderKioskControl.cycleFormLabel', { - defaultMessage: 'Change cycling interval', - }), - getCycleToggleSwitch: () => - i18n.translate('xpack.canvas.workpadHeaderKioskControl.cycleToggleSwitch', { - defaultMessage: 'Cycle slides automatically', - }), - getTitle: () => - i18n.translate('xpack.canvas.workpadHeaderKioskControl.controlTitle', { - defaultMessage: 'Cycle fullscreen pages', - }), - getAutoplayListDurationManualText: () => - i18n.translate('xpack.canvas.workpadHeaderKioskControl.autoplayListDurationManual', { - defaultMessage: 'Manually', - }), - getDisableTooltip: () => - i18n.translate('xpack.canvas.workpadHeaderKioskControl.disableTooltip', { - defaultMessage: 'Disable auto-play', - }), - }, - WorkpadHeaderRefreshControlSettings: { - getRefreshAriaLabel: () => - i18n.translate('xpack.canvas.workpadHeaderRefreshControlSettings.refreshAriaLabel', { - defaultMessage: 'Refresh Elements', - }), - getRefreshTooltip: () => - i18n.translate('xpack.canvas.workpadHeaderRefreshControlSettings.refreshTooltip', { - defaultMessage: 'Refresh data', - }), - }, - WorkpadHeaderShareMenu: { - getCopyPDFMessage: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.copyPDFMessage', { - defaultMessage: 'The {PDF} generation {URL} was copied to your clipboard.', - values: { - PDF, - URL, - }, - }), - getCopyShareConfigMessage: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.copyShareConfigMessage', { - defaultMessage: 'Copied share markup to clipboard', - }), - getShareableZipErrorTitle: (workpadName: string) => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareWebsiteErrorTitle', { - defaultMessage: - "Failed to create {ZIP} file for '{workpadName}'. The workpad may be too large. You'll need to download the files separately.", - values: { - ZIP, - workpadName, - }, - }), - getShareDownloadJSONTitle: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareDownloadJSONTitle', { - defaultMessage: 'Download as {JSON}', - values: { - JSON, - }, - }), - getShareDownloadPDFTitle: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareDownloadPDFTitle', { - defaultMessage: '{PDF} reports', - values: { - PDF, - }, - }), - getShareMenuButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareMenuButtonLabel', { - defaultMessage: 'Share', - }), - getShareWebsiteTitle: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareWebsiteTitle', { - defaultMessage: 'Share on a website', - }), - getShareWorkpadMessage: () => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.shareWorkpadMessage', { - defaultMessage: 'Share this workpad', - }), - getUnknownExportErrorMessage: (type: string) => - i18n.translate('xpack.canvas.workpadHeaderShareMenu.unknownExportErrorMessage', { - defaultMessage: 'Unknown export type: {type}', - values: { - type, - }, - }), - }, - WorkpadHeaderViewMenu: { - getAutoplayOffMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.autoplayOffMenuItemLabel', { - defaultMessage: 'Turn autoplay off', - }), - getAutoplayOnMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.autoplayOnMenuItemLabel', { - defaultMessage: 'Turn autoplay on', - }), - getAutoplaySettingsMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.autoplaySettingsMenuItemLabel', { - defaultMessage: 'Autoplay settings', - }), - getFullscreenMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.fullscreenMenuLabel', { - defaultMessage: 'Enter fullscreen mode', - }), - getHideEditModeLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.hideEditModeLabel', { - defaultMessage: 'Hide editing controls', - }), - getRefreshMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.refreshMenuItemLabel', { - defaultMessage: 'Refresh data', - }), - getRefreshSettingsMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.refreshSettingsMenuItemLabel', { - defaultMessage: 'Auto refresh settings', - }), - getShowEditModeLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.showEditModeLabel', { - defaultMessage: 'Show editing controls', - }), - getViewMenuButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.viewMenuButtonLabel', { - defaultMessage: 'View', - }), - getViewMenuLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.viewMenuLabel', { - defaultMessage: 'View options', - }), - getZoomControlsAriaLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomControlsAriaLabel', { - defaultMessage: 'Zoom controls', - }), - getZoomControlsTooltip: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomControlsTooltip', { - defaultMessage: 'Zoom controls', - }), - getZoomFitToWindowText: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomFitToWindowText', { - defaultMessage: 'Fit to window', - }), - getZoomInText: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomInText', { - defaultMessage: 'Zoom in', - }), - getZoomMenuItemLabel: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomMenuItemLabel', { - defaultMessage: 'Zoom', - }), - getZoomOutText: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomOutText', { - defaultMessage: 'Zoom out', - }), - getZoomPanelTitle: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomPanelTitle', { - defaultMessage: 'Zoom', - }), - getZoomPercentage: (scale: number) => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomResetText', { - defaultMessage: '{scalePercentage}%', - values: { - scalePercentage: scale * 100, - }, - }), - getZoomResetText: () => - i18n.translate('xpack.canvas.workpadHeaderViewMenu.zoomPrecentageValue', { - defaultMessage: 'Reset', - }), - }, -}; diff --git a/x-pack/plugins/canvas/i18n/index.ts b/x-pack/plugins/canvas/i18n/index.ts index 14c9e5d221b79..d35b915ea7fb6 100644 --- a/x-pack/plugins/canvas/i18n/index.ts +++ b/x-pack/plugins/canvas/i18n/index.ts @@ -6,7 +6,6 @@ */ export * from './capabilities'; -export * from './components'; export * from './constants'; export * from './errors'; export * from './expression_types'; diff --git a/x-pack/plugins/canvas/public/components/arg_add_popover/arg_add_popover.tsx b/x-pack/plugins/canvas/public/components/arg_add_popover/arg_add_popover.tsx index 194d2d8b3ddf5..d9df1e4661fbf 100644 --- a/x-pack/plugins/canvas/public/components/arg_add_popover/arg_add_popover.tsx +++ b/x-pack/plugins/canvas/public/components/arg_add_popover/arg_add_popover.tsx @@ -8,15 +8,20 @@ import React, { MouseEventHandler, FC } from 'react'; import PropTypes from 'prop-types'; import { EuiButtonIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + // @ts-expect-error untyped local import { Popover, PopoverChildrenProps } from '../popover'; import { ArgAdd } from '../arg_add'; // @ts-expect-error untyped local import { Arg } from '../../expression_types/arg'; -import { ComponentStrings } from '../../../i18n'; - -const { ArgAddPopover: strings } = ComponentStrings; +const strings = { + getAddAriaLabel: () => + i18n.translate('xpack.canvas.argAddPopover.addAriaLabel', { + defaultMessage: 'Add argument', + }), +}; interface ArgOptions { arg: Arg; diff --git a/x-pack/plugins/canvas/public/components/arg_form/advanced_failure.js b/x-pack/plugins/canvas/public/components/arg_form/advanced_failure.js index c40e74186e87e..14f47553002ac 100644 --- a/x-pack/plugins/canvas/public/components/arg_form/advanced_failure.js +++ b/x-pack/plugins/canvas/public/components/arg_form/advanced_failure.js @@ -9,12 +9,25 @@ import React from 'react'; import PropTypes from 'prop-types'; import { compose, withProps, withPropsOnChange } from 'recompose'; import { EuiTextArea, EuiButton, EuiButtonEmpty, EuiFormRow, EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { fromExpression, toExpression } from '@kbn/interpreter/common'; -import { createStatefulPropHoc } from '../../components/enhance/stateful_prop'; -import { ComponentStrings } from '../../../i18n'; +import { createStatefulPropHoc } from '../../components/enhance/stateful_prop'; -const { ArgFormAdvancedFailure: strings } = ComponentStrings; +const strings = { + getApplyButtonLabel: () => + i18n.translate('xpack.canvas.argFormAdvancedFailure.applyButtonLabel', { + defaultMessage: 'Apply', + }), + getResetButtonLabel: () => + i18n.translate('xpack.canvas.argFormAdvancedFailure.resetButtonLabel', { + defaultMessage: 'Reset', + }), + getRowErrorMessage: () => + i18n.translate('xpack.canvas.argFormAdvancedFailure.rowErrorMessage', { + defaultMessage: 'Invalid Expression', + }), +}; export const AdvancedFailureComponent = (props) => { const { diff --git a/x-pack/plugins/canvas/public/components/arg_form/arg_simple_form.tsx b/x-pack/plugins/canvas/public/components/arg_form/arg_simple_form.tsx index 2ae772cdc197a..84b87373c1c5a 100644 --- a/x-pack/plugins/canvas/public/components/arg_form/arg_simple_form.tsx +++ b/x-pack/plugins/canvas/public/components/arg_form/arg_simple_form.tsx @@ -8,12 +8,20 @@ import React, { ReactNode, MouseEventHandler } from 'react'; import PropTypes from 'prop-types'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; -import { TooltipIcon, IconType } from '../tooltip_icon'; - -import { ComponentStrings } from '../../../i18n'; +import { i18n } from '@kbn/i18n'; -const { ArgFormArgSimpleForm: strings } = ComponentStrings; +import { TooltipIcon, IconType } from '../tooltip_icon'; +const strings = { + getRemoveAriaLabel: () => + i18n.translate('xpack.canvas.argFormArgSimpleForm.removeAriaLabel', { + defaultMessage: 'Remove', + }), + getRequiredTooltip: () => + i18n.translate('xpack.canvas.argFormArgSimpleForm.requiredTooltip', { + defaultMessage: 'This argument is required, you should specify a value.', + }), +}; interface Props { children?: ReactNode; required?: boolean; diff --git a/x-pack/plugins/canvas/public/components/arg_form/pending_arg_value.js b/x-pack/plugins/canvas/public/components/arg_form/pending_arg_value.js index ff390a770f80e..f933230f39928 100644 --- a/x-pack/plugins/canvas/public/components/arg_form/pending_arg_value.js +++ b/x-pack/plugins/canvas/public/components/arg_form/pending_arg_value.js @@ -7,11 +7,17 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { ComponentStrings } from '../../../i18n'; +import { i18n } from '@kbn/i18n'; + import { Loading } from '../loading'; import { ArgLabel } from './arg_label'; -const { ArgFormPendingArgValue: strings } = ComponentStrings; +const strings = { + getLoadingMessage: () => + i18n.translate('xpack.canvas.argFormPendingArgValue.loadingMessage', { + defaultMessage: 'Loading', + }), +}; export class PendingArgValue extends React.PureComponent { static propTypes = { diff --git a/x-pack/plugins/canvas/public/components/arg_form/simple_failure.tsx b/x-pack/plugins/canvas/public/components/arg_form/simple_failure.tsx index cc4e92679a870..57173fa413e8f 100644 --- a/x-pack/plugins/canvas/public/components/arg_form/simple_failure.tsx +++ b/x-pack/plugins/canvas/public/components/arg_form/simple_failure.tsx @@ -6,11 +6,17 @@ */ import React from 'react'; -import { TooltipIcon, IconType } from '../tooltip_icon'; +import { i18n } from '@kbn/i18n'; -import { ComponentStrings } from '../../../i18n'; +import { TooltipIcon, IconType } from '../tooltip_icon'; -const { ArgFormSimpleFailure: strings } = ComponentStrings; +const strings = { + getFailureTooltip: () => + i18n.translate('xpack.canvas.argFormSimpleFailure.failureTooltip', { + defaultMessage: + 'The interface for this argument could not parse the value, so a fallback input is being used', + }), +}; // This is what is being generated by render() from the Arg class. It is called in FunctionForm export const SimpleFailure = () => ( diff --git a/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset_manager.stories.storyshot b/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset_manager.stories.storyshot index 34b6b333f3ef5..d567d3cf85f13 100644 --- a/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset_manager.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset_manager.stories.storyshot @@ -116,20 +116,13 @@ exports[`Storyshots components/Assets/AssetManager no assets 1`] = ` size="xxl" />

    - -

    - Import your assets to get started -

    -
    - + Import your assets to get started +

    diff --git a/x-pack/plugins/canvas/public/components/asset_manager/asset.component.tsx b/x-pack/plugins/canvas/public/components/asset_manager/asset.component.tsx index 8f9d90ccbe1d8..024137f640636 100644 --- a/x-pack/plugins/canvas/public/components/asset_manager/asset.component.tsx +++ b/x-pack/plugins/canvas/public/components/asset_manager/asset.component.tsx @@ -17,6 +17,7 @@ import { EuiTextColor, EuiToolTip, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { useNotifyService } from '../../services'; @@ -25,9 +26,40 @@ import { Clipboard } from '../clipboard'; import { Download } from '../download'; import { AssetType } from '../../../types'; -import { ComponentStrings } from '../../../i18n'; - -const { Asset: strings } = ComponentStrings; +const strings = { + getCopyAssetTooltip: () => + i18n.translate('xpack.canvas.asset.copyAssetTooltip', { + defaultMessage: 'Copy id to clipboard', + }), + getCreateImageTooltip: () => + i18n.translate('xpack.canvas.asset.createImageTooltip', { + defaultMessage: 'Create image element', + }), + getDeleteAssetTooltip: () => + i18n.translate('xpack.canvas.asset.deleteAssetTooltip', { + defaultMessage: 'Delete', + }), + getDownloadAssetTooltip: () => + i18n.translate('xpack.canvas.asset.downloadAssetTooltip', { + defaultMessage: 'Download', + }), + getThumbnailAltText: () => + i18n.translate('xpack.canvas.asset.thumbnailAltText', { + defaultMessage: 'Asset thumbnail', + }), + getConfirmModalButtonLabel: () => + i18n.translate('xpack.canvas.asset.confirmModalButtonLabel', { + defaultMessage: 'Remove', + }), + getConfirmModalMessageText: () => + i18n.translate('xpack.canvas.asset.confirmModalDetail', { + defaultMessage: 'Are you sure you want to remove this asset?', + }), + getConfirmModalTitle: () => + i18n.translate('xpack.canvas.asset.confirmModalTitle', { + defaultMessage: 'Remove Asset', + }), +}; export interface Props { /** The asset to be rendered */ diff --git a/x-pack/plugins/canvas/public/components/asset_manager/asset_manager.component.tsx b/x-pack/plugins/canvas/public/components/asset_manager/asset_manager.component.tsx index 7795aa9671b83..7b004d5ab5099 100644 --- a/x-pack/plugins/canvas/public/components/asset_manager/asset_manager.component.tsx +++ b/x-pack/plugins/canvas/public/components/asset_manager/asset_manager.component.tsx @@ -24,14 +24,47 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { ASSET_MAX_SIZE } from '../../../common/lib/constants'; import { Loading } from '../loading'; import { Asset } from './asset'; import { AssetType } from '../../../types'; -import { ComponentStrings } from '../../../i18n'; -const { AssetManager: strings } = ComponentStrings; +const strings = { + getDescription: () => + i18n.translate('xpack.canvas.assetModal.modalDescription', { + defaultMessage: + 'Below are the image assets in this workpad. Any assets that are currently in use cannot be determined at this time. To reclaim space, delete assets.', + }), + getEmptyAssetsDescription: () => + i18n.translate('xpack.canvas.assetModal.emptyAssetsDescription', { + defaultMessage: 'Import your assets to get started', + }), + getFilePickerPromptText: () => + i18n.translate('xpack.canvas.assetModal.filePickerPromptText', { + defaultMessage: 'Select or drag and drop images', + }), + getLoadingText: () => + i18n.translate('xpack.canvas.assetModal.loadingText', { + defaultMessage: 'Uploading images', + }), + getModalCloseButtonLabel: () => + i18n.translate('xpack.canvas.assetModal.modalCloseButtonLabel', { + defaultMessage: 'Close', + }), + getModalTitle: () => + i18n.translate('xpack.canvas.assetModal.modalTitle', { + defaultMessage: 'Manage workpad assets', + }), + getSpaceUsedText: (percentageUsed: number) => + i18n.translate('xpack.canvas.assetModal.spacedUsedText', { + defaultMessage: '{percentageUsed}% space used', + values: { + percentageUsed, + }, + }), +}; export interface Props { /** The assets to display within the modal */ diff --git a/x-pack/plugins/canvas/public/components/asset_picker/asset_picker.tsx b/x-pack/plugins/canvas/public/components/asset_picker/asset_picker.tsx index c2e2d8a053247..4bf13577aff53 100644 --- a/x-pack/plugins/canvas/public/components/asset_picker/asset_picker.tsx +++ b/x-pack/plugins/canvas/public/components/asset_picker/asset_picker.tsx @@ -8,12 +8,16 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { EuiFlexGrid, EuiFlexItem, EuiLink, EuiImage, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { CanvasAsset } from '../../../types'; -import { ComponentStrings } from '../../../i18n'; - -const { AssetPicker: strings } = ComponentStrings; +const strings = { + getAssetAltText: () => + i18n.translate('xpack.canvas.assetpicker.assetAltText', { + defaultMessage: 'Asset thumbnail', + }), +}; interface Props { assets: CanvasAsset[]; diff --git a/x-pack/plugins/canvas/public/components/canvas_loading/canvas_loading.component.tsx b/x-pack/plugins/canvas/public/components/canvas_loading/canvas_loading.component.tsx index 38e62f46c945a..8f55c31933291 100644 --- a/x-pack/plugins/canvas/public/components/canvas_loading/canvas_loading.component.tsx +++ b/x-pack/plugins/canvas/public/components/canvas_loading/canvas_loading.component.tsx @@ -7,9 +7,14 @@ import React, { FC } from 'react'; import { EuiPanel, EuiLoadingChart, EuiSpacer, EuiText } from '@elastic/eui'; -import { ComponentStrings } from '../../../i18n/components'; +import { i18n } from '@kbn/i18n'; -const { CanvasLoading: strings } = ComponentStrings; +const strings = { + getLoadingLabel: () => + i18n.translate('xpack.canvas.canvasLoading.loadingMessage', { + defaultMessage: 'Loading', + }), +}; export const CanvasLoading: FC<{ msg?: string }> = ({ msg = `${strings.getLoadingLabel()}...`, diff --git a/x-pack/plugins/canvas/public/components/color_manager/color_manager.tsx b/x-pack/plugins/canvas/public/components/color_manager/color_manager.tsx index ae5cfac85bdc9..50c679c2a1e51 100644 --- a/x-pack/plugins/canvas/public/components/color_manager/color_manager.tsx +++ b/x-pack/plugins/canvas/public/components/color_manager/color_manager.tsx @@ -9,11 +9,24 @@ import React, { FC } from 'react'; import PropTypes from 'prop-types'; import { EuiButtonIcon, EuiFieldText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import tinycolor from 'tinycolor2'; -import { ColorDot } from '../color_dot/color_dot'; +import { i18n } from '@kbn/i18n'; -import { ComponentStrings } from '../../../i18n/components'; +import { ColorDot } from '../color_dot/color_dot'; -const { ColorManager: strings } = ComponentStrings; +const strings = { + getAddAriaLabel: () => + i18n.translate('xpack.canvas.colorManager.addAriaLabel', { + defaultMessage: 'Add Color', + }), + getCodePlaceholder: () => + i18n.translate('xpack.canvas.colorManager.codePlaceholder', { + defaultMessage: 'Color code', + }), + getRemoveAriaLabel: () => + i18n.translate('xpack.canvas.colorManager.removeAriaLabel', { + defaultMessage: 'Remove Color', + }), +}; export interface Props { /** diff --git a/x-pack/plugins/canvas/public/components/custom_element_modal/__stories__/__snapshots__/custom_element_modal.stories.storyshot b/x-pack/plugins/canvas/public/components/custom_element_modal/__stories__/__snapshots__/custom_element_modal.stories.storyshot index 18f86aca24302..dc66eef809050 100644 --- a/x-pack/plugins/canvas/public/components/custom_element_modal/__stories__/__snapshots__/custom_element_modal.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/custom_element_modal/__stories__/__snapshots__/custom_element_modal.stories.storyshot @@ -80,7 +80,7 @@ exports[`Storyshots components/Elements/CustomElementModal with description 1`] className="euiFormControlLayout__childrenWrapper" >
    40 characters remaining
    @@ -119,7 +119,7 @@ exports[`Storyshots components/Elements/CustomElementModal with description 1`] className="euiFormRow__fieldWrapper" >